BARON-SSO 로그인 연동
This commit is contained in:
+12
-1
@@ -4,4 +4,15 @@ G5_MYSQL_DB=egbim
|
||||
G5_MYSQL_USER=egbim
|
||||
G5_MYSQL_PASSWORD=baron3840!!
|
||||
MARIADB_ROOT_PASSWORD=root_local_pass
|
||||
COMPOSE_PROJECT_NAME=egbim
|
||||
COMPOSE_PROJECT_NAME=egbim
|
||||
|
||||
# BARON SSO local test
|
||||
BARON_SSO_CLIENT_ID=
|
||||
BARON_SSO_CLIENT_SECRET=
|
||||
BARON_SSO_AUTHORIZE_ENDPOINT=
|
||||
BARON_SSO_TOKEN_ENDPOINT=
|
||||
BARON_SSO_USERINFO_ENDPOINT=
|
||||
BARON_SSO_ISSUER=
|
||||
BARON_SSO_REDIRECT_URI=http://localhost:8088/auth/callback
|
||||
BARON_SSO_SCOPE=openid profile email tenants
|
||||
BARON_SSO_CLIENT_AUTH_METHOD=basic
|
||||
@@ -45,6 +45,15 @@ jobs:
|
||||
G5_MYSQL_PASSWORD=${{ secrets.G5_MYSQL_PASSWORD }}
|
||||
MARIADB_ROOT_PASSWORD=${{ secrets.MARIADB_ROOT_PASSWORD }}
|
||||
COMPOSE_PROJECT_NAME=${{ secrets.COMPOSE_PROJECT_NAME }}
|
||||
BARON_SSO_CLIENT_ID=${{ secrets.BARON_SSO_CLIENT_ID }}
|
||||
BARON_SSO_CLIENT_SECRET=${{ secrets.BARON_SSO_CLIENT_SECRET }}
|
||||
BARON_SSO_AUTHORIZE_ENDPOINT=${{ secrets.BARON_SSO_AUTHORIZE_ENDPOINT }}
|
||||
BARON_SSO_TOKEN_ENDPOINT=${{ secrets.BARON_SSO_TOKEN_ENDPOINT }}
|
||||
BARON_SSO_USERINFO_ENDPOINT=${{ secrets.BARON_SSO_USERINFO_ENDPOINT }}
|
||||
BARON_SSO_ISSUER=${{ secrets.BARON_SSO_ISSUER }}
|
||||
BARON_SSO_REDIRECT_URI=${{ secrets.BARON_SSO_REDIRECT_URI }}
|
||||
BARON_SSO_SCOPE=${{ secrets.BARON_SSO_SCOPE }}
|
||||
BARON_SSO_CLIENT_AUTH_METHOD=${{ secrets.BARON_SSO_CLIENT_AUTH_METHOD }}
|
||||
EOF
|
||||
|
||||
- name: Upload deployment script
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
|
||||
# auth route compatibility
|
||||
RewriteRule ^auth/baron_login/?$ /auth/baron_login.php [L,QSA]
|
||||
RewriteRule ^auth/callback/?$ /auth/callback.php [L,QSA]
|
||||
RewriteRule ^auth/debug_claims/?$ /auth/debug_claims.php [L,QSA]
|
||||
|
||||
# local docker/localhost bypass
|
||||
RewriteCond %{ENV:APP_ENV} =local [OR]
|
||||
RewriteCond %{HTTP_HOST} ^(localhost|127\.0\.0\.1)(:[0-9]+)?$ [NC]
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
# BARON-SSO 연동 및 Q&A 접근 전환 작업 목록
|
||||
|
||||
## 목표
|
||||
|
||||
- 외부 사용자는 기존 Descope 로그인을 계속 사용한다.
|
||||
- 내부 사용자는 BARON-SSO 로그인으로 전환한다.
|
||||
- 내부 사용자도 기존과 동일하게 Q&A 페이지를 조회, 작성, 수정, 댓글 작성할 수 있어야 한다.
|
||||
- 로그인 공급자가 달라도 Q&A 는 동일한 세션 구조를 사용하도록 정리한다.
|
||||
|
||||
## 현재 확인된 전제
|
||||
|
||||
- 현재 Q&A 기능은 Descope 전용 세션 파일에 직접 의존한다.
|
||||
- 국문 `egbim/` 와 영문 `eng/` 에 동일한 구조가 병렬로 존재한다.
|
||||
- 내부/외부 사용자 판별은 현재 Q&A 작성 로직에서 이메일 도메인 기준으로 처리한다.
|
||||
- 관리자 권한은 세션 파일 내부의 이메일 화이트리스트 기반이다.
|
||||
- BARON-SSO 전용 콜백, 토큰 교환, 사용자 정보 매핑 로직은 현재 코드베이스에 별도로 구현되어 있지 않다.
|
||||
- BARON-SSO 애플리케이션 보안 수준은 `Server-side app` 으로 확정되었다.
|
||||
|
||||
## 이번 회차 확정 사항
|
||||
|
||||
- [x] BARON-SSO 애플리케이션 유형은 `Server-side app` 으로 설정
|
||||
- [x] `client_secret` 을 사용하는 Authorization Code 기반 서버 처리 방식 사용
|
||||
- [x] 토큰 교환과 세션 생성은 브라우저가 아니라 서버에서 처리
|
||||
- [ ] BARON-SSO claim 샘플 확보
|
||||
- [ ] callback 엔드포인트 경로 확정
|
||||
- [ ] 공통 auth bootstrap 파일 구조 확정
|
||||
|
||||
## 현재 영향 파일
|
||||
|
||||
### 공통 세션/인증 진입점
|
||||
|
||||
- `egbim/skin/member/basic/descope_session.php`
|
||||
- `eng/skin/member/basic/descope_session.php`
|
||||
- `egbim/skin/member/basic/descope_login.php`
|
||||
- `eng/skin/member/basic/descope_login.php`
|
||||
- `egbim/skin/member/basic/descope_logout.php`
|
||||
- `eng/skin/member/basic/descope_logout.php`
|
||||
|
||||
### Q&A 주요 진입점
|
||||
|
||||
- `egbim/bbs/descope_qa_list.php`
|
||||
- `egbim/bbs/descope_qa_detail.php`
|
||||
- `egbim/bbs/descope_qa_write.php`
|
||||
- `egbim/bbs/descope_qa_comment.php`
|
||||
- `egbim/bbs/descope_qa_comment_update.php`
|
||||
- `egbim/bbs/descope_qa_comment_delete.php`
|
||||
- `egbim/bbs/descope_qa_delete.php`
|
||||
- `egbim/bbs/descope_qa_status.php`
|
||||
- `egbim/bbs/admin_guard.php`
|
||||
- `eng/bbs/descope_qa_list.php`
|
||||
- `eng/bbs/descope_qa_detail.php`
|
||||
- `eng/bbs/descope_qa_write.php`
|
||||
- `eng/bbs/descope_qa_comment.php`
|
||||
- `eng/bbs/descope_qa_comment_update.php`
|
||||
- `eng/bbs/descope_qa_comment_delete.php`
|
||||
- `eng/bbs/descope_qa_delete.php`
|
||||
- `eng/bbs/descope_qa_status.php`
|
||||
- `eng/bbs/admin_guard.php`
|
||||
|
||||
## 1. 정책 확정
|
||||
|
||||
- [ ] 내부 사용자와 외부 사용자의 로그인 정책을 문서로 확정한다.
|
||||
- [ ] 내부 사용자 판별 기준을 확정한다.
|
||||
- [ ] 내부 사용자 판별을 이메일 도메인으로 유지할지, 별도 진입 버튼으로 분기할지 결정한다.
|
||||
- [ ] 로그인 화면에서 외부용 Descope 와 내부용 BARON-SSO 를 어떻게 노출할지 결정한다.
|
||||
- [ ] 국문 `egbim/` 와 영문 `eng/` 모두 동일 정책을 적용할지 확인한다.
|
||||
|
||||
### 정책 결정 시 확인 항목
|
||||
|
||||
- [ ] 내부 사용자 도메인 목록 확정
|
||||
- [ ] 예외 계정 처리 방식 확정
|
||||
- [ ] 관리자 계정 판단 기준 유지 여부 확정
|
||||
- [ ] 로그인 실패 시 안내 문구와 리다이렉트 방식 확정
|
||||
|
||||
## 2. BARON-SSO 연동 정보 정리
|
||||
|
||||
- [ ] BARON-SSO OIDC 설정값을 확정한다.
|
||||
- [ ] `client_id`, `client_secret`, `discovery_endpoint`, `issuer`, `authorization_endpoint`, `token_endpoint`, `userinfo_endpoint` 를 운영값 기준으로 문서화한다.
|
||||
- [ ] Redirect URI 를 실제 서비스 경로와 맞춘다.
|
||||
- [ ] 운영 도메인이 `eg-bim.com` 인지 `eg-bim.co.kr` 인지 최종 확정한다.
|
||||
- [ ] 로컬/개발/운영 환경별 Redirect URI 분리 여부를 결정한다.
|
||||
|
||||
### 현재 확정된 방식
|
||||
|
||||
- [x] 보안 수준: `Server-side app`
|
||||
- [x] 인증 플로우: 서버에서 Authorization Code 처리
|
||||
- [x] `client_secret` 필요
|
||||
- [ ] PKCE 지원 여부 확인
|
||||
- [ ] `scope` 목록 확정
|
||||
|
||||
### 확인 필요 항목
|
||||
|
||||
- [ ] 현재 등록된 Redirect URI: `https://eg-bim.com/auth/callback`
|
||||
- [ ] 실제 애플리케이션에서 사용할 콜백 경로가 존재하는지 확인
|
||||
- [ ] 사내망 또는 VPN 환경에서만 접근 가능한지 확인
|
||||
- [ ] BARON-SSO 로그아웃 엔드포인트 제공 여부 확인
|
||||
- [ ] 로컬 테스트용 Redirect URI 추가: `http://localhost:8088/auth/callback`
|
||||
|
||||
## 3. 공통 인증 계층 설계
|
||||
|
||||
- [ ] Descope 전용 세션 파일을 공급자 중립적인 공통 인증 bootstrap 구조로 분리한다.
|
||||
- [ ] 로그인 공급자가 달라도 최종 세션 구조는 동일하게 맞춘다.
|
||||
- [ ] Q&A 코드는 특정 공급자명을 몰라도 되도록 정리한다.
|
||||
- [ ] 국문/영문 트리에서 인증 공통화 파일 위치를 통일한다.
|
||||
|
||||
### 권장 방향
|
||||
|
||||
- [ ] `descope_session.php` 를 바로 확장하지 말고 공통 auth loader 로 분리
|
||||
- [ ] Descope 와 BARON-SSO 모두 동일한 `$_SESSION['user']` 구조를 채우도록 통합
|
||||
- [ ] Q&A 는 공통 auth loader 만 include 하도록 변경
|
||||
- [ ] 공급자별 쿠키 복원, 세션 복원, 권한 매핑은 내부 모듈로 캡슐화
|
||||
|
||||
### 세션 표준 구조 초안
|
||||
|
||||
- [ ] `$_SESSION['user']['userId']`
|
||||
- [ ] `$_SESSION['user']['loginIds'][0]`
|
||||
- [ ] `$_SESSION['user']['name']`
|
||||
- [ ] `$_SESSION['user']['email']`
|
||||
- [ ] `$_SESSION['user']['phone']`
|
||||
- [ ] `$_SESSION['user']['customAttributes']`
|
||||
- [ ] `$_SESSION['user']['roleNames']`
|
||||
- [ ] 그누보드 연동용 `$_SESSION['ss_mb_id']`, `$_SESSION['ss_mb_level']`
|
||||
|
||||
## 4. BARON-SSO 인증 구현
|
||||
|
||||
- [ ] 로그인 시작 엔드포인트 구현
|
||||
- [ ] state, nonce 저장 및 검증 구현
|
||||
- [ ] callback 엔드포인트 구현
|
||||
- [ ] authorization code 를 token 으로 교환하는 로직 구현
|
||||
- [ ] id token 또는 userinfo 기반 사용자 정보 파싱 구현
|
||||
- [ ] 실패 시 예외 처리 및 사용자 안내 구현
|
||||
|
||||
### 구현 상세 체크리스트
|
||||
|
||||
- [x] 브라우저 직접 토큰 저장 방식은 사용하지 않음
|
||||
- [x] callback 에서 서버가 token endpoint 호출
|
||||
- [x] callback 성공 후 서버 세션에 사용자 정보 저장
|
||||
- [ ] OIDC discovery 자동 사용 여부 결정
|
||||
- [ ] discovery 미사용 시 endpoint 고정값 사용
|
||||
- [ ] 세션 고정 공격 방지용 session regenerate 적용 여부 확인
|
||||
- [ ] callback 에서 CSRF 방지용 state 검증
|
||||
- [ ] 토큰 검증 책임 범위 결정
|
||||
- [ ] 토큰 만료와 재로그인 정책 정의
|
||||
|
||||
### Server-side app 기준 추가 작업
|
||||
|
||||
- [ ] callback 에서 `code`, `state`, `error` 파라미터 처리 규칙 정의
|
||||
- [ ] token endpoint 요청 파라미터 목록 정리
|
||||
- [ ] `client_secret` 저장 위치 확정
|
||||
- [ ] 세션 생성 직후 `session_regenerate_id(true)` 적용
|
||||
- [ ] 로그인 성공 후 이동할 기본 URL 확정
|
||||
- [ ] 로그인 실패 시 복귀 URL 과 안내 문구 확정
|
||||
|
||||
## 5. 사용자 정보 매핑 규칙 정리
|
||||
|
||||
- [ ] BARON-SSO claim 샘플을 확보한다.
|
||||
- [ ] BARON-SSO 의 사용자 식별자 중 무엇을 `loginIds[0]` 으로 쓸지 확정한다.
|
||||
- [ ] 이름, 이메일, 전화번호, 회사, 계열사, 부서 claim 매핑 규칙을 정한다.
|
||||
- [ ] BARON claim 에 없는 값은 기본값 또는 빈값 처리 기준을 정한다.
|
||||
|
||||
### 현재 Q&A 가 기대하는 값
|
||||
|
||||
- [ ] 내부 사용자 저장 시 `family_company`, `department` 사용
|
||||
- [ ] 외부 사용자 저장 시 `company`, `department` 사용
|
||||
- [ ] 작성자 식별은 `login_id` 기준
|
||||
- [ ] 내 글 여부 판단도 `login_id` 기준
|
||||
|
||||
### 확인 필요 리스크
|
||||
|
||||
- [ ] BARON-SSO 가 이메일을 직접 주지 않는 경우 대응 필요
|
||||
- [ ] 부서명 claim key 가 `team` 과 다를 가능성 확인 필요
|
||||
- [ ] 계열사명 claim 이 `familyCompany` 와 다를 가능성 확인 필요
|
||||
- [ ] 영문 사이트에서 표시명 영문화가 필요한지 검토
|
||||
|
||||
## 6. Q&A 접근 제어 변경
|
||||
|
||||
- [ ] Q&A 목록에서 로그인 사용자 식별이 공통 세션으로 동작하는지 확인
|
||||
- [ ] Q&A 상세에서 비밀글 접근 제어가 내부 사용자에게도 동일하게 동작하는지 확인
|
||||
- [ ] Q&A 작성에서 로그인 강제가 BARON-SSO 사용자에게도 정상 동작하는지 확인
|
||||
- [ ] 댓글 작성, 수정, 삭제도 동일 세션 구조 기준으로 동작하는지 확인
|
||||
- [ ] 관리자 상태 변경 기능이 정상 동작하는지 확인
|
||||
|
||||
### 변경 대상 포인트
|
||||
|
||||
- [ ] `require_once .../descope_session.php` 참조를 공통 auth bootstrap 으로 치환
|
||||
- [ ] 로그인 여부 조건식이 특정 공급자 쿠키에 의존하지 않는지 점검
|
||||
- [ ] 작성자 식별 키가 공급자별로 달라지지 않도록 통일
|
||||
- [ ] 비로그인 사용자 공개글 조회 정책 유지 여부 확인
|
||||
|
||||
## 7. 관리자 권한 체계 정리
|
||||
|
||||
- [ ] 현재 이메일 화이트리스트 방식을 유지할지 결정한다.
|
||||
- [ ] BARON-SSO role 또는 group claim 기반으로 바꿀지 결정한다.
|
||||
- [ ] 관리자 표시 이름 가공 로직과 충돌이 없는지 확인한다.
|
||||
|
||||
### 권장 방향
|
||||
|
||||
- [ ] 1차 전환은 이메일 화이트리스트 유지
|
||||
- [ ] 2차에서 BARON role 기반 권한 이관 검토
|
||||
- [ ] 권한 기준은 국문/영문 공통으로 단일화
|
||||
|
||||
## 8. 로그인 화면 및 UX 변경
|
||||
|
||||
- [ ] 메인 헤더 또는 로그인 팝업에 내부 사용자용 BARON-SSO 진입 버튼 추가
|
||||
- [ ] 외부 사용자용 기존 Descope 로그인 버튼 유지
|
||||
- [ ] 로그인 실패 안내 문구를 공급자별로 구분할지 결정
|
||||
- [ ] 내부 사용자가 Descope 로그인으로 잘못 진입했을 때 안내 문구 제공 여부 결정
|
||||
|
||||
### 권장 UX
|
||||
|
||||
- [ ] 버튼 2개 분리: `외부 사용자 로그인`, `사내 사용자 로그인`
|
||||
- [ ] 최초 단계에서는 자동 도메인 판별보다 명시적 분기 우선
|
||||
- [ ] 내부 사용자 안내 문구에 사내 계정 사용 정책 명시
|
||||
|
||||
## 9. 로그아웃 및 세션 종료 처리
|
||||
|
||||
- [ ] Descope 로그아웃은 기존 방식 유지
|
||||
- [ ] BARON-SSO 로그아웃 처리 추가
|
||||
- [ ] 로컬 세션 종료와 IdP 세션 종료를 분리할지 결정
|
||||
- [ ] 로그아웃 후 재진입 시 자동 재로그인 여부 확인
|
||||
|
||||
### 점검 항목
|
||||
|
||||
- [ ] PHP 세션 제거
|
||||
- [ ] Descope 관련 쿠키 제거
|
||||
- [ ] BARON 관련 쿠키 또는 state 쿠키 제거
|
||||
- [ ] 브라우저 백버튼 진입 시 인증 상태 오동작 여부 확인
|
||||
|
||||
## 10. 설정값 및 비밀정보 관리
|
||||
|
||||
- [ ] BARON-SSO 설정값을 코드 하드코딩 없이 분리한다.
|
||||
- [ ] 운영/개발/로컬 설정 파일 구조를 정리한다.
|
||||
- [ ] 민감정보가 저장소에 올라가지 않도록 관리 방식을 정한다.
|
||||
|
||||
### 체크리스트
|
||||
|
||||
- [ ] `client_secret` 는 환경변수 또는 비공개 설정 파일로 관리
|
||||
- [ ] callback URL 도 환경별 분리
|
||||
- [ ] issuer, endpoint 값도 환경별 override 가능하도록 구성
|
||||
- [ ] 배포 문서에 설정 절차 추가
|
||||
|
||||
## 11. 테스트 시나리오
|
||||
|
||||
- [ ] 외부 사용자 Descope 로그인 후 Q&A 목록 조회
|
||||
- [ ] 외부 사용자 Descope 로그인 후 Q&A 작성
|
||||
- [ ] 내부 사용자 BARON-SSO 로그인 후 Q&A 목록 조회
|
||||
- [ ] 내부 사용자 BARON-SSO 로그인 후 Q&A 작성
|
||||
- [ ] 내부 사용자 BARON-SSO 로그인 후 댓글 작성
|
||||
- [ ] 내부 관리자 BARON-SSO 로그인 후 상태 변경
|
||||
- [ ] 비로그인 사용자 공개글 조회
|
||||
- [ ] 비밀글 작성자 본인 접근
|
||||
- [ ] 비밀글 타 사용자 접근 차단
|
||||
- [ ] 로그아웃 후 접근 차단
|
||||
- [ ] 국문 `egbim/` 검증
|
||||
- [ ] 영문 `eng/` 검증
|
||||
|
||||
### 추가 확인 항목
|
||||
|
||||
- [ ] 모바일 브라우저 로그인 진입 확인
|
||||
- [ ] 로그인 팝업 차단 이슈 여부 확인
|
||||
- [ ] 세션 만료 후 재시도 흐름 확인
|
||||
- [ ] 첨부파일 다운로드 권한 흐름 확인
|
||||
|
||||
## 12. 배포 및 롤백 계획
|
||||
|
||||
- [ ] BARON-SSO 기능 배포 순서를 정한다.
|
||||
- [ ] 운영 반영 전 내부 사용자 테스트 계정으로 사전 검증한다.
|
||||
- [ ] 장애 시 내부 사용자를 임시 우회시킬 방법을 준비한다.
|
||||
|
||||
### 권장 배포 순서
|
||||
|
||||
- [ ] 1차: 공통 auth bootstrap 도입
|
||||
- [ ] 2차: BARON-SSO callback 및 세션 매핑 구현
|
||||
- [ ] 3차: 로그인 UI 에 사내 사용자 버튼 노출
|
||||
- [ ] 4차: 내부 사용자 실사용 테스트
|
||||
- [ ] 5차: 운영 전환
|
||||
|
||||
### 롤백 포인트
|
||||
|
||||
- [ ] BARON-SSO 버튼 숨김만으로 즉시 차단 가능한지 확인
|
||||
- [ ] 내부 사용자 임시 Descope 허용 스위치 필요 여부 검토
|
||||
- [ ] 공통 auth bootstrap 에 공급자 fallback 허용 여부 검토
|
||||
|
||||
## 바로 실행할 우선순위
|
||||
|
||||
### P0
|
||||
|
||||
- [ ] 내부/외부 로그인 정책 확정
|
||||
- [ ] BARON-SSO claim 샘플 확보
|
||||
- [ ] Redirect URI 와 실제 콜백 경로 확정
|
||||
- [ ] 공통 세션 구조 정의
|
||||
|
||||
## 다음 진행 항목
|
||||
|
||||
### 지금 바로 할 일
|
||||
|
||||
- [ ] BARON-SSO callback URL 을 실제 구현 경로로 확정한다.
|
||||
- [ ] BARON-SSO 에서 내려주는 id token 또는 userinfo claim 샘플 1건을 확보한다.
|
||||
- [ ] 공통 auth bootstrap 파일명을 확정한다.
|
||||
- [ ] Descope 세션 구조와 BARON 세션 구조 매핑표를 작성한다.
|
||||
|
||||
### 다음 구현 순서
|
||||
|
||||
- [ ] 1단계: BARON 로그인 시작 엔드포인트 추가
|
||||
- [ ] 2단계: BARON callback 엔드포인트 추가
|
||||
- [ ] 3단계: callback 에서 공통 `$_SESSION['user']` 생성
|
||||
- [ ] 4단계: Q&A 진입 파일들의 `descope_session.php` include 를 공통 auth bootstrap 으로 교체
|
||||
- [ ] 5단계: 로그인 UI 에 사내 사용자 로그인 버튼 추가
|
||||
|
||||
### 구현 전에 확정되면 좋은 값
|
||||
|
||||
- [ ] 내부 사용자 기본 이동 페이지
|
||||
- [ ] 로그인 성공 후 이동 페이지가 메인인지 Q&A 인지 결정
|
||||
- [ ] 로그아웃 후 이동 페이지 결정
|
||||
- [ ] BARON-SSO 에서 제공하는 부서, 계열사, 이메일 claim key 명칭 확정
|
||||
|
||||
### P1
|
||||
|
||||
- [ ] 공통 auth bootstrap 구현
|
||||
- [ ] BARON-SSO callback 구현
|
||||
- [ ] Q&A 진입점 공통 auth 로 치환
|
||||
- [ ] 로그아웃 처리 추가
|
||||
|
||||
### P2
|
||||
|
||||
- [ ] 로그인 UI 개선
|
||||
- [ ] 관리자 권한 체계 정리
|
||||
- [ ] 영문 사이트 동기화
|
||||
- [ ] 운영 문서와 배포 절차 정리
|
||||
|
||||
## 메모
|
||||
|
||||
- 현재 구조에서는 Q&A 비즈니스 로직보다 인증 세션 통합이 핵심 작업이다.
|
||||
- 가장 안전한 방향은 BARON-SSO 사용자도 최종적으로 기존과 동일한 `$_SESSION['user']` 구조를 갖게 만드는 것이다.
|
||||
- 이렇게 하면 Q&A 자체의 수정량을 줄이고, 외부 사용자용 Descope 흐름도 그대로 유지할 수 있다.
|
||||
@@ -138,6 +138,15 @@ Gitea 저장소 화면에서 아래 순서로 실행한다.
|
||||
- `G5_MYSQL_PASSWORD`: 운영 DB 비밀번호
|
||||
- `MARIADB_ROOT_PASSWORD`: 운영 MariaDB root 비밀번호
|
||||
- `COMPOSE_PROJECT_NAME`: 예) `egbim`
|
||||
- `BARON_SSO_CLIENT_ID`: BARON 서버사이드 앱 Client ID
|
||||
- `BARON_SSO_CLIENT_SECRET`: BARON 서버사이드 앱 Client Secret
|
||||
- `BARON_SSO_AUTHORIZE_ENDPOINT`: `https://app.brsw.kr/oidc/oauth2/auth`
|
||||
- `BARON_SSO_TOKEN_ENDPOINT`: `https://app.brsw.kr/oidc/oauth2/token`
|
||||
- `BARON_SSO_USERINFO_ENDPOINT`: `https://app.brsw.kr/oidc/userinfo`
|
||||
- `BARON_SSO_ISSUER`: `https://app.brsw.kr/oidc`
|
||||
- `BARON_SSO_REDIRECT_URI`: 운영 콜백 URL 예) `https://<운영도메인>/auth/callback`
|
||||
- `BARON_SSO_SCOPE`: 예) `openid profile email tenants`
|
||||
- `BARON_SSO_CLIENT_AUTH_METHOD`: 보통 `basic`, 필요 시 `post`
|
||||
|
||||
이 워크플로는 위 시크릿으로 서버의 `.env` 파일을 생성한 뒤 배포를 수행한다.
|
||||
|
||||
@@ -169,6 +178,15 @@ G5_MYSQL_USER=egbim
|
||||
G5_MYSQL_PASSWORD=<set-on-server>
|
||||
MARIADB_ROOT_PASSWORD=<set-on-server>
|
||||
COMPOSE_PROJECT_NAME=egbim
|
||||
BARON_SSO_CLIENT_ID=<set-on-server>
|
||||
BARON_SSO_CLIENT_SECRET=<set-on-server>
|
||||
BARON_SSO_AUTHORIZE_ENDPOINT=https://app.brsw.kr/oidc/oauth2/auth
|
||||
BARON_SSO_TOKEN_ENDPOINT=https://app.brsw.kr/oidc/oauth2/token
|
||||
BARON_SSO_USERINFO_ENDPOINT=https://app.brsw.kr/oidc/userinfo
|
||||
BARON_SSO_ISSUER=https://app.brsw.kr/oidc
|
||||
BARON_SSO_REDIRECT_URI=https://<운영도메인>/auth/callback
|
||||
BARON_SSO_SCOPE=openid profile email tenants
|
||||
BARON_SSO_CLIENT_AUTH_METHOD=basic
|
||||
```
|
||||
|
||||
## 5-1. SQL 덤프 포함 주의
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
2. 필요하면 `.env.docker.example` 내용을 참고해 `.env` 파일을 만든다.
|
||||
3. DB 덤프가 있으면 `docker/mysql/init/` 아래에 `.sql` 파일로 넣는다.
|
||||
4. 기본 로컬 DB명은 운영과 맞춘 `egbim` 이다.
|
||||
5. BARON-SSO 를 로컬에서 테스트하려면 BARON 앱 설정에 로컬 Redirect URI 를 추가해야 한다.
|
||||
|
||||
## 실행
|
||||
|
||||
@@ -20,6 +21,57 @@
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
## BARON-SSO 로컬 테스트 설정
|
||||
|
||||
`.env` 에 아래 값을 채운다.
|
||||
|
||||
```dotenv
|
||||
BARON_SSO_CLIENT_ID=<client id>
|
||||
BARON_SSO_CLIENT_SECRET=<client secret>
|
||||
BARON_SSO_AUTHORIZE_ENDPOINT=https://app.brsw.kr/oidc/oauth2/auth
|
||||
BARON_SSO_TOKEN_ENDPOINT=https://app.brsw.kr/oidc/oauth2/token
|
||||
BARON_SSO_USERINFO_ENDPOINT=https://app.brsw.kr/oidc/userinfo
|
||||
BARON_SSO_ISSUER=https://app.brsw.kr/oidc
|
||||
BARON_SSO_REDIRECT_URI=http://localhost:8088/auth/callback
|
||||
BARON_SSO_SCOPE=openid profile email tenants
|
||||
BARON_SSO_CLIENT_AUTH_METHOD=basic
|
||||
```
|
||||
|
||||
`BARON_SSO_CLIENT_AUTH_METHOD` 는 우선 `basic` 으로 두고, 토큰 엔드포인트가 `invalid_client` 를 반환하면 `post` 로 바꿔 다시 확인한다.
|
||||
|
||||
BARON 관리자 화면의 Redirect URI 허용 목록에도 아래 중 하나를 추가한다.
|
||||
|
||||
```text
|
||||
http://localhost:8088/auth/callback
|
||||
http://eg-bim.com:8088/auth/callback
|
||||
```
|
||||
|
||||
운영에 등록된 `https://eg-bim.com/auth/callback` 만 허용되어 있으면 로컬 callback 은 실패한다.
|
||||
|
||||
## 로컬 확인 순서
|
||||
|
||||
1. `docker compose up --build -d`
|
||||
2. `docker compose ps` 로 `app`, `db` 상태 확인
|
||||
3. 브라우저에서 `http://localhost:8088/egbim/` 접속
|
||||
4. 로그인 팝업에서 `사내 사용자 로그인` 버튼 클릭
|
||||
5. BARON 로그인 후 `http://localhost:8088/auth/callback` 로 복귀하는지 확인
|
||||
6. 복귀 후 `http://localhost:8088/egbim/bbs/descope_qa_list.php` 로 이동하는지 확인
|
||||
|
||||
## 빠른 점검 URL
|
||||
|
||||
- BARON 시작점: `http://localhost:8088/auth/baron_login?site=egbim&return_url=/egbim/bbs/descope_qa_list.php`
|
||||
- BARON 콜백: `http://localhost:8088/auth/callback`
|
||||
- 국문 Q&A: `http://localhost:8088/egbim/bbs/descope_qa_list.php`
|
||||
- 영문 Q&A: `http://localhost:8088/eng/bbs/descope_qa_list.php`
|
||||
|
||||
## 문제 발생 시 먼저 볼 항목
|
||||
|
||||
- Redirect URI 가 BARON 허용 목록과 정확히 일치하는지 확인
|
||||
- `.env` 값이 비어 있지 않은지 확인
|
||||
- 컨테이너 재기동 전에 `.env` 변경분이 반영되었는지 확인
|
||||
- callback 경로가 `/auth/callback` 으로 등록되어 있는지 확인
|
||||
- 브라우저 개발자도구 Network 에서 token endpoint 호출 전 redirect 단계가 정상인지 확인
|
||||
|
||||
## 접속
|
||||
|
||||
- 기본: `http://localhost:8080/egbim/`
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/common.php';
|
||||
|
||||
auth_session_start_if_needed();
|
||||
|
||||
$site = ($_GET['site'] ?? 'egbim') === 'eng' ? 'eng' : 'egbim';
|
||||
$returnUrl = trim((string) ($_GET['return_url'] ?? auth_site_qna_url($site)));
|
||||
|
||||
if (!auth_baron_is_configured()) {
|
||||
auth_script_redirect('BARON-SSO 설정이 아직 완료되지 않았습니다.', auth_site_base_url($site) . '/index.php');
|
||||
}
|
||||
|
||||
$config = auth_baron_config();
|
||||
$statePayload = auth_baron_state_payload($site, $returnUrl);
|
||||
$state = auth_store_baron_state($statePayload);
|
||||
|
||||
$query = http_build_query([
|
||||
'client_id' => $config['client_id'],
|
||||
'response_type' => 'code',
|
||||
'redirect_uri' => auth_baron_redirect_uri(),
|
||||
'scope' => $config['scope'],
|
||||
'state' => $state,
|
||||
'nonce' => $statePayload['nonce'],
|
||||
]);
|
||||
|
||||
header('Location: ' . $config['authorize_endpoint'] . '?' . $query, true, 302);
|
||||
exit;
|
||||
?>
|
||||
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/common.php';
|
||||
|
||||
auth_session_start_if_needed();
|
||||
|
||||
$error = trim((string) ($_GET['error'] ?? ''));
|
||||
$state = trim((string) ($_GET['state'] ?? ''));
|
||||
$code = trim((string) ($_GET['code'] ?? ''));
|
||||
|
||||
$fallbackSite = 'egbim';
|
||||
|
||||
if ($error !== '') {
|
||||
auth_script_redirect('BARON-SSO 로그인에 실패했습니다: ' . $error, auth_site_base_url($fallbackSite) . '/index.php');
|
||||
}
|
||||
|
||||
if ($state === '' || $code === '') {
|
||||
auth_script_redirect('BARON-SSO 응답값이 올바르지 않습니다.', auth_site_base_url($fallbackSite) . '/index.php');
|
||||
}
|
||||
|
||||
$statePayload = auth_consume_baron_state($state);
|
||||
if ($statePayload === null) {
|
||||
auth_script_redirect('BARON-SSO state 검증에 실패했습니다.', auth_site_base_url($fallbackSite) . '/index.php');
|
||||
}
|
||||
|
||||
$site = ($statePayload['site'] ?? 'egbim') === 'eng' ? 'eng' : 'egbim';
|
||||
$redirectUrl = $statePayload['return_url'] ?? auth_site_qna_url($site);
|
||||
$config = auth_baron_config();
|
||||
|
||||
if (!auth_baron_is_configured()) {
|
||||
auth_script_redirect('BARON-SSO 설정이 아직 완료되지 않았습니다.', auth_site_base_url($site) . '/index.php');
|
||||
}
|
||||
|
||||
$tokenRequestData = [
|
||||
'grant_type' => 'authorization_code',
|
||||
'redirect_uri' => auth_baron_redirect_uri(),
|
||||
'code' => $code,
|
||||
];
|
||||
|
||||
$basicAuthUser = null;
|
||||
$basicAuthPassword = null;
|
||||
|
||||
if ($config['client_auth_method'] === 'post') {
|
||||
$tokenRequestData['client_id'] = $config['client_id'];
|
||||
$tokenRequestData['client_secret'] = $config['client_secret'];
|
||||
} else {
|
||||
$tokenRequestData['client_id'] = $config['client_id'];
|
||||
$basicAuthUser = $config['client_id'];
|
||||
$basicAuthPassword = $config['client_secret'];
|
||||
}
|
||||
|
||||
$tokenResponse = auth_http_post_form(
|
||||
$config['token_endpoint'],
|
||||
$tokenRequestData,
|
||||
[],
|
||||
$basicAuthUser,
|
||||
$basicAuthPassword
|
||||
);
|
||||
|
||||
if ($tokenResponse['error'] !== '') {
|
||||
auth_script_redirect('BARON-SSO 토큰 요청에 실패했습니다.', auth_site_base_url($site) . '/index.php');
|
||||
}
|
||||
|
||||
$tokenData = json_decode($tokenResponse['body'], true);
|
||||
if ($tokenResponse['http_code'] < 200 || $tokenResponse['http_code'] >= 300 || !is_array($tokenData)) {
|
||||
if (auth_is_local_debug()) {
|
||||
$debugMessage = sprintf(
|
||||
'BARON token error HTTP %d | body=%s',
|
||||
$tokenResponse['http_code'],
|
||||
auth_compact_debug_value((string) $tokenResponse['body'])
|
||||
);
|
||||
auth_script_redirect($debugMessage, auth_site_base_url($site) . '/index.php');
|
||||
}
|
||||
|
||||
auth_script_redirect('BARON-SSO 토큰 응답을 처리할 수 없습니다.', auth_site_base_url($site) . '/index.php');
|
||||
}
|
||||
|
||||
$claims = [];
|
||||
$idToken = trim((string) ($tokenData['id_token'] ?? ''));
|
||||
if ($idToken !== '') {
|
||||
$claims = auth_jwt_payload($idToken);
|
||||
}
|
||||
|
||||
if ($config['userinfo_endpoint'] !== '' && !empty($tokenData['access_token'])) {
|
||||
$userInfoResponse = auth_http_get_json($config['userinfo_endpoint'], (string) $tokenData['access_token']);
|
||||
$userInfoData = json_decode($userInfoResponse['body'], true);
|
||||
if ($userInfoResponse['http_code'] >= 200 && $userInfoResponse['http_code'] < 300 && is_array($userInfoData)) {
|
||||
$claims = array_merge($claims, $userInfoData);
|
||||
}
|
||||
}
|
||||
|
||||
$user = auth_baron_user_from_claims($claims);
|
||||
if (empty($user['userId']) || empty($user['loginIds'])) {
|
||||
auth_script_redirect('BARON-SSO 사용자 정보를 확인할 수 없습니다.', auth_site_base_url($site) . '/index.php');
|
||||
}
|
||||
|
||||
$normalizedUser = auth_apply_user_session($user, 'baron', [
|
||||
'baron_access_token' => (string) ($tokenData['access_token'] ?? ''),
|
||||
'baron_id_token' => $idToken,
|
||||
]);
|
||||
|
||||
setcookie('descope_login_id', '', auth_cookie_options(time() - 42000));
|
||||
setcookie('descope_user_id', '', auth_cookie_options(time() - 42000));
|
||||
setcookie('descope_user_name', '', auth_cookie_options(time() - 42000));
|
||||
setcookie('descope_user_email', '', auth_cookie_options(time() - 42000));
|
||||
setcookie('descope_user_phone', '', auth_cookie_options(time() - 42000));
|
||||
setcookie('descope_custom_attributes', '', auth_cookie_options(time() - 42000));
|
||||
setcookie('descope_role_names', '', auth_cookie_options(time() - 42000));
|
||||
auth_set_descope_session_cookies($normalizedUser);
|
||||
auth_set_baron_session_cookies($normalizedUser, $claims);
|
||||
|
||||
$clientSessionPayload = [
|
||||
'loginId' => (string) ($normalizedUser['loginIds'][0] ?? ''),
|
||||
'descopeUserId' => (string) ($normalizedUser['userId'] ?? ''),
|
||||
'userName' => (string) ($normalizedUser['name'] ?? ''),
|
||||
'phone' => (string) ($normalizedUser['phone'] ?? ''),
|
||||
'company' => (string) ($normalizedUser['customAttributes']['company'] ?? ''),
|
||||
'familyCompany' => (string) ($normalizedUser['customAttributes']['familyCompany'] ?? ''),
|
||||
'team' => (string) ($normalizedUser['customAttributes']['team'] ?? ''),
|
||||
'position' => (string) ($normalizedUser['customAttributes']['position'] ?? ''),
|
||||
'familyUniqueKey' => (string) ($normalizedUser['customAttributes']['familyUniqueKey'] ?? ''),
|
||||
'userRole' => (string) (($normalizedUser['roleNames'][0] ?? '')),
|
||||
'authProvider' => 'baron',
|
||||
'sessionJwt' => $idToken !== '' ? $idToken : (string) ($tokenData['access_token'] ?? ''),
|
||||
'baronClaims' => json_encode($claims, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
];
|
||||
|
||||
$jsonPayload = json_encode($clientSessionPayload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$jsonRedirectUrl = json_encode($redirectUrl, JSON_UNESCAPED_SLASHES);
|
||||
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
echo <<<HTML
|
||||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<title>Signing in...</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
(function () {
|
||||
var payload = {$jsonPayload} || {};
|
||||
var redirectUrl = {$jsonRedirectUrl};
|
||||
|
||||
Object.keys(payload).forEach(function (key) {
|
||||
var value = payload[key];
|
||||
if (typeof value === 'string' && value !== '') {
|
||||
sessionStorage.setItem(key, value);
|
||||
localStorage.setItem(key, value);
|
||||
} else {
|
||||
sessionStorage.removeItem(key);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
window.dispatchEvent(new CustomEvent('descope-auth-changed', {
|
||||
detail: { loginId: payload.loginId || '' }
|
||||
}));
|
||||
} catch (e) {}
|
||||
|
||||
window.location.replace(redirectUrl);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
exit;
|
||||
?>
|
||||
+790
@@ -0,0 +1,790 @@
|
||||
<?php
|
||||
|
||||
function auth_session_start_if_needed(): void
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
}
|
||||
|
||||
function auth_cookie_options(int $expires): array
|
||||
{
|
||||
return [
|
||||
'expires' => $expires,
|
||||
'path' => '/',
|
||||
'samesite' => 'Lax',
|
||||
];
|
||||
}
|
||||
|
||||
function auth_json_cookie_encode($value): string
|
||||
{
|
||||
return base64_encode(json_encode($value, JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
function auth_json_cookie_decode(string $value, $default)
|
||||
{
|
||||
if ($value === '') {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$decoded = base64_decode($value, true);
|
||||
if ($decoded === false) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$json = json_decode($decoded, true);
|
||||
|
||||
return $json === null ? $default : $json;
|
||||
}
|
||||
|
||||
function auth_current_host(): string
|
||||
{
|
||||
return $_SERVER['HTTP_HOST'] ?? 'eg-bim.com';
|
||||
}
|
||||
|
||||
function auth_current_scheme(): string
|
||||
{
|
||||
$https = $_SERVER['HTTPS'] ?? '';
|
||||
$forwardedProto = $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '';
|
||||
|
||||
if ($https === 'on' || $https === '1' || strtolower((string) $forwardedProto) === 'https') {
|
||||
return 'https';
|
||||
}
|
||||
|
||||
return 'http';
|
||||
}
|
||||
|
||||
function auth_absolute_url(string $path): string
|
||||
{
|
||||
return auth_current_scheme() . '://' . auth_current_host() . $path;
|
||||
}
|
||||
|
||||
function auth_site_base_url(string $site): string
|
||||
{
|
||||
return $site === 'eng' ? '/eng' : '/egbim';
|
||||
}
|
||||
|
||||
function auth_site_qna_url(string $site): string
|
||||
{
|
||||
return auth_site_base_url($site) . '/bbs/descope_qa_list.php';
|
||||
}
|
||||
|
||||
function auth_baron_config(): array
|
||||
{
|
||||
$scopes = trim((string) (getenv('BARON_SSO_SCOPE') ?: 'openid profile email'));
|
||||
|
||||
return [
|
||||
'client_id' => trim((string) getenv('BARON_SSO_CLIENT_ID')),
|
||||
'client_secret' => trim((string) getenv('BARON_SSO_CLIENT_SECRET')),
|
||||
'authorize_endpoint' => trim((string) getenv('BARON_SSO_AUTHORIZE_ENDPOINT')),
|
||||
'token_endpoint' => trim((string) getenv('BARON_SSO_TOKEN_ENDPOINT')),
|
||||
'userinfo_endpoint' => trim((string) getenv('BARON_SSO_USERINFO_ENDPOINT')),
|
||||
'issuer' => trim((string) getenv('BARON_SSO_ISSUER')),
|
||||
'redirect_uri' => trim((string) getenv('BARON_SSO_REDIRECT_URI')),
|
||||
'scope' => $scopes,
|
||||
'client_auth_method' => trim((string) (getenv('BARON_SSO_CLIENT_AUTH_METHOD') ?: 'basic')),
|
||||
];
|
||||
}
|
||||
|
||||
function auth_baron_redirect_uri(): string
|
||||
{
|
||||
$config = auth_baron_config();
|
||||
if ($config['redirect_uri'] !== '') {
|
||||
return $config['redirect_uri'];
|
||||
}
|
||||
|
||||
return auth_absolute_url('/auth/callback');
|
||||
}
|
||||
|
||||
function auth_baron_is_configured(): bool
|
||||
{
|
||||
$config = auth_baron_config();
|
||||
|
||||
return $config['client_id'] !== ''
|
||||
&& $config['client_secret'] !== ''
|
||||
&& $config['authorize_endpoint'] !== ''
|
||||
&& $config['token_endpoint'] !== '';
|
||||
}
|
||||
|
||||
function auth_normalize_user(array $user): array
|
||||
{
|
||||
$loginIds = $user['loginIds'] ?? [];
|
||||
if (!is_array($loginIds)) {
|
||||
$loginIds = [];
|
||||
}
|
||||
|
||||
$loginId = trim((string) ($loginIds[0] ?? $user['loginId'] ?? $user['email'] ?? ''));
|
||||
if ($loginId !== '' && empty($loginIds)) {
|
||||
$loginIds = [$loginId];
|
||||
}
|
||||
|
||||
$customAttributes = $user['customAttributes'] ?? [];
|
||||
$roleNames = $user['roleNames'] ?? [];
|
||||
|
||||
return [
|
||||
'userId' => trim((string) ($user['userId'] ?? $loginId)),
|
||||
'loginIds' => $loginIds,
|
||||
'name' => trim((string) ($user['name'] ?? '')),
|
||||
'email' => trim((string) ($user['email'] ?? $loginId)),
|
||||
'phone' => trim((string) ($user['phone'] ?? '')),
|
||||
'customAttributes' => is_array($customAttributes) ? $customAttributes : [],
|
||||
'roleNames' => is_array($roleNames) ? $roleNames : [],
|
||||
];
|
||||
}
|
||||
|
||||
function auth_descope_user_from_cookies(): ?array
|
||||
{
|
||||
$loginId = trim((string) ($_COOKIE['descope_login_id'] ?? ''));
|
||||
if ($loginId === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return auth_normalize_user([
|
||||
'userId' => trim((string) ($_COOKIE['descope_user_id'] ?? $loginId)),
|
||||
'loginIds' => [$loginId],
|
||||
'name' => trim((string) ($_COOKIE['descope_user_name'] ?? '')),
|
||||
'email' => trim((string) ($_COOKIE['descope_user_email'] ?? $loginId)),
|
||||
'phone' => trim((string) ($_COOKIE['descope_user_phone'] ?? '')),
|
||||
'customAttributes' => auth_json_cookie_decode((string) ($_COOKIE['descope_custom_attributes'] ?? ''), []),
|
||||
'roleNames' => auth_json_cookie_decode((string) ($_COOKIE['descope_role_names'] ?? ''), []),
|
||||
]);
|
||||
}
|
||||
|
||||
function auth_baron_user_from_cookies(): ?array
|
||||
{
|
||||
$encodedUser = (string) ($_COOKIE['baron_user'] ?? '');
|
||||
if ($encodedUser === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$user = auth_json_cookie_decode($encodedUser, []);
|
||||
if (!is_array($user) || empty($user)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return auth_normalize_user($user);
|
||||
}
|
||||
|
||||
function auth_restore_user_session(): void
|
||||
{
|
||||
auth_session_start_if_needed();
|
||||
|
||||
if (!empty($_SESSION['user']['userId'])) {
|
||||
$_SESSION['user'] = auth_normalize_user($_SESSION['user']);
|
||||
return;
|
||||
}
|
||||
|
||||
$user = auth_baron_user_from_cookies();
|
||||
if ($user === null) {
|
||||
$user = auth_descope_user_from_cookies();
|
||||
}
|
||||
|
||||
if ($user !== null) {
|
||||
$_SESSION['user'] = $user;
|
||||
}
|
||||
}
|
||||
|
||||
function auth_apply_user_session(array $user, string $provider, array $tokens = []): array
|
||||
{
|
||||
auth_session_start_if_needed();
|
||||
session_regenerate_id(true);
|
||||
|
||||
$normalizedUser = auth_normalize_user($user);
|
||||
|
||||
$_SESSION['user'] = $normalizedUser;
|
||||
$_SESSION['auth_provider'] = $provider;
|
||||
|
||||
foreach ($tokens as $key => $value) {
|
||||
$_SESSION[$key] = $value;
|
||||
}
|
||||
|
||||
return $normalizedUser;
|
||||
}
|
||||
|
||||
function auth_set_descope_session_cookies(array $user): void
|
||||
{
|
||||
$normalizedUser = auth_normalize_user($user);
|
||||
$expire = time() + 86400;
|
||||
$cookieOptions = auth_cookie_options($expire);
|
||||
|
||||
setcookie('descope_login_id', (string) ($normalizedUser['loginIds'][0] ?? ''), $cookieOptions);
|
||||
setcookie('descope_user_id', (string) $normalizedUser['userId'], $cookieOptions);
|
||||
setcookie('descope_user_name', (string) $normalizedUser['name'], $cookieOptions);
|
||||
setcookie('descope_user_email', (string) $normalizedUser['email'], $cookieOptions);
|
||||
setcookie('descope_user_phone', (string) $normalizedUser['phone'], $cookieOptions);
|
||||
setcookie('descope_custom_attributes', auth_json_cookie_encode($normalizedUser['customAttributes']), $cookieOptions);
|
||||
setcookie('descope_role_names', auth_json_cookie_encode($normalizedUser['roleNames']), $cookieOptions);
|
||||
}
|
||||
|
||||
function auth_set_baron_session_cookies(array $user, array $claims = []): void
|
||||
{
|
||||
$normalizedUser = auth_normalize_user($user);
|
||||
$expire = time() + 86400;
|
||||
$cookieOptions = auth_cookie_options($expire);
|
||||
|
||||
setcookie('baron_user', auth_json_cookie_encode($normalizedUser), $cookieOptions);
|
||||
setcookie('baron_claims', auth_json_cookie_encode($claims), $cookieOptions);
|
||||
setcookie('baron_provider', 'baron', $cookieOptions);
|
||||
}
|
||||
|
||||
function auth_clear_all_cookies(): void
|
||||
{
|
||||
$expired = auth_cookie_options(time() - 42000);
|
||||
|
||||
foreach ([
|
||||
'PHPSESSID',
|
||||
'G5sessphp',
|
||||
'descope_login_id',
|
||||
'descope_user_id',
|
||||
'descope_user_name',
|
||||
'descope_user_email',
|
||||
'descope_user_phone',
|
||||
'descope_custom_attributes',
|
||||
'descope_role_names',
|
||||
'baron_user',
|
||||
'baron_claims',
|
||||
'baron_provider',
|
||||
] as $cookieName) {
|
||||
setcookie($cookieName, '', $expired);
|
||||
}
|
||||
}
|
||||
|
||||
function auth_admin_emails(): array
|
||||
{
|
||||
return [
|
||||
'b23008@baroncs.co.kr',
|
||||
'kjy0426@hanmaceng.co.kr',
|
||||
'b24014@hanmaceng.co.kr',
|
||||
'b23065@hanmaceng.co.kr',
|
||||
'shyeom1@samaneng.com',
|
||||
'cjy627@hanmaceng.co.kr',
|
||||
'b23072@hanmaceng.co.kr',
|
||||
'cozyjin@hanmaceng.co.kr',
|
||||
'm24031@hanmaceng.co.kr',
|
||||
'b24051@hanmaceng.co.kr',
|
||||
'rmsgud1202@hanmaceng.co.kr',
|
||||
'm21318@hanmaceng.co.kr',
|
||||
'b21367@hanmaceng.co.kr',
|
||||
'b25023@hanmaceng.co.kr',
|
||||
'sdi9429@naver.com',
|
||||
'junsuy@hanmail.net',
|
||||
'ilphilo92@gmail.com',
|
||||
];
|
||||
}
|
||||
|
||||
function auth_normalize_identity(string $value): string
|
||||
{
|
||||
return strtolower(trim($value));
|
||||
}
|
||||
|
||||
function auth_is_admin_login(string $loginId): bool
|
||||
{
|
||||
$normalizedLoginId = auth_normalize_identity($loginId);
|
||||
if ($normalizedLoginId === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (auth_admin_emails() as $adminLoginId) {
|
||||
if ($normalizedLoginId === auth_normalize_identity($adminLoginId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function auth_is_admin_user(array $user, ?string $loginId = null): bool
|
||||
{
|
||||
$candidates = [];
|
||||
|
||||
if ($loginId !== null) {
|
||||
$candidates[] = $loginId;
|
||||
}
|
||||
|
||||
$loginIds = $user['loginIds'] ?? [];
|
||||
if (is_array($loginIds)) {
|
||||
foreach ($loginIds as $candidate) {
|
||||
if (is_string($candidate)) {
|
||||
$candidates[] = $candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$candidates[] = (string) ($user['email'] ?? '');
|
||||
|
||||
$customAttributes = $user['customAttributes'] ?? [];
|
||||
if (is_array($customAttributes)) {
|
||||
$candidates[] = (string) ($customAttributes['employeeId'] ?? '');
|
||||
$candidates[] = (string) ($customAttributes['familyUniqueKey'] ?? '');
|
||||
}
|
||||
|
||||
foreach ($candidates as $candidate) {
|
||||
if (auth_is_admin_login((string) $candidate)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$roleNames = $user['roleNames'] ?? [];
|
||||
if (is_array($roleNames)) {
|
||||
foreach ($roleNames as $roleName) {
|
||||
$normalizedRole = auth_normalize_identity((string) $roleName);
|
||||
if ($normalizedRole === 'super' || $normalizedRole === 'admin' || $normalizedRole === 'administrator') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function auth_sync_gnuboard_session(): array
|
||||
{
|
||||
auth_restore_user_session();
|
||||
|
||||
$loginIds = $_SESSION['user']['loginIds'] ?? [];
|
||||
$loginId = (is_array($loginIds) && !empty($loginIds)) ? trim((string) $loginIds[0]) : '';
|
||||
$userName = trim((string) ($_SESSION['user']['name'] ?? ''));
|
||||
$userEmail = trim((string) ($_SESSION['user']['email'] ?? ''));
|
||||
$isAdmin = !empty($_SESSION['user']) && is_array($_SESSION['user'])
|
||||
? auth_is_admin_user($_SESSION['user'], $loginId)
|
||||
: false;
|
||||
|
||||
if ($loginId !== '') {
|
||||
$_SESSION['ss_mb_id'] = $loginId;
|
||||
$_SESSION['ss_mb_level'] = $isAdmin ? 10 : 2;
|
||||
|
||||
return [
|
||||
'loginId' => $loginId,
|
||||
'isAdmin' => $isAdmin,
|
||||
'is_member' => true,
|
||||
'is_admin' => $isAdmin ? 'super' : '',
|
||||
'member' => [
|
||||
'mb_id' => $loginId,
|
||||
'mb_name' => $userName !== '' ? $userName : $loginId,
|
||||
'mb_email' => $userEmail,
|
||||
'mb_level' => $_SESSION['ss_mb_level'],
|
||||
'mb_nick' => $userName !== '' ? $userName : $loginId,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
unset($_SESSION['ss_mb_id'], $_SESSION['ss_mb_level']);
|
||||
|
||||
return [
|
||||
'loginId' => '',
|
||||
'isAdmin' => false,
|
||||
'is_member' => false,
|
||||
'is_admin' => '',
|
||||
'member' => [],
|
||||
];
|
||||
}
|
||||
|
||||
function auth_url_is_safe(string $url, string $site): bool
|
||||
{
|
||||
if ($url === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strpos($url, '://') !== false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strpos($url, auth_site_base_url($site) . '/') === 0;
|
||||
}
|
||||
|
||||
function auth_baron_state_payload(string $site, string $returnUrl): array
|
||||
{
|
||||
return [
|
||||
'site' => $site,
|
||||
'return_url' => auth_url_is_safe($returnUrl, $site) ? $returnUrl : auth_site_qna_url($site),
|
||||
'nonce' => bin2hex(random_bytes(16)),
|
||||
'created_at' => time(),
|
||||
];
|
||||
}
|
||||
|
||||
function auth_store_baron_state(array $payload): string
|
||||
{
|
||||
auth_session_start_if_needed();
|
||||
|
||||
$state = bin2hex(random_bytes(16));
|
||||
$_SESSION['baron_oauth_state'] = [
|
||||
'value' => $state,
|
||||
'payload' => $payload,
|
||||
];
|
||||
|
||||
return $state;
|
||||
}
|
||||
|
||||
function auth_consume_baron_state(string $state): ?array
|
||||
{
|
||||
auth_session_start_if_needed();
|
||||
|
||||
$stored = $_SESSION['baron_oauth_state'] ?? null;
|
||||
unset($_SESSION['baron_oauth_state']);
|
||||
|
||||
if (!is_array($stored) || ($stored['value'] ?? '') !== $state) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$payload = $stored['payload'] ?? null;
|
||||
|
||||
return is_array($payload) ? $payload : null;
|
||||
}
|
||||
|
||||
function auth_http_post_form(string $url, array $data, array $headers = [], ?string $username = null, ?string $password = null): array
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
|
||||
|
||||
$mergedHeaders = array_merge([
|
||||
'Accept: application/json',
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
], $headers);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $mergedHeaders);
|
||||
|
||||
if ($username !== null) {
|
||||
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $username . ':' . ($password ?? ''));
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return [
|
||||
'body' => $response === false ? '' : $response,
|
||||
'http_code' => $httpCode,
|
||||
'error' => $error,
|
||||
];
|
||||
}
|
||||
|
||||
function auth_http_get_json(string $url, string $accessToken): array
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Accept: application/json',
|
||||
'Authorization: Bearer ' . $accessToken,
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return [
|
||||
'body' => $response === false ? '' : $response,
|
||||
'http_code' => $httpCode,
|
||||
'error' => $error,
|
||||
];
|
||||
}
|
||||
|
||||
function auth_jwt_payload(string $jwt): array
|
||||
{
|
||||
$parts = explode('.', $jwt);
|
||||
if (count($parts) < 2) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$payload = strtr($parts[1], '-_', '+/');
|
||||
$padding = strlen($payload) % 4;
|
||||
if ($padding > 0) {
|
||||
$payload .= str_repeat('=', 4 - $padding);
|
||||
}
|
||||
|
||||
$decoded = base64_decode($payload, true);
|
||||
if ($decoded === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$json = json_decode($decoded, true);
|
||||
|
||||
return is_array($json) ? $json : [];
|
||||
}
|
||||
|
||||
function auth_first_non_empty_string(...$values): string
|
||||
{
|
||||
foreach ($values as $value) {
|
||||
if (is_string($value)) {
|
||||
$trimmed = trim($value);
|
||||
if ($trimmed !== '') {
|
||||
return $trimmed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function auth_claim_list($value): array
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$stringValue = trim((string) $value);
|
||||
if ($stringValue === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [$stringValue];
|
||||
}
|
||||
|
||||
function auth_extract_phone_from_claims(array $claims): string
|
||||
{
|
||||
$profile = isset($claims['profile']) && is_array($claims['profile']) ? $claims['profile'] : [];
|
||||
$phones = auth_claim_list($claims['phones'] ?? ($profile['phones'] ?? []));
|
||||
|
||||
foreach ($phones as $phoneEntry) {
|
||||
if (is_array($phoneEntry)) {
|
||||
$candidate = auth_first_non_empty_string(
|
||||
$phoneEntry['value'] ?? '',
|
||||
$phoneEntry['phone_number'] ?? '',
|
||||
$phoneEntry['number'] ?? ''
|
||||
);
|
||||
if ($candidate !== '') {
|
||||
return $candidate;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$candidate = trim((string) $phoneEntry);
|
||||
if ($candidate !== '') {
|
||||
return $candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return auth_first_non_empty_string(
|
||||
$claims['phone_number'] ?? '',
|
||||
$claims['phone'] ?? '',
|
||||
$profile['phone_number'] ?? '',
|
||||
$profile['phone'] ?? ''
|
||||
);
|
||||
}
|
||||
|
||||
function auth_extract_company_fields(array $claims): array
|
||||
{
|
||||
$company = auth_first_non_empty_string(
|
||||
$claims['company'] ?? '',
|
||||
$claims['company_name'] ?? '',
|
||||
$claims['organization'] ?? '',
|
||||
$claims['organization_name'] ?? '',
|
||||
$claims['tenant_name'] ?? ''
|
||||
);
|
||||
$familyCompany = auth_first_non_empty_string(
|
||||
$claims['familyCompany'] ?? '',
|
||||
$claims['family_company'] ?? '',
|
||||
$claims['group_name'] ?? '',
|
||||
$claims['affiliate'] ?? ''
|
||||
);
|
||||
$team = auth_first_non_empty_string(
|
||||
$claims['team'] ?? '',
|
||||
$claims['department'] ?? '',
|
||||
$claims['dept'] ?? '',
|
||||
$claims['division'] ?? '',
|
||||
$claims['org_unit'] ?? ''
|
||||
);
|
||||
|
||||
$tenants = auth_claim_list($claims['tenants'] ?? []);
|
||||
|
||||
$primaryTenant = null;
|
||||
$tenantId = trim((string) ($claims['tenant_id'] ?? ''));
|
||||
|
||||
foreach ($tenants as $tenantKey => $tenant) {
|
||||
if (!is_array($tenant)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$candidateId = trim((string) ($tenant['id'] ?? (is_string($tenantKey) ? $tenantKey : '')));
|
||||
if (($tenant['isPrimary'] ?? false) === true || ($tenant['representative'] ?? false) === true || ($tenantId !== '' && $candidateId === $tenantId)) {
|
||||
$primaryTenant = $tenant;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($primaryTenant === null) {
|
||||
foreach ($tenants as $tenant) {
|
||||
if (is_array($tenant)) {
|
||||
$primaryTenant = $tenant;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($primaryTenant)) {
|
||||
$companyAncestor = '';
|
||||
$familyCompanyAncestor = '';
|
||||
$departmentAncestor = '';
|
||||
|
||||
$ancestors = $primaryTenant['ancestors'] ?? [];
|
||||
if (is_array($ancestors)) {
|
||||
foreach ($ancestors as $ancestor) {
|
||||
if (!is_array($ancestor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ancestorType = strtoupper(trim((string) ($ancestor['type'] ?? '')));
|
||||
$ancestorName = trim((string) ($ancestor['name'] ?? ''));
|
||||
if ($ancestorName === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($companyAncestor === '' && $ancestorType === 'COMPANY') {
|
||||
$companyAncestor = $ancestorName;
|
||||
}
|
||||
|
||||
if ($familyCompanyAncestor === '' && $ancestorType === 'COMPANY_GROUP') {
|
||||
$familyCompanyAncestor = $ancestorName;
|
||||
}
|
||||
|
||||
if ($ancestorType === 'ORGANIZATION') {
|
||||
$departmentAncestor = $ancestorName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$company = auth_first_non_empty_string(
|
||||
$company,
|
||||
$companyAncestor,
|
||||
trim((string) ($primaryTenant['name'] ?? ''))
|
||||
);
|
||||
|
||||
$familyCompany = auth_first_non_empty_string(
|
||||
$familyCompany,
|
||||
$companyAncestor,
|
||||
$familyCompanyAncestor,
|
||||
$company
|
||||
);
|
||||
|
||||
$team = auth_first_non_empty_string(
|
||||
$team,
|
||||
$departmentAncestor,
|
||||
trim((string) ($primaryTenant['name'] ?? ''))
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($tenants as $tenant) {
|
||||
if (!is_array($tenant)) {
|
||||
if ($company === '') {
|
||||
$company = trim((string) $tenant);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($company === '') {
|
||||
$company = auth_first_non_empty_string(
|
||||
$tenant['name'] ?? '',
|
||||
$tenant['display_name'] ?? '',
|
||||
$tenant['company'] ?? '',
|
||||
$tenant['company_name'] ?? '',
|
||||
$tenant['organization'] ?? ''
|
||||
);
|
||||
}
|
||||
|
||||
if ($familyCompany === '') {
|
||||
$familyCompany = auth_first_non_empty_string(
|
||||
$tenant['familyCompany'] ?? '',
|
||||
$tenant['family_company'] ?? '',
|
||||
$tenant['group_name'] ?? '',
|
||||
$tenant['affiliate'] ?? ''
|
||||
);
|
||||
}
|
||||
|
||||
if ($team === '') {
|
||||
$team = auth_first_non_empty_string(
|
||||
$tenant['team'] ?? '',
|
||||
$tenant['department'] ?? '',
|
||||
$tenant['dept'] ?? '',
|
||||
$tenant['division'] ?? '',
|
||||
$tenant['org_unit'] ?? ''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'company' => $company,
|
||||
'familyCompany' => $familyCompany,
|
||||
'team' => $team,
|
||||
];
|
||||
}
|
||||
|
||||
function auth_baron_user_from_claims(array $claims): array
|
||||
{
|
||||
$profile = isset($claims['profile']) && is_array($claims['profile']) ? $claims['profile'] : [];
|
||||
$email = trim((string) ($claims['email'] ?? $profile['email'] ?? $claims['upn'] ?? $claims['preferred_username'] ?? $claims['sub'] ?? ''));
|
||||
$name = trim((string) ($claims['name'] ?? $profile['name'] ?? $claims['display_name'] ?? $claims['preferred_username'] ?? $email));
|
||||
$phone = auth_extract_phone_from_claims($claims);
|
||||
$companyFields = auth_extract_company_fields($claims);
|
||||
$company = $companyFields['company'];
|
||||
$familyCompany = $companyFields['familyCompany'];
|
||||
$team = $companyFields['team'];
|
||||
$employeeId = trim((string) ($claims['employee_id'] ?? $profile['employee_id'] ?? $claims['employeeId'] ?? $claims['familyUniqueKey'] ?? ''));
|
||||
$status = trim((string) ($claims['status'] ?? $profile['status'] ?? ''));
|
||||
$tenants = auth_claim_list($claims['tenants'] ?? []);
|
||||
$secondaryEmails = auth_claim_list($claims['secondary_emails'] ?? $claims['secondaryEmails'] ?? ($profile['secondary_emails'] ?? []));
|
||||
$roles = auth_claim_list($claims['roles'] ?? $claims['roleNames'] ?? []);
|
||||
$position = auth_first_non_empty_string(
|
||||
$claims['position'] ?? '',
|
||||
$profile['position'] ?? ''
|
||||
);
|
||||
|
||||
foreach ($tenants as $tenant) {
|
||||
if (!is_array($tenant)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($position === '') {
|
||||
$position = auth_first_non_empty_string(
|
||||
$tenant['grade'] ?? '',
|
||||
$tenant['position'] ?? ''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return auth_normalize_user([
|
||||
'userId' => trim((string) ($claims['sub'] ?? $email)),
|
||||
'loginIds' => $email !== '' ? [$email] : [],
|
||||
'name' => $name,
|
||||
'email' => $email,
|
||||
'phone' => $phone,
|
||||
'customAttributes' => [
|
||||
'company' => $company,
|
||||
'familyCompany' => $familyCompany,
|
||||
'team' => $team,
|
||||
'position' => $position,
|
||||
'employeeId' => $employeeId,
|
||||
'familyUniqueKey' => $employeeId,
|
||||
'status' => $status,
|
||||
'tenants' => $tenants,
|
||||
'secondaryEmails' => $secondaryEmails,
|
||||
],
|
||||
'roleNames' => $roles,
|
||||
]);
|
||||
}
|
||||
|
||||
function auth_script_redirect(string $message, string $redirectUrl): void
|
||||
{
|
||||
$safeMessage = htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
|
||||
$safeUrl = htmlspecialchars($redirectUrl, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
echo "<script>alert('{$safeMessage}'); window.location.href = '{$safeUrl}';</script>";
|
||||
exit;
|
||||
}
|
||||
|
||||
function auth_is_local_debug(): bool
|
||||
{
|
||||
return (getenv('APP_ENV') ?: '') === 'local';
|
||||
}
|
||||
|
||||
function auth_compact_debug_value(string $value, int $maxLength = 300): string
|
||||
{
|
||||
$value = preg_replace('/\s+/', ' ', trim($value)) ?? '';
|
||||
if (strlen($value) <= $maxLength) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return substr($value, 0, $maxLength) . '...';
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/common.php';
|
||||
|
||||
auth_session_start_if_needed();
|
||||
|
||||
if (!auth_is_local_debug()) {
|
||||
http_response_code(404);
|
||||
exit;
|
||||
}
|
||||
|
||||
header('Content-Type: application/json; charset=UTF-8');
|
||||
|
||||
$baronClaims = auth_json_cookie_decode((string) ($_COOKIE['baron_claims'] ?? ''), []);
|
||||
$baronUser = auth_baron_user_from_cookies();
|
||||
$sessionUser = $_SESSION['user'] ?? [];
|
||||
|
||||
echo json_encode([
|
||||
'auth_provider' => $_SESSION['auth_provider'] ?? '',
|
||||
'session_user' => is_array($sessionUser) ? $sessionUser : [],
|
||||
'baron_user_cookie' => $baronUser,
|
||||
'baron_claims_cookie' => is_array($baronClaims) ? $baronClaims : [],
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
@@ -13,6 +13,15 @@ services:
|
||||
G5_MYSQL_USER: ${G5_MYSQL_USER:-egbim}
|
||||
G5_MYSQL_PASSWORD: ${G5_MYSQL_PASSWORD:-baron3840!!}
|
||||
G5_MYSQL_DB: ${G5_MYSQL_DB:-egbim}
|
||||
BARON_SSO_CLIENT_ID: ${BARON_SSO_CLIENT_ID:-}
|
||||
BARON_SSO_CLIENT_SECRET: ${BARON_SSO_CLIENT_SECRET:-}
|
||||
BARON_SSO_AUTHORIZE_ENDPOINT: ${BARON_SSO_AUTHORIZE_ENDPOINT:-}
|
||||
BARON_SSO_TOKEN_ENDPOINT: ${BARON_SSO_TOKEN_ENDPOINT:-}
|
||||
BARON_SSO_USERINFO_ENDPOINT: ${BARON_SSO_USERINFO_ENDPOINT:-}
|
||||
BARON_SSO_ISSUER: ${BARON_SSO_ISSUER:-}
|
||||
BARON_SSO_REDIRECT_URI: ${BARON_SSO_REDIRECT_URI:-}
|
||||
BARON_SSO_SCOPE: ${BARON_SSO_SCOPE:-openid profile email}
|
||||
BARON_SSO_CLIENT_AUTH_METHOD: ${BARON_SSO_CLIENT_AUTH_METHOD:-basic}
|
||||
ports:
|
||||
- "${APP_PORT:-8088}:80"
|
||||
volumes:
|
||||
|
||||
@@ -1,31 +1,12 @@
|
||||
<?php
|
||||
// /egbim/bbs/admin_guard.php
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) @session_start();
|
||||
require_once dirname(__DIR__, 2) . '/auth/common.php';
|
||||
|
||||
if (!function_exists('is_qna_admin')) {
|
||||
function is_qna_admin(): bool {
|
||||
$loginId = $_SESSION['user']['loginIds'][0] ?? '';
|
||||
$allow = [
|
||||
'kjy0426@hanmaceng.co.kr',
|
||||
'b24014@hanmaceng.co.kr',
|
||||
'b23065@hanmaceng.co.kr',
|
||||
'b23008@baroncs.co.kr',
|
||||
'cjy627@hanmaceng.co.kr',
|
||||
'b23072@hanmaceng.co.kr',
|
||||
'b24051@hanmaceng.co.kr',
|
||||
'rmsgud1202@hanmaceng.co.kr',
|
||||
'm21318@hanmaceng.co.kr',
|
||||
'b21367@hanmaceng.co.kr',
|
||||
'sdi9429@naver.com',
|
||||
'junsuy@hanmail.net',
|
||||
'ilphilo92@gmail.com'
|
||||
];
|
||||
$norm = static function($s){ return strtolower(trim((string)$s)); };
|
||||
$loginId = $norm($loginId);
|
||||
foreach ($allow as $id) {
|
||||
if ($loginId === $norm($id)) return true;
|
||||
}
|
||||
return false;
|
||||
function is_qna_admin(?string $loginId = null): bool {
|
||||
$user = $_SESSION['user'] ?? [];
|
||||
return is_array($user) && auth_is_admin_user($user, $loginId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -5885,7 +5885,7 @@ footer .address em.tel {
|
||||
padding: 0;
|
||||
}
|
||||
.login form {
|
||||
gap: 24px;
|
||||
gap: 10px;
|
||||
}
|
||||
.login .input_wrap {
|
||||
width: 100%;
|
||||
|
||||
@@ -1,25 +1,7 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||
|
||||
function set_descope_session_cookies(array $user): void {
|
||||
$expire = time() + 86400;
|
||||
$cookieOptions = [
|
||||
'expires' => $expire,
|
||||
'path' => '/',
|
||||
'samesite' => 'Lax',
|
||||
];
|
||||
|
||||
$customAttributes = $user['customAttributes'] ?? [];
|
||||
$roleNames = $user['roleNames'] ?? [];
|
||||
|
||||
setcookie('descope_login_id', (string)($user['loginIds'][0] ?? ''), $cookieOptions);
|
||||
setcookie('descope_user_id', (string)($user['userId'] ?? ''), $cookieOptions);
|
||||
setcookie('descope_user_name', (string)($user['name'] ?? ''), $cookieOptions);
|
||||
setcookie('descope_user_email', (string)($user['email'] ?? ''), $cookieOptions);
|
||||
setcookie('descope_user_phone', (string)($user['phone'] ?? ''), $cookieOptions);
|
||||
setcookie('descope_custom_attributes', base64_encode(json_encode($customAttributes, JSON_UNESCAPED_UNICODE)), $cookieOptions);
|
||||
setcookie('descope_role_names', base64_encode(json_encode($roleNames, JSON_UNESCAPED_UNICODE)), $cookieOptions);
|
||||
}
|
||||
auth_session_start_if_needed();
|
||||
|
||||
$email = trim($_POST['mb_id'] ?? '');
|
||||
$password = trim($_POST['mb_password'] ?? '');
|
||||
@@ -60,16 +42,24 @@ if ($http_code === 200 && isset($res_data['sessionJwt'])) {
|
||||
// 로그인 성공 → 실패횟수 초기화
|
||||
$_SESSION['login_fail_count'] = 0;
|
||||
//session으로도 저장
|
||||
$_SESSION['user'] = $res_data['user']; // userId, email 등
|
||||
$_SESSION['sessionJwt'] = $res_data['sessionJwt'];
|
||||
$_SESSION['refreshJwt'] = $res_data['refreshJwt'];
|
||||
set_descope_session_cookies($res_data['user'] ?? []);
|
||||
$normalizedUser = auth_apply_user_session(
|
||||
$res_data['user'] ?? [],
|
||||
'descope',
|
||||
[
|
||||
'sessionJwt' => $res_data['sessionJwt'] ?? '',
|
||||
'refreshJwt' => $res_data['refreshJwt'] ?? '',
|
||||
]
|
||||
);
|
||||
setcookie('baron_user', '', auth_cookie_options(time() - 42000));
|
||||
setcookie('baron_claims', '', auth_cookie_options(time() - 42000));
|
||||
setcookie('baron_provider', '', auth_cookie_options(time() - 42000));
|
||||
auth_set_descope_session_cookies($normalizedUser);
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'ok',
|
||||
'sessionJwt' => $res_data['sessionJwt'] ?? '',
|
||||
// 'refreshJwt' => $res_data['refreshJwt'] ?? '',
|
||||
'user' => $res_data['user'] ?? [],
|
||||
'user' => $normalizedUser,
|
||||
'session' => $_SESSION,
|
||||
]);
|
||||
// } else {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||
// descope_logout.php
|
||||
// session_start();
|
||||
// $_SESSION = [];
|
||||
@@ -16,17 +17,9 @@
|
||||
// http_response_code(200);
|
||||
// echo json_encode(['status'=>'ok']);
|
||||
// exit;
|
||||
session_start();
|
||||
auth_session_start_if_needed();
|
||||
$_SESSION = [];
|
||||
|
||||
foreach (['PHPSESSID', 'G5sessphp', 'descope_login_id', 'descope_user_id', 'descope_user_name', 'descope_user_email', 'descope_user_phone', 'descope_custom_attributes', 'descope_role_names'] as $cookieName) {
|
||||
setcookie($cookieName, '', [
|
||||
'expires' => time() - 42000,
|
||||
'path' => '/',
|
||||
'samesite' => 'Lax',
|
||||
]);
|
||||
}
|
||||
|
||||
auth_clear_all_cookies();
|
||||
session_destroy();
|
||||
|
||||
http_response_code(200);
|
||||
|
||||
@@ -1,148 +1,11 @@
|
||||
<?php
|
||||
// // descope_session.php
|
||||
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||
|
||||
// // 1) 에러 출력 (개발용)
|
||||
// // ini_set('display_errors', 1);
|
||||
// // error_reporting(E_ALL);
|
||||
$authState = auth_sync_gnuboard_session();
|
||||
|
||||
// // 2) 세션 시작 (중복 호출 방지)
|
||||
// if (session_status() === PHP_SESSION_NONE) {
|
||||
// session_start();
|
||||
// }
|
||||
|
||||
// // 3) 로그인 검사
|
||||
// // 로그인 시 $_SESSION['user']['userId'] 와 $_SESSION['user']['loginIds'][0] 를 설정해 두었다면
|
||||
// // if (empty($_SESSION['user']['userId'])) {
|
||||
// // // 로그인 필요 알림 후 로그인 페이지로 이동
|
||||
// // echo <<<HTML
|
||||
// // <script>
|
||||
// // alert('Q&A를 이용하시려면 로그인이 필요합니다.');
|
||||
// // window.location.href = '/egbim/index.php';
|
||||
// // </script>
|
||||
// // HTML;
|
||||
// // exit;
|
||||
// // }
|
||||
|
||||
// // ✅ Descope 로그인 성공 시 g5_member 세션 동기화
|
||||
// $loginId = $_SESSION['user']['loginIds'][0] ?? '';
|
||||
// if ($loginId === 'b24014@hanmaceng.co.kr') {
|
||||
// // g5_member 계정이 이미 존재한다고 가정
|
||||
// $_SESSION['ss_mb_id'] = $loginId;
|
||||
// $_SESSION['ss_mb_level'] = 10;
|
||||
|
||||
// $is_member = true;
|
||||
// $is_admin = 'super';
|
||||
|
||||
// $member = [
|
||||
// 'mb_id' => $loginId,
|
||||
// 'mb_name' => $_SESSION['user']['name'] ?? '관리자',
|
||||
// 'mb_email' => $_SESSION['user']['email'] ?? '',
|
||||
// 'mb_level' => 10,
|
||||
// 'mb_nick' => '관리자',
|
||||
// ];
|
||||
// }
|
||||
// // Descope 로그인 성공 시 (예: descope_session.php 내부)
|
||||
// if (!empty($_SESSION['user']['loginIds'][0])) {
|
||||
// $loginId = $_SESSION['user']['loginIds'][0];
|
||||
|
||||
// // g5_member 테이블에 계정이 존재한다고 가정
|
||||
// $_SESSION['ss_mb_id'] = $loginId;
|
||||
// $_SESSION['ss_mb_level'] = 10; // 관리자 레벨
|
||||
|
||||
// // 그누보드 전역 변수 보정
|
||||
// $is_member = true;
|
||||
// $is_admin = 'super';
|
||||
|
||||
// $member = [
|
||||
// 'mb_id' => $loginId,
|
||||
// 'mb_name' => $_SESSION['user']['name'] ?? '관리자',
|
||||
// 'mb_email' => $_SESSION['user']['email'] ?? '',
|
||||
// 'mb_level' => 10,
|
||||
// 'mb_nick' => '관리자',
|
||||
// ];
|
||||
// }
|
||||
// descope_session.php
|
||||
|
||||
// 1) 세션 시작
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (empty($_SESSION['user']['userId']) && !empty($_COOKIE['descope_login_id'])) {
|
||||
$customAttributes = json_decode(base64_decode($_COOKIE['descope_custom_attributes'] ?? ''), true);
|
||||
$roleNames = json_decode(base64_decode($_COOKIE['descope_role_names'] ?? ''), true);
|
||||
$loginIdFromCookie = trim((string)($_COOKIE['descope_login_id'] ?? ''));
|
||||
|
||||
$_SESSION['user'] = [
|
||||
'userId' => trim((string)($_COOKIE['descope_user_id'] ?? $loginIdFromCookie)),
|
||||
'loginIds' => $loginIdFromCookie !== '' ? [$loginIdFromCookie] : [],
|
||||
'name' => trim((string)($_COOKIE['descope_user_name'] ?? '')),
|
||||
'email' => trim((string)($_COOKIE['descope_user_email'] ?? '')),
|
||||
'phone' => trim((string)($_COOKIE['descope_user_phone'] ?? '')),
|
||||
'customAttributes' => is_array($customAttributes) ? $customAttributes : [],
|
||||
'roleNames' => is_array($roleNames) ? $roleNames : [],
|
||||
];
|
||||
}
|
||||
|
||||
// 2) 로그인 검사 (비로그인 차단)
|
||||
// if (empty($_SESSION['user']['userId'])) {
|
||||
// // 세션 없으면 바로 차단
|
||||
// session_destroy();
|
||||
// echo "<script>
|
||||
// alert('로그인이 필요합니다.');
|
||||
// // 로그인 팝업 자동 오픈 플래그 전달
|
||||
// window.location.href = '/egbim/index.php?popup=login';
|
||||
// </script>";
|
||||
// exit;
|
||||
// }
|
||||
|
||||
// 3) 로그인 정보 추출
|
||||
$loginIds = $_SESSION['user']['loginIds'] ?? [];
|
||||
$loginId = (is_array($loginIds) && !empty($loginIds)) ? $loginIds[0] : '';
|
||||
|
||||
$userName = $_SESSION['user']['name'] ?? '';
|
||||
$userEmail = $_SESSION['user']['email'] ?? '';
|
||||
|
||||
// 4) 관리자/일반 사용자 구분
|
||||
$ADMIN_EMAILS = [
|
||||
'kjy0426@hanmaceng.co.kr',
|
||||
'b24014@hanmaceng.co.kr',
|
||||
'b23065@hanmaceng.co.kr',
|
||||
'shyeom1@samaneng.com',
|
||||
'cjy627@hanmaceng.co.kr',
|
||||
'b23072@hanmaceng.co.kr',
|
||||
'cozyjin@hanmaceng.co.kr',
|
||||
'm24031@hanmaceng.co.kr',
|
||||
'b24051@hanmaceng.co.kr',
|
||||
'rmsgud1202@hanmaceng.co.kr',
|
||||
'm21318@hanmaceng.co.kr',
|
||||
'b25023@hanmaceng.co.kr',
|
||||
'sdi9429@naver.com',
|
||||
'junsuy@hanmail.net',
|
||||
'ilphilo92@gmail.com'
|
||||
];
|
||||
// $isAdmin = in_array($loginId, $ADMIN_EMAILS, true);
|
||||
$isAdmin = $loginId && in_array($loginId, $ADMIN_EMAILS, true);
|
||||
|
||||
// ✅ 그누보드 전역 세션 동기화
|
||||
if ($loginId) {
|
||||
$_SESSION['ss_mb_id'] = $loginId;
|
||||
$_SESSION['ss_mb_level'] = $isAdmin ? 10 : 2;
|
||||
|
||||
$is_member = true;
|
||||
$is_admin = $isAdmin ? 'super' : '';
|
||||
|
||||
$member = [
|
||||
'mb_id' => $loginId,
|
||||
'mb_name' => $userName ?: $loginId,
|
||||
'mb_email' => $userEmail,
|
||||
'mb_level' => $_SESSION['ss_mb_level'],
|
||||
'mb_nick' => $userName ?: $loginId,
|
||||
];
|
||||
} else {
|
||||
// ✅ 비회원: 로그인 세션 없음
|
||||
$is_member = false;
|
||||
$is_admin = '';
|
||||
$member = [];
|
||||
}
|
||||
$loginId = $authState['loginId'];
|
||||
$isAdmin = $authState['isAdmin'];
|
||||
$is_member = $authState['is_member'];
|
||||
$is_admin = $authState['is_admin'];
|
||||
$member = $authState['member'];
|
||||
?>
|
||||
@@ -1,66 +1,38 @@
|
||||
<?php
|
||||
// header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||
|
||||
// $management_key = 'P2wON5fy1K6kyia269VpeIzYP8oP:K2ycqpjeh1voPBdxXxzB3ScZOQ6v9aiLmU2cIj70X1H8Kcoz0KWfCWmofUwAsAkJroXA8QC';
|
||||
// $loginId = $_POST['loginId'] ?? '';
|
||||
auth_session_start_if_needed();
|
||||
header('Content-Type: application/json; charset=UTF-8');
|
||||
|
||||
// $url = "https://api.descope.com/v2/mgmt/user/search";
|
||||
// $payload = json_encode([
|
||||
// "loginId" => $loginId,
|
||||
// "limit" => 1
|
||||
// ]);
|
||||
$loginId = trim((string) ($_POST['loginId'] ?? ''));
|
||||
$sessionUser = $_SESSION['user'] ?? [];
|
||||
$sessionLoginId = trim((string) ($sessionUser['loginIds'][0] ?? ''));
|
||||
|
||||
// $headers = [
|
||||
// "Authorization: Bearer $management_key",
|
||||
// "Content-Type: application/json"
|
||||
// ];
|
||||
if ($loginId !== '' && $sessionLoginId !== '' && strcasecmp($loginId, $sessionLoginId) === 0) {
|
||||
echo json_encode(['status' => 'ok', 'user' => auth_normalize_user($sessionUser)]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// $ch = curl_init($url);
|
||||
// curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
// curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
// curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
// $response = curl_exec($ch);
|
||||
// $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
// curl_close($ch);
|
||||
|
||||
// if ($http_code === 200) {
|
||||
// $res = json_decode($response, true);
|
||||
|
||||
// if (!empty($res['users'][0])) {
|
||||
// // ✅ 먼저 $user에 복사
|
||||
// $user = $res['users'][0];
|
||||
|
||||
// // ✅ 값이 없으면 테스트용 기본값 삽입
|
||||
// $user['name'] = $user['name'] ?: '홍길동';
|
||||
// $user['phone'] = $user['phone'] ?: '01012345678';
|
||||
// $user['customAttributes']['company'] = $user['customAttributes']['company'] ?? '한맥ENG';
|
||||
// $user['customAttributes']['team'] = $user['customAttributes']['team'] ?? '설계팀';
|
||||
// $user['customAttributes']['employeeId'] = $user['customAttributes']['employeeId'] ?? 'HM2024';
|
||||
|
||||
// // ✅ 수정된 $user 사용
|
||||
// echo json_encode(['status' => 'ok', 'user' => $user]);
|
||||
// } else {
|
||||
// echo json_encode(['status' => 'fail', 'message' => '사용자를 찾을 수 없음']);
|
||||
// }
|
||||
// } else {
|
||||
// echo json_encode(['status' => 'fail', 'message' => 'Descope API 오류', 'code' => $http_code]);
|
||||
// }
|
||||
?>
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
$cookieUser = auth_baron_user_from_cookies();
|
||||
if ($loginId !== '' && $cookieUser !== null) {
|
||||
$cookieLoginId = trim((string) ($cookieUser['loginIds'][0] ?? ''));
|
||||
if ($cookieLoginId !== '' && strcasecmp($loginId, $cookieLoginId) === 0) {
|
||||
echo json_encode(['status' => 'ok', 'user' => $cookieUser]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$management_key = 'P2wON5fy1K6kyia269VpeIzYP8oP:K32l5ORmzy32OvaaPvpdZsMY3JmKQb7a3vvrl10PgjlJUGk3K7EssMH3uW5VGQSbrgtEdPj';
|
||||
$loginId = $_POST['loginId'] ?? '';
|
||||
|
||||
$url = "https://api.descope.com/v2/mgmt/user/search";
|
||||
$url = 'https://api.descope.com/v2/mgmt/user/search';
|
||||
$payload = json_encode([
|
||||
"loginId" => $loginId,
|
||||
"limit" => 1
|
||||
'loginId' => $loginId,
|
||||
'limit' => 1,
|
||||
]);
|
||||
|
||||
$headers = [
|
||||
"Authorization: Bearer $management_key",
|
||||
"Content-Type: application/json"
|
||||
'Content-Type: application/json',
|
||||
];
|
||||
|
||||
$ch = curl_init($url);
|
||||
@@ -76,18 +48,17 @@ if ($http_code === 200) {
|
||||
|
||||
if (!empty($res['users'][0])) {
|
||||
$user = $res['users'][0];
|
||||
|
||||
// ✅ 실제 값이 있으면 그대로 사용, 없으면 빈 문자열
|
||||
$user['name'] = $user['name'] ?? '';
|
||||
$user['phone'] = $user['phone'] ?? '';
|
||||
$user['customAttributes']['company'] = $user['customAttributes']['company'] ?? '';
|
||||
$user['customAttributes']['familyCompany'] = $user['customAttributes']['familyCompany'] ?? '';
|
||||
$user['customAttributes']['team'] = $user['customAttributes']['team'] ?? '';
|
||||
$user['customAttributes']['employeeId'] = $user['customAttributes']['employeeId'] ?? '';
|
||||
$user['customAttributes']['familyUniqueKey'] = $user['customAttributes']['familyUniqueKey'] ?? '';
|
||||
|
||||
echo json_encode(['status' => 'ok', 'user' => $user]);
|
||||
} else {
|
||||
echo json_encode(['status' => 'fail', 'message' => '사용자를 찾을 수 없음']);
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['status' => 'fail', 'message' => 'Descope API 오류', 'code' => $http_code]);
|
||||
}
|
||||
|
||||
echo json_encode(['status' => 'fail', 'message' => '사용자를 찾을 수 없음', 'code' => $http_code]);
|
||||
|
||||
@@ -52,8 +52,12 @@
|
||||
<div class="join_btn_wrap">
|
||||
<button type="submit" id="btn_login">로그인</button>
|
||||
</div>
|
||||
<div class="join_btn_wrap" style="margin-top:10px;">
|
||||
<a href="/auth/baron_login?site=egbim&return_url=/egbim/bbs/descope_qa_list.php" style="display:block; width:100%; text-align:center; padding:14px 16px; border:1px solid #0f3a2f; border-radius:10px; color:#0f3a2f; font-weight:700;">BARON-SSO 로그인</a>
|
||||
</div>
|
||||
<p style="margin-bottom:20px; font-size:16px; font-weight:700; color:#c62828; line-height:1.6; text-align:center;">eg-bim 1.5.1 버전 이상 사용자는 baron-sso로 로그인해야 합니다.</p>
|
||||
<div class="btn_go">
|
||||
<div class="go_signup"><a href="javascript:agreement();"><span>회원가입</span><i class="arrow_r"></i></a></div>
|
||||
<div class="go_signup"><a href="#" class="signup-disabled-link" aria-disabled="true"><span>회원가입</span><i class="arrow_r"></i></a></div>
|
||||
<div class="go_find"><a href="javascript:search();"><span>비밀번호 재설정</span><i class="arrow_r"></i></a></div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -127,6 +131,10 @@ $(document).ready(function () {
|
||||
e.preventDefault(); // 기본 제출 막기
|
||||
$('#btn_login').click(); // 버튼 클릭 이벤트 재사용
|
||||
});
|
||||
|
||||
$('.signup-disabled-link').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
@@ -55,7 +55,7 @@ $download_url = "https://release-download.hmac.kr/eg-bim/{$version}/setup_eg-bim
|
||||
<a href="javascript:descopeLogout();">로그아웃</a>
|
||||
</li>
|
||||
<li id="my_join" class="my_join">
|
||||
<a href="javascript:agreement();">회원가입</a>
|
||||
<a href="#" class="signup-disabled-link" aria-disabled="true">회원가입</a>
|
||||
</li>
|
||||
<li id="my_login" class="my_login">
|
||||
<a href="javascript:login();">로그인</a>
|
||||
@@ -94,9 +94,84 @@ $download_url = "https://release-download.hmac.kr/eg-bim/{$version}/setup_eg-bim
|
||||
// e.preventDefault(); // 링크 이동 막기
|
||||
// alert('다운로드 기능은 준비중입니다.');
|
||||
// });
|
||||
</script>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
function getCookieValue(name) {
|
||||
const prefix = name + '=';
|
||||
const parts = document.cookie.split(';');
|
||||
for (let i = 0; i < parts.length; i += 1) {
|
||||
const part = parts[i].trim();
|
||||
if (part.indexOf(prefix) === 0) {
|
||||
return part.substring(prefix.length);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function decodeBase64Json(value) {
|
||||
if (!value) return null;
|
||||
|
||||
try {
|
||||
const binary = window.atob(value);
|
||||
const bytes = Array.prototype.map.call(binary, function (char) {
|
||||
return '%' + ('00' + char.charCodeAt(0).toString(16)).slice(-2);
|
||||
}).join('');
|
||||
return JSON.parse(decodeURIComponent(bytes));
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function setStoredValue(key, value) {
|
||||
if (typeof value === 'string' && value !== '') {
|
||||
sessionStorage.setItem(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
function restoreClientAuthSessionFromCookies() {
|
||||
const baronUser = decodeBase64Json(getCookieValue('baron_user'));
|
||||
|
||||
if (baronUser && Array.isArray(baronUser.loginIds) && baronUser.loginIds[0]) {
|
||||
const customAttributes = baronUser.customAttributes || {};
|
||||
const roleNames = Array.isArray(baronUser.roleNames) ? baronUser.roleNames : [];
|
||||
|
||||
setStoredValue('authProvider', 'baron');
|
||||
setStoredValue('loginId', baronUser.loginIds[0] || '');
|
||||
setStoredValue('descopeUserId', baronUser.userId || '');
|
||||
setStoredValue('userName', baronUser.name || '');
|
||||
setStoredValue('phone', baronUser.phone || '');
|
||||
setStoredValue('company', customAttributes.company || '');
|
||||
setStoredValue('familyCompany', customAttributes.familyCompany || '');
|
||||
setStoredValue('team', customAttributes.team || '');
|
||||
setStoredValue('position', customAttributes.position || '');
|
||||
setStoredValue('familyUniqueKey', customAttributes.familyUniqueKey || '');
|
||||
setStoredValue('userRole', roleNames[0] || '');
|
||||
setStoredValue('sessionJwt', sessionStorage.getItem('sessionJwt') || 'baron.session.marker');
|
||||
return;
|
||||
}
|
||||
|
||||
const descopeLoginId = getCookieValue('descope_login_id');
|
||||
if (descopeLoginId && !sessionStorage.getItem('loginId')) {
|
||||
const customAttributes = decodeBase64Json(getCookieValue('descope_custom_attributes')) || {};
|
||||
const roleNames = decodeBase64Json(getCookieValue('descope_role_names')) || [];
|
||||
|
||||
setStoredValue('authProvider', sessionStorage.getItem('authProvider') || 'descope');
|
||||
setStoredValue('loginId', descopeLoginId);
|
||||
setStoredValue('descopeUserId', getCookieValue('descope_user_id'));
|
||||
setStoredValue('userName', getCookieValue('descope_user_name'));
|
||||
setStoredValue('phone', getCookieValue('descope_user_phone'));
|
||||
setStoredValue('company', customAttributes.company || '');
|
||||
setStoredValue('familyCompany', customAttributes.familyCompany || '');
|
||||
setStoredValue('team', customAttributes.team || '');
|
||||
setStoredValue('position', customAttributes.position || '');
|
||||
setStoredValue('familyUniqueKey', customAttributes.familyUniqueKey || '');
|
||||
setStoredValue('userRole', Array.isArray(roleNames) ? (roleNames[0] || '') : '');
|
||||
}
|
||||
}
|
||||
|
||||
restoreClientAuthSessionFromCookies();
|
||||
})();
|
||||
|
||||
(function () {
|
||||
const ADMIN_ICON_IDS = [
|
||||
'b24014@hanmaceng.co.kr',
|
||||
@@ -181,6 +256,10 @@ $download_url = "https://release-download.hmac.kr/eg-bim/{$version}/setup_eg-bim
|
||||
}, 50);
|
||||
});
|
||||
|
||||
$(document).on('click', '.signup-disabled-link', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// 혹시 로그인 팝업을 다른 곳에서 열어도 배너가 필요하면, 팝업 열릴 때 한 번 더 시도
|
||||
$(document).on('show-login-popup', function () {
|
||||
injectBanner();
|
||||
|
||||
@@ -438,7 +438,7 @@ function updateHeaderMenu(){
|
||||
$('#my_join2').html(
|
||||
isLoggedIn && isAdmin
|
||||
? "<a href='/egbim/adm/index.php' target='_blank'>관리페이지</a>"
|
||||
: "<a href='/egbim/index.php'>메인으로</a>"
|
||||
: "<a href='javascript:mypage03();'>마이페이지</a>"
|
||||
);
|
||||
} catch(e){ console.error(e); }
|
||||
}
|
||||
|
||||
@@ -430,7 +430,7 @@ function updateHeaderMenu(){
|
||||
$('#my_join2').html(
|
||||
isLoggedIn && isAdmin
|
||||
? "<a href='/egbim/adm/index.php' target='_blank'>관리페이지</a>"
|
||||
: "<a href='/egbim/index.php'>메인으로</a>"
|
||||
: "<a href='javascript:mypage03();'>마이페이지</a>"
|
||||
);
|
||||
} catch(e){ console.error(e); }
|
||||
}
|
||||
|
||||
+4
-16
@@ -1,24 +1,12 @@
|
||||
<?php
|
||||
// /egbim/bbs/admin_guard.php
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) @session_start();
|
||||
require_once dirname(__DIR__, 2) . '/auth/common.php';
|
||||
|
||||
if (!function_exists('is_qna_admin')) {
|
||||
function is_qna_admin(): bool {
|
||||
$loginId = $_SESSION['user']['loginIds'][0] ?? '';
|
||||
$allow = [
|
||||
'kjy0426@hanmaceng.co.kr',
|
||||
'b24014@hanmaceng.co.kr',
|
||||
'b23065@hanmaceng.co.kr',
|
||||
'b23008@baroncs.co.kr',
|
||||
'cjy627@hanmaceng.co.kr',
|
||||
'b23072@hanmaceng.co.kr',
|
||||
];
|
||||
$norm = static function($s){ return strtolower(trim((string)$s)); };
|
||||
$loginId = $norm($loginId);
|
||||
foreach ($allow as $id) {
|
||||
if ($loginId === $norm($id)) return true;
|
||||
}
|
||||
return false;
|
||||
function is_qna_admin(?string $loginId = null): bool {
|
||||
$user = $_SESSION['user'] ?? [];
|
||||
return is_array($user) && auth_is_admin_user($user, $loginId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+34
-19
@@ -1185,27 +1185,42 @@ $(function () {
|
||||
$(".menu_my").mouseout(function () {
|
||||
$(".menu_my_list").hide();
|
||||
});
|
||||
|
||||
//footer family site toggle
|
||||
$(".family_btn").click(function (event) {
|
||||
event.stopPropagation(); // family_btn 클릭 시 이벤트 전파를 막음
|
||||
$(".family_list").toggleClass("family_on");
|
||||
$(".family_btn").toggleClass("family_on");
|
||||
});
|
||||
|
||||
// 화면 아무 곳이나 클릭했을 때 family_list를 제외한 영역 클릭 시 리스트 닫기
|
||||
$(document).click(function (event) {
|
||||
if (
|
||||
!$(event.target).closest(".family_list").length &&
|
||||
!$(event.target).closest(".family_btn").length
|
||||
) {
|
||||
// family_list와 family_btn 외의 영역을 클릭한 경우
|
||||
$(".family_list").removeClass("family_on");
|
||||
$(".family_btn").removeClass("family_on");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (!window.__egbimFamilySiteToggleBound) {
|
||||
window.__egbimFamilySiteToggleBound = true;
|
||||
|
||||
document.addEventListener(
|
||||
"click",
|
||||
function (event) {
|
||||
const familyButton = event.target.closest(".family_btn");
|
||||
|
||||
if (familyButton) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
const shouldOpen = !familyButton.classList.contains("family_on");
|
||||
document
|
||||
.querySelectorAll(".family_btn, .family_list")
|
||||
.forEach((element) => {
|
||||
element.classList.toggle("family_on", shouldOpen);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!event.target.closest(".family_wrap")) {
|
||||
document
|
||||
.querySelectorAll(".family_btn, .family_list")
|
||||
.forEach((element) => {
|
||||
element.classList.remove("family_on");
|
||||
});
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
// 마우스 스크롤 마크 표시하기
|
||||
// 사용 클래스 : js__mouse_mark , js__mouse_area
|
||||
// + TODO 진슬 추가_ addEventListener error debugging
|
||||
|
||||
+12
-1
@@ -660,8 +660,19 @@ function html_purifier($html)
|
||||
include_once(G5_PLUGIN_PATH . '/htmlpurifier/extend.video.php');
|
||||
|
||||
$config = HTMLPurifier_Config::createDefault();
|
||||
// data/cache 가 없거나 쓰기 불가한 환경에서도 FAQ 출력이 깨지지 않도록 캐시 경로를 보장한다.
|
||||
$serializer_path = G5_DATA_PATH . '/cache';
|
||||
if (!is_dir($serializer_path)) {
|
||||
@mkdir($serializer_path, 0755, true);
|
||||
}
|
||||
if (!is_dir($serializer_path) || !is_writable($serializer_path)) {
|
||||
$serializer_path = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'egbim_htmlpurifier_cache';
|
||||
if (!is_dir($serializer_path)) {
|
||||
@mkdir($serializer_path, 0755, true);
|
||||
}
|
||||
}
|
||||
// data/cache 디렉토리에 CSS, HTML, URI 디렉토리 등을 만든다.
|
||||
$config->set('Cache.SerializerPath', G5_DATA_PATH . '/cache');
|
||||
$config->set('Cache.SerializerPath', $serializer_path);
|
||||
$config->set('HTML.SafeEmbed', false);
|
||||
$config->set('HTML.SafeObject', false);
|
||||
$config->set('Output.FlashCompat', false);
|
||||
|
||||
@@ -268,19 +268,6 @@ echo '<div id="faq_hhtml">'.conv_content($fm['fm_head_html'], 1).'</div>';
|
||||
|
||||
<!-- 상단(헤더) 스크립트 구문 -->
|
||||
<script>
|
||||
$(function(){
|
||||
// footer family site toggle
|
||||
$('.family_btn').click(function(e){
|
||||
e.stopPropagation();
|
||||
$('.family_list, .family_btn').toggleClass('family_on');
|
||||
});
|
||||
$(document).click(function(e){
|
||||
if(!$(e.target).closest('.family_wrap').length){
|
||||
$('.family_list, .family_btn').removeClass('family_on');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 회원 탈퇴
|
||||
function member_leave() {
|
||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||
|
||||
@@ -112,19 +112,6 @@
|
||||
</body>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
// footer family site toggle
|
||||
$('.family_btn').click(function(e){
|
||||
e.stopPropagation();
|
||||
$('.family_list, .family_btn').toggleClass('family_on');
|
||||
});
|
||||
$(document).click(function(e){
|
||||
if(!$(e.target).closest('.family_wrap').length){
|
||||
$('.family_list, .family_btn').removeClass('family_on');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 회원 탈퇴
|
||||
function member_leave() {
|
||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||
|
||||
@@ -1,25 +1,7 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||
|
||||
function set_descope_session_cookies(array $user): void {
|
||||
$expire = time() + 86400;
|
||||
$cookieOptions = [
|
||||
'expires' => $expire,
|
||||
'path' => '/',
|
||||
'samesite' => 'Lax',
|
||||
];
|
||||
|
||||
$customAttributes = $user['customAttributes'] ?? [];
|
||||
$roleNames = $user['roleNames'] ?? [];
|
||||
|
||||
setcookie('descope_login_id', (string)($user['loginIds'][0] ?? ''), $cookieOptions);
|
||||
setcookie('descope_user_id', (string)($user['userId'] ?? ''), $cookieOptions);
|
||||
setcookie('descope_user_name', (string)($user['name'] ?? ''), $cookieOptions);
|
||||
setcookie('descope_user_email', (string)($user['email'] ?? ''), $cookieOptions);
|
||||
setcookie('descope_user_phone', (string)($user['phone'] ?? ''), $cookieOptions);
|
||||
setcookie('descope_custom_attributes', base64_encode(json_encode($customAttributes, JSON_UNESCAPED_UNICODE)), $cookieOptions);
|
||||
setcookie('descope_role_names', base64_encode(json_encode($roleNames, JSON_UNESCAPED_UNICODE)), $cookieOptions);
|
||||
}
|
||||
auth_session_start_if_needed();
|
||||
|
||||
$email = trim($_POST['mb_id'] ?? '');
|
||||
$password = trim($_POST['mb_password'] ?? '');
|
||||
@@ -60,16 +42,24 @@ if ($http_code === 200 && isset($res_data['sessionJwt'])) {
|
||||
// 로그인 성공 → 실패횟수 초기화
|
||||
$_SESSION['login_fail_count'] = 0;
|
||||
//session으로도 저장
|
||||
$_SESSION['user'] = $res_data['user']; // userId, email 등
|
||||
$_SESSION['sessionJwt'] = $res_data['sessionJwt'];
|
||||
$_SESSION['refreshJwt'] = $res_data['refreshJwt'];
|
||||
set_descope_session_cookies($res_data['user'] ?? []);
|
||||
$normalizedUser = auth_apply_user_session(
|
||||
$res_data['user'] ?? [],
|
||||
'descope',
|
||||
[
|
||||
'sessionJwt' => $res_data['sessionJwt'] ?? '',
|
||||
'refreshJwt' => $res_data['refreshJwt'] ?? '',
|
||||
]
|
||||
);
|
||||
setcookie('baron_user', '', auth_cookie_options(time() - 42000));
|
||||
setcookie('baron_claims', '', auth_cookie_options(time() - 42000));
|
||||
setcookie('baron_provider', '', auth_cookie_options(time() - 42000));
|
||||
auth_set_descope_session_cookies($normalizedUser);
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'ok',
|
||||
'sessionJwt' => $res_data['sessionJwt'] ?? '',
|
||||
// 'refreshJwt' => $res_data['refreshJwt'] ?? '',
|
||||
'user' => $res_data['user'] ?? [],
|
||||
'user' => $normalizedUser,
|
||||
'session' => $_SESSION,
|
||||
]);
|
||||
// } else {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||
// descope_logout.php
|
||||
// session_start();
|
||||
// $_SESSION = [];
|
||||
@@ -16,17 +17,9 @@
|
||||
// http_response_code(200);
|
||||
// echo json_encode(['status'=>'ok']);
|
||||
// exit;
|
||||
session_start();
|
||||
auth_session_start_if_needed();
|
||||
$_SESSION = [];
|
||||
|
||||
foreach (['PHPSESSID', 'G5sessphp', 'descope_login_id', 'descope_user_id', 'descope_user_name', 'descope_user_email', 'descope_user_phone', 'descope_custom_attributes', 'descope_role_names'] as $cookieName) {
|
||||
setcookie($cookieName, '', [
|
||||
'expires' => time() - 42000,
|
||||
'path' => '/',
|
||||
'samesite' => 'Lax',
|
||||
]);
|
||||
}
|
||||
|
||||
auth_clear_all_cookies();
|
||||
session_destroy();
|
||||
|
||||
http_response_code(200);
|
||||
|
||||
@@ -1,148 +1,11 @@
|
||||
<?php
|
||||
// // descope_session.php
|
||||
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||
|
||||
// // 1) 에러 출력 (개발용)
|
||||
// // ini_set('display_errors', 1);
|
||||
// // error_reporting(E_ALL);
|
||||
$authState = auth_sync_gnuboard_session();
|
||||
|
||||
// // 2) 세션 시작 (중복 호출 방지)
|
||||
// if (session_status() === PHP_SESSION_NONE) {
|
||||
// session_start();
|
||||
// }
|
||||
|
||||
// // 3) 로그인 검사
|
||||
// // 로그인 시 $_SESSION['user']['userId'] 와 $_SESSION['user']['loginIds'][0] 를 설정해 두었다면
|
||||
// // if (empty($_SESSION['user']['userId'])) {
|
||||
// // // 로그인 필요 알림 후 로그인 페이지로 이동
|
||||
// // echo <<<HTML
|
||||
// // <script>
|
||||
// // alert('Q&A를 이용하시려면 로그인이 필요합니다.');
|
||||
// // window.location.href = '/egbim/index.php';
|
||||
// // </script>
|
||||
// // HTML;
|
||||
// // exit;
|
||||
// // }
|
||||
|
||||
// // ✅ Descope 로그인 성공 시 g5_member 세션 동기화
|
||||
// $loginId = $_SESSION['user']['loginIds'][0] ?? '';
|
||||
// if ($loginId === 'b24014@hanmaceng.co.kr') {
|
||||
// // g5_member 계정이 이미 존재한다고 가정
|
||||
// $_SESSION['ss_mb_id'] = $loginId;
|
||||
// $_SESSION['ss_mb_level'] = 10;
|
||||
|
||||
// $is_member = true;
|
||||
// $is_admin = 'super';
|
||||
|
||||
// $member = [
|
||||
// 'mb_id' => $loginId,
|
||||
// 'mb_name' => $_SESSION['user']['name'] ?? '관리자',
|
||||
// 'mb_email' => $_SESSION['user']['email'] ?? '',
|
||||
// 'mb_level' => 10,
|
||||
// 'mb_nick' => '관리자',
|
||||
// ];
|
||||
// }
|
||||
// // Descope 로그인 성공 시 (예: descope_session.php 내부)
|
||||
// if (!empty($_SESSION['user']['loginIds'][0])) {
|
||||
// $loginId = $_SESSION['user']['loginIds'][0];
|
||||
|
||||
// // g5_member 테이블에 계정이 존재한다고 가정
|
||||
// $_SESSION['ss_mb_id'] = $loginId;
|
||||
// $_SESSION['ss_mb_level'] = 10; // 관리자 레벨
|
||||
|
||||
// // 그누보드 전역 변수 보정
|
||||
// $is_member = true;
|
||||
// $is_admin = 'super';
|
||||
|
||||
// $member = [
|
||||
// 'mb_id' => $loginId,
|
||||
// 'mb_name' => $_SESSION['user']['name'] ?? '관리자',
|
||||
// 'mb_email' => $_SESSION['user']['email'] ?? '',
|
||||
// 'mb_level' => 10,
|
||||
// 'mb_nick' => '관리자',
|
||||
// ];
|
||||
// }
|
||||
// descope_session.php
|
||||
|
||||
// 1) 세션 시작
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (empty($_SESSION['user']['userId']) && !empty($_COOKIE['descope_login_id'])) {
|
||||
$customAttributes = json_decode(base64_decode($_COOKIE['descope_custom_attributes'] ?? ''), true);
|
||||
$roleNames = json_decode(base64_decode($_COOKIE['descope_role_names'] ?? ''), true);
|
||||
$loginIdFromCookie = trim((string)($_COOKIE['descope_login_id'] ?? ''));
|
||||
|
||||
$_SESSION['user'] = [
|
||||
'userId' => trim((string)($_COOKIE['descope_user_id'] ?? $loginIdFromCookie)),
|
||||
'loginIds' => $loginIdFromCookie !== '' ? [$loginIdFromCookie] : [],
|
||||
'name' => trim((string)($_COOKIE['descope_user_name'] ?? '')),
|
||||
'email' => trim((string)($_COOKIE['descope_user_email'] ?? '')),
|
||||
'phone' => trim((string)($_COOKIE['descope_user_phone'] ?? '')),
|
||||
'customAttributes' => is_array($customAttributes) ? $customAttributes : [],
|
||||
'roleNames' => is_array($roleNames) ? $roleNames : [],
|
||||
];
|
||||
}
|
||||
|
||||
// 2) 로그인 검사 (비로그인 차단)
|
||||
// if (empty($_SESSION['user']['userId'])) {
|
||||
// // 세션 없으면 바로 차단
|
||||
// session_destroy();
|
||||
// echo "<script>
|
||||
// alert('로그인이 필요합니다.');
|
||||
// // 로그인 팝업 자동 오픈 플래그 전달
|
||||
// window.location.href = '/egbim/index.php?popup=login';
|
||||
// </script>";
|
||||
// exit;
|
||||
// }
|
||||
|
||||
// 3) 로그인 정보 추출
|
||||
$loginIds = $_SESSION['user']['loginIds'] ?? [];
|
||||
$loginId = (is_array($loginIds) && !empty($loginIds)) ? $loginIds[0] : '';
|
||||
|
||||
$userName = $_SESSION['user']['name'] ?? '';
|
||||
$userEmail = $_SESSION['user']['email'] ?? '';
|
||||
|
||||
// 4) 관리자/일반 사용자 구분
|
||||
$ADMIN_EMAILS = [
|
||||
'kjy0426@hanmaceng.co.kr',
|
||||
'b24014@hanmaceng.co.kr',
|
||||
'b23065@hanmaceng.co.kr',
|
||||
'shyeom1@samaneng.com',
|
||||
'cjy627@hanmaceng.co.kr',
|
||||
'b23072@hanmaceng.co.kr',
|
||||
'cozyjin@hanmaceng.co.kr',
|
||||
'm24031@hanmaceng.co.kr',
|
||||
'b24051@hanmaceng.co.kr',
|
||||
'rmsgud1202@hanmaceng.co.kr',
|
||||
'm21318@hanmaceng.co.kr',
|
||||
'b25023@hanmaceng.co.kr',
|
||||
'sdi9429@naver.com',
|
||||
'junsuy@hanmail.net',
|
||||
'ilphilo92@gmail.com'
|
||||
];
|
||||
// $isAdmin = in_array($loginId, $ADMIN_EMAILS, true);
|
||||
$isAdmin = $loginId && in_array($loginId, $ADMIN_EMAILS, true);
|
||||
|
||||
// ✅ 그누보드 전역 세션 동기화
|
||||
if ($loginId) {
|
||||
$_SESSION['ss_mb_id'] = $loginId;
|
||||
$_SESSION['ss_mb_level'] = $isAdmin ? 10 : 2;
|
||||
|
||||
$is_member = true;
|
||||
$is_admin = $isAdmin ? 'super' : '';
|
||||
|
||||
$member = [
|
||||
'mb_id' => $loginId,
|
||||
'mb_name' => $userName ?: $loginId,
|
||||
'mb_email' => $userEmail,
|
||||
'mb_level' => $_SESSION['ss_mb_level'],
|
||||
'mb_nick' => $userName ?: $loginId,
|
||||
];
|
||||
} else {
|
||||
// ✅ 비회원: 로그인 세션 없음
|
||||
$is_member = false;
|
||||
$is_admin = '';
|
||||
$member = [];
|
||||
}
|
||||
$loginId = $authState['loginId'];
|
||||
$isAdmin = $authState['isAdmin'];
|
||||
$is_member = $authState['is_member'];
|
||||
$is_admin = $authState['is_admin'];
|
||||
$member = $authState['member'];
|
||||
?>
|
||||
@@ -1,66 +1,38 @@
|
||||
<?php
|
||||
// header('Content-Type: application/json');
|
||||
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||
|
||||
// $management_key = 'P2wON5fy1K6kyia269VpeIzYP8oP:K2ycqpjeh1voPBdxXxzB3ScZOQ6v9aiLmU2cIj70X1H8Kcoz0KWfCWmofUwAsAkJroXA8QC';
|
||||
// $loginId = $_POST['loginId'] ?? '';
|
||||
auth_session_start_if_needed();
|
||||
header('Content-Type: application/json; charset=UTF-8');
|
||||
|
||||
// $url = "https://api.descope.com/v2/mgmt/user/search";
|
||||
// $payload = json_encode([
|
||||
// "loginId" => $loginId,
|
||||
// "limit" => 1
|
||||
// ]);
|
||||
$loginId = trim((string) ($_POST['loginId'] ?? ''));
|
||||
$sessionUser = $_SESSION['user'] ?? [];
|
||||
$sessionLoginId = trim((string) ($sessionUser['loginIds'][0] ?? ''));
|
||||
|
||||
// $headers = [
|
||||
// "Authorization: Bearer $management_key",
|
||||
// "Content-Type: application/json"
|
||||
// ];
|
||||
if ($loginId !== '' && $sessionLoginId !== '' && strcasecmp($loginId, $sessionLoginId) === 0) {
|
||||
echo json_encode(['status' => 'ok', 'user' => auth_normalize_user($sessionUser)]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// $ch = curl_init($url);
|
||||
// curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
// curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
// curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
// $response = curl_exec($ch);
|
||||
// $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
// curl_close($ch);
|
||||
|
||||
// if ($http_code === 200) {
|
||||
// $res = json_decode($response, true);
|
||||
|
||||
// if (!empty($res['users'][0])) {
|
||||
// // ✅ 먼저 $user에 복사
|
||||
// $user = $res['users'][0];
|
||||
|
||||
// // ✅ 값이 없으면 테스트용 기본값 삽입
|
||||
// $user['name'] = $user['name'] ?: '홍길동';
|
||||
// $user['phone'] = $user['phone'] ?: '01012345678';
|
||||
// $user['customAttributes']['company'] = $user['customAttributes']['company'] ?? '한맥ENG';
|
||||
// $user['customAttributes']['team'] = $user['customAttributes']['team'] ?? '설계팀';
|
||||
// $user['customAttributes']['employeeId'] = $user['customAttributes']['employeeId'] ?? 'HM2024';
|
||||
|
||||
// // ✅ 수정된 $user 사용
|
||||
// echo json_encode(['status' => 'ok', 'user' => $user]);
|
||||
// } else {
|
||||
// echo json_encode(['status' => 'fail', 'message' => '사용자를 찾을 수 없음']);
|
||||
// }
|
||||
// } else {
|
||||
// echo json_encode(['status' => 'fail', 'message' => 'Descope API 오류', 'code' => $http_code]);
|
||||
// }
|
||||
?>
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
$cookieUser = auth_baron_user_from_cookies();
|
||||
if ($loginId !== '' && $cookieUser !== null) {
|
||||
$cookieLoginId = trim((string) ($cookieUser['loginIds'][0] ?? ''));
|
||||
if ($cookieLoginId !== '' && strcasecmp($loginId, $cookieLoginId) === 0) {
|
||||
echo json_encode(['status' => 'ok', 'user' => $cookieUser]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$management_key = 'P2wON5fy1K6kyia269VpeIzYP8oP:K32l5ORmzy32OvaaPvpdZsMY3JmKQb7a3vvrl10PgjlJUGk3K7EssMH3uW5VGQSbrgtEdPj';
|
||||
$loginId = $_POST['loginId'] ?? '';
|
||||
|
||||
$url = "https://api.descope.com/v2/mgmt/user/search";
|
||||
$url = 'https://api.descope.com/v2/mgmt/user/search';
|
||||
$payload = json_encode([
|
||||
"loginId" => $loginId,
|
||||
"limit" => 1
|
||||
'loginId' => $loginId,
|
||||
'limit' => 1,
|
||||
]);
|
||||
|
||||
$headers = [
|
||||
"Authorization: Bearer $management_key",
|
||||
"Content-Type: application/json"
|
||||
'Content-Type: application/json',
|
||||
];
|
||||
|
||||
$ch = curl_init($url);
|
||||
@@ -76,18 +48,17 @@ if ($http_code === 200) {
|
||||
|
||||
if (!empty($res['users'][0])) {
|
||||
$user = $res['users'][0];
|
||||
|
||||
// ✅ 실제 값이 있으면 그대로 사용, 없으면 빈 문자열
|
||||
$user['name'] = $user['name'] ?? '';
|
||||
$user['phone'] = $user['phone'] ?? '';
|
||||
$user['customAttributes']['company'] = $user['customAttributes']['company'] ?? '';
|
||||
$user['customAttributes']['familyCompany'] = $user['customAttributes']['familyCompany'] ?? '';
|
||||
$user['customAttributes']['team'] = $user['customAttributes']['team'] ?? '';
|
||||
$user['customAttributes']['employeeId'] = $user['customAttributes']['employeeId'] ?? '';
|
||||
$user['customAttributes']['familyUniqueKey'] = $user['customAttributes']['familyUniqueKey'] ?? '';
|
||||
|
||||
echo json_encode(['status' => 'ok', 'user' => $user]);
|
||||
} else {
|
||||
echo json_encode(['status' => 'fail', 'message' => '사용자를 찾을 수 없음']);
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['status' => 'fail', 'message' => 'Descope API 오류', 'code' => $http_code]);
|
||||
}
|
||||
|
||||
echo json_encode(['status' => 'fail', 'message' => '사용자를 찾을 수 없음', 'code' => $http_code]);
|
||||
|
||||
@@ -355,19 +355,6 @@
|
||||
</body>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
// footer family site toggle
|
||||
$('.family_btn').click(function(e){
|
||||
e.stopPropagation();
|
||||
$('.family_list, .family_btn').toggleClass('family_on');
|
||||
});
|
||||
$(document).click(function(e){
|
||||
if(!$(e.target).closest('.family_wrap').length){
|
||||
$('.family_list, .family_btn').removeClass('family_on');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 회원 탈퇴
|
||||
function member_leave() {
|
||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||
|
||||
@@ -198,19 +198,6 @@
|
||||
</body>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
// footer family site toggle
|
||||
$('.family_btn').click(function(e){
|
||||
e.stopPropagation();
|
||||
$('.family_list, .family_btn').toggleClass('family_on');
|
||||
});
|
||||
$(document).click(function(e){
|
||||
if(!$(e.target).closest('.family_wrap').length){
|
||||
$('.family_list, .family_btn').removeClass('family_on');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 회원 탈퇴
|
||||
function member_leave() {
|
||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||
|
||||
@@ -219,19 +219,6 @@
|
||||
</body>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
// footer family site toggle
|
||||
$('.family_btn').click(function(e){
|
||||
e.stopPropagation();
|
||||
$('.family_list, .family_btn').toggleClass('family_on');
|
||||
});
|
||||
$(document).click(function(e){
|
||||
if(!$(e.target).closest('.family_wrap').length){
|
||||
$('.family_list, .family_btn').removeClass('family_on');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 회원 탈퇴
|
||||
function member_leave() {
|
||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
<script type="text/javascript" src="<?php echo G5_URL ?>/js/jquery-3.6.1.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="<?php echo G5_URL ?>/js/jquery.mousewheel.min.js"></script>
|
||||
<script type="text/javascript" src="<?php echo G5_URL ?>/js/common.js"></script>
|
||||
<script type="text/javascript" src="<?php echo G5_URL ?>/js/include.js"></script>
|
||||
<script type="text/javascript" src="<?php echo G5_URL ?>/js/popup.js?v=20260723"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||
|
||||
<!--popup_wrap-->
|
||||
@@ -57,8 +51,12 @@
|
||||
<div class="join_btn_wrap">
|
||||
<button type="submit" id="btn_login">Login</button>
|
||||
</div>
|
||||
<div class="join_btn_wrap" style="margin-top:10px;">
|
||||
<a href="/auth/baron_login?site=eng&return_url=/eng/bbs/descope_qa_list.php" style="display:block; width:100%; text-align:center; padding:14px 16px; border:1px solid #0f3a2f; border-radius:10px; color:#0f3a2f; font-weight:700;">BARON-SSO Login</a>
|
||||
</div>
|
||||
<p style="margin-top:10px; font-size:16px; font-weight:700; color:#c62828; line-height:1.6; text-align:center;">eg-bim 1.5.1 버전 이상 사용자는 baron-sso로 로그인해야 합니다.</p>
|
||||
<div class="btn_go">
|
||||
<div class="go_signup"><a href="javascript:agreement();"><span>Sign up</span><i class="arrow_r"></i></a></div>
|
||||
<div class="go_signup"><a href="#" class="signup-disabled-link" aria-disabled="true"><span>Sign up</span><i class="arrow_r"></i></a></div>
|
||||
<div class="go_find"><a href="javascript:search();"><span>Reset Password</span><i class="arrow_r"></i></a></div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -132,6 +130,10 @@ $(document).ready(function () {
|
||||
e.preventDefault(); // 기본 제출 막기
|
||||
$('#btn_login').click(); // 버튼 클릭 이벤트 재사용
|
||||
});
|
||||
|
||||
$('.signup-disabled-link').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
@@ -151,19 +151,6 @@ include_once __DIR__ . '/asset_urls.php';
|
||||
</script>
|
||||
</body>
|
||||
<script>
|
||||
$(function(){
|
||||
// footer family site toggle
|
||||
$('.family_btn').click(function(e){
|
||||
e.stopPropagation();
|
||||
$('.family_list, .family_btn').toggleClass('family_on');
|
||||
});
|
||||
$(document).click(function(e){
|
||||
if(!$(e.target).closest('.family_wrap').length){
|
||||
$('.family_list, .family_btn').removeClass('family_on');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 회원 탈퇴
|
||||
function member_leave() {
|
||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||
|
||||
@@ -55,7 +55,7 @@ $download_url = "https://release-download.hmac.kr/eg-bim/{$version}/setup_eg-bim
|
||||
<a href="javascript:descopeLogout();">Logout</a>
|
||||
</li>
|
||||
<li id="my_join" class="my_join">
|
||||
<a href="javascript:agreement();">Sign up</a>
|
||||
<a href="#" class="signup-disabled-link" aria-disabled="true">Sign up</a>
|
||||
</li>
|
||||
<li id="my_login" class="my_login">
|
||||
<a href="javascript:login();">Login</a>
|
||||
@@ -94,9 +94,84 @@ $download_url = "https://release-download.hmac.kr/eg-bim/{$version}/setup_eg-bim
|
||||
// e.preventDefault(); // 링크 이동 막기
|
||||
// alert('다운로드 기능은 준비중입니다.');
|
||||
// });
|
||||
</script>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
function getCookieValue(name) {
|
||||
const prefix = name + '=';
|
||||
const parts = document.cookie.split(';');
|
||||
for (let i = 0; i < parts.length; i += 1) {
|
||||
const part = parts[i].trim();
|
||||
if (part.indexOf(prefix) === 0) {
|
||||
return part.substring(prefix.length);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function decodeBase64Json(value) {
|
||||
if (!value) return null;
|
||||
|
||||
try {
|
||||
const binary = window.atob(value);
|
||||
const bytes = Array.prototype.map.call(binary, function (char) {
|
||||
return '%' + ('00' + char.charCodeAt(0).toString(16)).slice(-2);
|
||||
}).join('');
|
||||
return JSON.parse(decodeURIComponent(bytes));
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function setStoredValue(key, value) {
|
||||
if (typeof value === 'string' && value !== '') {
|
||||
sessionStorage.setItem(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
function restoreClientAuthSessionFromCookies() {
|
||||
const baronUser = decodeBase64Json(getCookieValue('baron_user'));
|
||||
|
||||
if (baronUser && Array.isArray(baronUser.loginIds) && baronUser.loginIds[0]) {
|
||||
const customAttributes = baronUser.customAttributes || {};
|
||||
const roleNames = Array.isArray(baronUser.roleNames) ? baronUser.roleNames : [];
|
||||
|
||||
setStoredValue('authProvider', 'baron');
|
||||
setStoredValue('loginId', baronUser.loginIds[0] || '');
|
||||
setStoredValue('descopeUserId', baronUser.userId || '');
|
||||
setStoredValue('userName', baronUser.name || '');
|
||||
setStoredValue('phone', baronUser.phone || '');
|
||||
setStoredValue('company', customAttributes.company || '');
|
||||
setStoredValue('familyCompany', customAttributes.familyCompany || '');
|
||||
setStoredValue('team', customAttributes.team || '');
|
||||
setStoredValue('position', customAttributes.position || '');
|
||||
setStoredValue('familyUniqueKey', customAttributes.familyUniqueKey || '');
|
||||
setStoredValue('userRole', roleNames[0] || '');
|
||||
setStoredValue('sessionJwt', sessionStorage.getItem('sessionJwt') || 'baron.session.marker');
|
||||
return;
|
||||
}
|
||||
|
||||
const descopeLoginId = getCookieValue('descope_login_id');
|
||||
if (descopeLoginId && !sessionStorage.getItem('loginId')) {
|
||||
const customAttributes = decodeBase64Json(getCookieValue('descope_custom_attributes')) || {};
|
||||
const roleNames = decodeBase64Json(getCookieValue('descope_role_names')) || [];
|
||||
|
||||
setStoredValue('authProvider', sessionStorage.getItem('authProvider') || 'descope');
|
||||
setStoredValue('loginId', descopeLoginId);
|
||||
setStoredValue('descopeUserId', getCookieValue('descope_user_id'));
|
||||
setStoredValue('userName', getCookieValue('descope_user_name'));
|
||||
setStoredValue('phone', getCookieValue('descope_user_phone'));
|
||||
setStoredValue('company', customAttributes.company || '');
|
||||
setStoredValue('familyCompany', customAttributes.familyCompany || '');
|
||||
setStoredValue('team', customAttributes.team || '');
|
||||
setStoredValue('position', customAttributes.position || '');
|
||||
setStoredValue('familyUniqueKey', customAttributes.familyUniqueKey || '');
|
||||
setStoredValue('userRole', Array.isArray(roleNames) ? (roleNames[0] || '') : '');
|
||||
}
|
||||
}
|
||||
|
||||
restoreClientAuthSessionFromCookies();
|
||||
})();
|
||||
|
||||
(function () {
|
||||
const ADMIN_ICON_IDS = [
|
||||
'b24014@hanmaceng.co.kr',
|
||||
@@ -179,6 +254,10 @@ $download_url = "https://release-download.hmac.kr/eg-bim/{$version}/setup_eg-bim
|
||||
}, 50);
|
||||
});
|
||||
|
||||
$(document).on('click', '.signup-disabled-link', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// 혹시 로그인 팝업을 다른 곳에서 열어도 배너가 필요하면, 팝업 열릴 때 한 번 더 시도
|
||||
$(document).on('show-login-popup', function () {
|
||||
injectBanner();
|
||||
|
||||
@@ -312,19 +312,6 @@
|
||||
</body>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
// footer family site toggle
|
||||
$('.family_btn').click(function(e){
|
||||
e.stopPropagation();
|
||||
$('.family_list, .family_btn').toggleClass('family_on');
|
||||
});
|
||||
$(document).click(function(e){
|
||||
if(!$(e.target).closest('.family_wrap').length){
|
||||
$('.family_list, .family_btn').removeClass('family_on');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 회원 탈퇴
|
||||
function member_leave() {
|
||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||
|
||||
@@ -17,9 +17,6 @@ if (!isset($register_action_url)) {
|
||||
<link rel="stylesheet" href="../css/style.css" />
|
||||
<script src="../js/jquery-3.6.1.min.js"></script> -->
|
||||
<!-- </head> -->
|
||||
<script src="<?php echo G5_URL ?>/js/common.js"></script>
|
||||
<script src="<?php echo G5_URL ?>/js/popup.js?v=20260723"></script>
|
||||
|
||||
<!-- 회원가입약관 동의 시작 { -->
|
||||
<!-- <body>
|
||||
<div class="register"> -->
|
||||
|
||||
@@ -256,19 +256,6 @@
|
||||
</body>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
// footer family site toggle
|
||||
$('.family_btn').click(function(e){
|
||||
e.stopPropagation();
|
||||
$('.family_list, .family_btn').toggleClass('family_on');
|
||||
});
|
||||
$(document).click(function(e){
|
||||
if(!$(e.target).closest('.family_wrap').length){
|
||||
$('.family_list, .family_btn').removeClass('family_on');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 회원 탈퇴
|
||||
function member_leave() {
|
||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||
|
||||
@@ -556,19 +556,6 @@ foreach ($partials as $p) {
|
||||
</html>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
// footer family site toggle
|
||||
$('.family_btn').click(function(e){
|
||||
e.stopPropagation();
|
||||
$('.family_list, .family_btn').toggleClass('family_on');
|
||||
});
|
||||
$(document).click(function(e){
|
||||
if(!$(e.target).closest('.family_wrap').length){
|
||||
$('.family_list, .family_btn').removeClass('family_on');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 회원 탈퇴
|
||||
function member_leave() {
|
||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||
|
||||
@@ -307,19 +307,6 @@ function getQueryString(name){
|
||||
if (writer) writer.addEventListener('change', submitForm);
|
||||
})();
|
||||
|
||||
// Footer family site toggle
|
||||
$(function(){
|
||||
$('.family_btn').on('click', function(e){
|
||||
e.stopPropagation();
|
||||
$('.family_list, .family_btn').toggleClass('family_on');
|
||||
});
|
||||
$(document).on('click', function(e){
|
||||
if(!$(e.target).closest('.family_wrap').length){
|
||||
$('.family_list, .family_btn').removeClass('family_on');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Cookie helpers (기존 로직 유지)
|
||||
function deleteCookie(name){
|
||||
document.cookie = name+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/eng;";
|
||||
@@ -437,7 +424,7 @@ function updateHeaderMenu(){
|
||||
$('#my_join2').html(
|
||||
isLoggedIn && isAdmin
|
||||
? "<a href='/eng/adm/index.php' target='_blank'>관리페이지</a>"
|
||||
: "<a href='/eng/index.php'>Home</a>"
|
||||
: "<a href='javascript:mypage03();'>My Page</a>"
|
||||
);
|
||||
} catch(e){ console.error(e); }
|
||||
}
|
||||
|
||||
@@ -430,7 +430,7 @@ function updateHeaderMenu(){
|
||||
$('#my_join2').html(
|
||||
isLoggedIn && isAdmin
|
||||
? "<a href='/eng/adm/index.php' target='_blank'>관리페이지</a>"
|
||||
: "<a href='/eng/index.php'>Home</a>"
|
||||
: "<a href='javascript:mypage03();'>My Page</a>"
|
||||
);
|
||||
} catch(e){ console.error(e); }
|
||||
}
|
||||
|
||||
@@ -363,19 +363,6 @@
|
||||
|
||||
<!-- 상단(헤더) 스크립트 구문 -->
|
||||
<script>
|
||||
$(function(){
|
||||
// footer family site toggle
|
||||
$('.family_btn').click(function(e){
|
||||
e.stopPropagation();
|
||||
$('.family_list, .family_btn').toggleClass('family_on');
|
||||
});
|
||||
$(document).click(function(e){
|
||||
if(!$(e.target).closest('.family_wrap').length){
|
||||
$('.family_list, .family_btn').removeClass('family_on');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 회원 탈퇴
|
||||
function member_leave() {
|
||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||
|
||||
@@ -38,6 +38,14 @@ if [[ -f .env ]]; then
|
||||
set +a
|
||||
fi
|
||||
|
||||
mkdir -p \
|
||||
egbim/data/cache \
|
||||
egbim/data/session \
|
||||
egbim/data/log \
|
||||
eng/data/cache \
|
||||
eng/data/session \
|
||||
eng/data/log
|
||||
|
||||
mkdir -p docker/mysql/init
|
||||
|
||||
docker compose up -d --build
|
||||
|
||||
Reference in New Issue
Block a user