BARON-SSO 로그인 연동
This commit is contained in:
@@ -5,3 +5,14 @@ G5_MYSQL_USER=egbim
|
|||||||
G5_MYSQL_PASSWORD=baron3840!!
|
G5_MYSQL_PASSWORD=baron3840!!
|
||||||
MARIADB_ROOT_PASSWORD=root_local_pass
|
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 }}
|
G5_MYSQL_PASSWORD=${{ secrets.G5_MYSQL_PASSWORD }}
|
||||||
MARIADB_ROOT_PASSWORD=${{ secrets.MARIADB_ROOT_PASSWORD }}
|
MARIADB_ROOT_PASSWORD=${{ secrets.MARIADB_ROOT_PASSWORD }}
|
||||||
COMPOSE_PROJECT_NAME=${{ secrets.COMPOSE_PROJECT_NAME }}
|
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
|
EOF
|
||||||
|
|
||||||
- name: Upload deployment script
|
- name: Upload deployment script
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
<IfModule mod_rewrite.c>
|
<IfModule mod_rewrite.c>
|
||||||
RewriteEngine On
|
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
|
# local docker/localhost bypass
|
||||||
RewriteCond %{ENV:APP_ENV} =local [OR]
|
RewriteCond %{ENV:APP_ENV} =local [OR]
|
||||||
RewriteCond %{HTTP_HOST} ^(localhost|127\.0\.0\.1)(:[0-9]+)?$ [NC]
|
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 비밀번호
|
- `G5_MYSQL_PASSWORD`: 운영 DB 비밀번호
|
||||||
- `MARIADB_ROOT_PASSWORD`: 운영 MariaDB root 비밀번호
|
- `MARIADB_ROOT_PASSWORD`: 운영 MariaDB root 비밀번호
|
||||||
- `COMPOSE_PROJECT_NAME`: 예) `egbim`
|
- `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` 파일을 생성한 뒤 배포를 수행한다.
|
이 워크플로는 위 시크릿으로 서버의 `.env` 파일을 생성한 뒤 배포를 수행한다.
|
||||||
|
|
||||||
@@ -169,6 +178,15 @@ G5_MYSQL_USER=egbim
|
|||||||
G5_MYSQL_PASSWORD=<set-on-server>
|
G5_MYSQL_PASSWORD=<set-on-server>
|
||||||
MARIADB_ROOT_PASSWORD=<set-on-server>
|
MARIADB_ROOT_PASSWORD=<set-on-server>
|
||||||
COMPOSE_PROJECT_NAME=egbim
|
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 덤프 포함 주의
|
## 5-1. SQL 덤프 포함 주의
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
2. 필요하면 `.env.docker.example` 내용을 참고해 `.env` 파일을 만든다.
|
2. 필요하면 `.env.docker.example` 내용을 참고해 `.env` 파일을 만든다.
|
||||||
3. DB 덤프가 있으면 `docker/mysql/init/` 아래에 `.sql` 파일로 넣는다.
|
3. DB 덤프가 있으면 `docker/mysql/init/` 아래에 `.sql` 파일로 넣는다.
|
||||||
4. 기본 로컬 DB명은 운영과 맞춘 `egbim` 이다.
|
4. 기본 로컬 DB명은 운영과 맞춘 `egbim` 이다.
|
||||||
|
5. BARON-SSO 를 로컬에서 테스트하려면 BARON 앱 설정에 로컬 Redirect URI 를 추가해야 한다.
|
||||||
|
|
||||||
## 실행
|
## 실행
|
||||||
|
|
||||||
@@ -20,6 +21,57 @@
|
|||||||
docker compose up --build
|
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/`
|
- 기본: `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_USER: ${G5_MYSQL_USER:-egbim}
|
||||||
G5_MYSQL_PASSWORD: ${G5_MYSQL_PASSWORD:-baron3840!!}
|
G5_MYSQL_PASSWORD: ${G5_MYSQL_PASSWORD:-baron3840!!}
|
||||||
G5_MYSQL_DB: ${G5_MYSQL_DB:-egbim}
|
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:
|
ports:
|
||||||
- "${APP_PORT:-8088}:80"
|
- "${APP_PORT:-8088}:80"
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -1,31 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
// /egbim/bbs/admin_guard.php
|
// /egbim/bbs/admin_guard.php
|
||||||
if (session_status() !== PHP_SESSION_ACTIVE) @session_start();
|
if (session_status() !== PHP_SESSION_ACTIVE) @session_start();
|
||||||
|
require_once dirname(__DIR__, 2) . '/auth/common.php';
|
||||||
|
|
||||||
if (!function_exists('is_qna_admin')) {
|
if (!function_exists('is_qna_admin')) {
|
||||||
function is_qna_admin(): bool {
|
function is_qna_admin(?string $loginId = null): bool {
|
||||||
$loginId = $_SESSION['user']['loginIds'][0] ?? '';
|
$user = $_SESSION['user'] ?? [];
|
||||||
$allow = [
|
return is_array($user) && auth_is_admin_user($user, $loginId);
|
||||||
'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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -5885,7 +5885,7 @@ footer .address em.tel {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
.login form {
|
.login form {
|
||||||
gap: 24px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
.login .input_wrap {
|
.login .input_wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -1,25 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||||
|
|
||||||
function set_descope_session_cookies(array $user): void {
|
auth_session_start_if_needed();
|
||||||
$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);
|
|
||||||
}
|
|
||||||
|
|
||||||
$email = trim($_POST['mb_id'] ?? '');
|
$email = trim($_POST['mb_id'] ?? '');
|
||||||
$password = trim($_POST['mb_password'] ?? '');
|
$password = trim($_POST['mb_password'] ?? '');
|
||||||
@@ -60,16 +42,24 @@ if ($http_code === 200 && isset($res_data['sessionJwt'])) {
|
|||||||
// 로그인 성공 → 실패횟수 초기화
|
// 로그인 성공 → 실패횟수 초기화
|
||||||
$_SESSION['login_fail_count'] = 0;
|
$_SESSION['login_fail_count'] = 0;
|
||||||
//session으로도 저장
|
//session으로도 저장
|
||||||
$_SESSION['user'] = $res_data['user']; // userId, email 등
|
$normalizedUser = auth_apply_user_session(
|
||||||
$_SESSION['sessionJwt'] = $res_data['sessionJwt'];
|
$res_data['user'] ?? [],
|
||||||
$_SESSION['refreshJwt'] = $res_data['refreshJwt'];
|
'descope',
|
||||||
set_descope_session_cookies($res_data['user'] ?? []);
|
[
|
||||||
|
'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([
|
echo json_encode([
|
||||||
'status' => 'ok',
|
'status' => 'ok',
|
||||||
'sessionJwt' => $res_data['sessionJwt'] ?? '',
|
'sessionJwt' => $res_data['sessionJwt'] ?? '',
|
||||||
// 'refreshJwt' => $res_data['refreshJwt'] ?? '',
|
// 'refreshJwt' => $res_data['refreshJwt'] ?? '',
|
||||||
'user' => $res_data['user'] ?? [],
|
'user' => $normalizedUser,
|
||||||
'session' => $_SESSION,
|
'session' => $_SESSION,
|
||||||
]);
|
]);
|
||||||
// } else {
|
// } else {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||||
// descope_logout.php
|
// descope_logout.php
|
||||||
// session_start();
|
// session_start();
|
||||||
// $_SESSION = [];
|
// $_SESSION = [];
|
||||||
@@ -16,17 +17,9 @@
|
|||||||
// http_response_code(200);
|
// http_response_code(200);
|
||||||
// echo json_encode(['status'=>'ok']);
|
// echo json_encode(['status'=>'ok']);
|
||||||
// exit;
|
// exit;
|
||||||
session_start();
|
auth_session_start_if_needed();
|
||||||
$_SESSION = [];
|
$_SESSION = [];
|
||||||
|
auth_clear_all_cookies();
|
||||||
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',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
session_destroy();
|
session_destroy();
|
||||||
|
|
||||||
http_response_code(200);
|
http_response_code(200);
|
||||||
|
|||||||
@@ -1,148 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
// // descope_session.php
|
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||||
|
|
||||||
// // 1) 에러 출력 (개발용)
|
$authState = auth_sync_gnuboard_session();
|
||||||
// // ini_set('display_errors', 1);
|
|
||||||
// // error_reporting(E_ALL);
|
|
||||||
|
|
||||||
// // 2) 세션 시작 (중복 호출 방지)
|
$loginId = $authState['loginId'];
|
||||||
// if (session_status() === PHP_SESSION_NONE) {
|
$isAdmin = $authState['isAdmin'];
|
||||||
// session_start();
|
$is_member = $authState['is_member'];
|
||||||
// }
|
$is_admin = $authState['is_admin'];
|
||||||
|
$member = $authState['member'];
|
||||||
// // 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 = [];
|
|
||||||
}
|
|
||||||
?>
|
?>
|
||||||
@@ -1,66 +1,38 @@
|
|||||||
<?php
|
<?php
|
||||||
// header('Content-Type: application/json');
|
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||||
|
|
||||||
// $management_key = 'P2wON5fy1K6kyia269VpeIzYP8oP:K2ycqpjeh1voPBdxXxzB3ScZOQ6v9aiLmU2cIj70X1H8Kcoz0KWfCWmofUwAsAkJroXA8QC';
|
auth_session_start_if_needed();
|
||||||
// $loginId = $_POST['loginId'] ?? '';
|
header('Content-Type: application/json; charset=UTF-8');
|
||||||
|
|
||||||
// $url = "https://api.descope.com/v2/mgmt/user/search";
|
$loginId = trim((string) ($_POST['loginId'] ?? ''));
|
||||||
// $payload = json_encode([
|
$sessionUser = $_SESSION['user'] ?? [];
|
||||||
// "loginId" => $loginId,
|
$sessionLoginId = trim((string) ($sessionUser['loginIds'][0] ?? ''));
|
||||||
// "limit" => 1
|
|
||||||
// ]);
|
|
||||||
|
|
||||||
// $headers = [
|
if ($loginId !== '' && $sessionLoginId !== '' && strcasecmp($loginId, $sessionLoginId) === 0) {
|
||||||
// "Authorization: Bearer $management_key",
|
echo json_encode(['status' => 'ok', 'user' => auth_normalize_user($sessionUser)]);
|
||||||
// "Content-Type: application/json"
|
exit;
|
||||||
// ];
|
}
|
||||||
|
|
||||||
// $ch = curl_init($url);
|
$cookieUser = auth_baron_user_from_cookies();
|
||||||
// curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
if ($loginId !== '' && $cookieUser !== null) {
|
||||||
// curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
$cookieLoginId = trim((string) ($cookieUser['loginIds'][0] ?? ''));
|
||||||
// curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
if ($cookieLoginId !== '' && strcasecmp($loginId, $cookieLoginId) === 0) {
|
||||||
// $response = curl_exec($ch);
|
echo json_encode(['status' => 'ok', 'user' => $cookieUser]);
|
||||||
// $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
exit;
|
||||||
// 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');
|
|
||||||
|
|
||||||
$management_key = 'P2wON5fy1K6kyia269VpeIzYP8oP:K32l5ORmzy32OvaaPvpdZsMY3JmKQb7a3vvrl10PgjlJUGk3K7EssMH3uW5VGQSbrgtEdPj';
|
$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([
|
$payload = json_encode([
|
||||||
"loginId" => $loginId,
|
'loginId' => $loginId,
|
||||||
"limit" => 1
|
'limit' => 1,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$headers = [
|
$headers = [
|
||||||
"Authorization: Bearer $management_key",
|
"Authorization: Bearer $management_key",
|
||||||
"Content-Type: application/json"
|
'Content-Type: application/json',
|
||||||
];
|
];
|
||||||
|
|
||||||
$ch = curl_init($url);
|
$ch = curl_init($url);
|
||||||
@@ -76,18 +48,17 @@ if ($http_code === 200) {
|
|||||||
|
|
||||||
if (!empty($res['users'][0])) {
|
if (!empty($res['users'][0])) {
|
||||||
$user = $res['users'][0];
|
$user = $res['users'][0];
|
||||||
|
|
||||||
// ✅ 실제 값이 있으면 그대로 사용, 없으면 빈 문자열
|
|
||||||
$user['name'] = $user['name'] ?? '';
|
$user['name'] = $user['name'] ?? '';
|
||||||
$user['phone'] = $user['phone'] ?? '';
|
$user['phone'] = $user['phone'] ?? '';
|
||||||
$user['customAttributes']['company'] = $user['customAttributes']['company'] ?? '';
|
$user['customAttributes']['company'] = $user['customAttributes']['company'] ?? '';
|
||||||
|
$user['customAttributes']['familyCompany'] = $user['customAttributes']['familyCompany'] ?? '';
|
||||||
$user['customAttributes']['team'] = $user['customAttributes']['team'] ?? '';
|
$user['customAttributes']['team'] = $user['customAttributes']['team'] ?? '';
|
||||||
$user['customAttributes']['employeeId'] = $user['customAttributes']['employeeId'] ?? '';
|
$user['customAttributes']['employeeId'] = $user['customAttributes']['employeeId'] ?? '';
|
||||||
|
$user['customAttributes']['familyUniqueKey'] = $user['customAttributes']['familyUniqueKey'] ?? '';
|
||||||
|
|
||||||
echo json_encode(['status' => 'ok', 'user' => $user]);
|
echo json_encode(['status' => 'ok', 'user' => $user]);
|
||||||
} else {
|
exit;
|
||||||
echo json_encode(['status' => 'fail', 'message' => '사용자를 찾을 수 없음']);
|
|
||||||
}
|
}
|
||||||
} 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">
|
<div class="join_btn_wrap">
|
||||||
<button type="submit" id="btn_login">로그인</button>
|
<button type="submit" id="btn_login">로그인</button>
|
||||||
</div>
|
</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="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 class="go_find"><a href="javascript:search();"><span>비밀번호 재설정</span><i class="arrow_r"></i></a></div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -127,6 +131,10 @@ $(document).ready(function () {
|
|||||||
e.preventDefault(); // 기본 제출 막기
|
e.preventDefault(); // 기본 제출 막기
|
||||||
$('#btn_login').click(); // 버튼 클릭 이벤트 재사용
|
$('#btn_login').click(); // 버튼 클릭 이벤트 재사용
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.signup-disabled-link').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
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>
|
<a href="javascript:descopeLogout();">로그아웃</a>
|
||||||
</li>
|
</li>
|
||||||
<li id="my_join" class="my_join">
|
<li id="my_join" class="my_join">
|
||||||
<a href="javascript:agreement();">회원가입</a>
|
<a href="#" class="signup-disabled-link" aria-disabled="true">회원가입</a>
|
||||||
</li>
|
</li>
|
||||||
<li id="my_login" class="my_login">
|
<li id="my_login" class="my_login">
|
||||||
<a href="javascript:login();">로그인</a>
|
<a href="javascript:login();">로그인</a>
|
||||||
@@ -94,9 +94,84 @@ $download_url = "https://release-download.hmac.kr/eg-bim/{$version}/setup_eg-bim
|
|||||||
// e.preventDefault(); // 링크 이동 막기
|
// e.preventDefault(); // 링크 이동 막기
|
||||||
// alert('다운로드 기능은 준비중입니다.');
|
// 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 () {
|
(function () {
|
||||||
const ADMIN_ICON_IDS = [
|
const ADMIN_ICON_IDS = [
|
||||||
'b24014@hanmaceng.co.kr',
|
'b24014@hanmaceng.co.kr',
|
||||||
@@ -181,6 +256,10 @@ $download_url = "https://release-download.hmac.kr/eg-bim/{$version}/setup_eg-bim
|
|||||||
}, 50);
|
}, 50);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.signup-disabled-link', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
// 혹시 로그인 팝업을 다른 곳에서 열어도 배너가 필요하면, 팝업 열릴 때 한 번 더 시도
|
// 혹시 로그인 팝업을 다른 곳에서 열어도 배너가 필요하면, 팝업 열릴 때 한 번 더 시도
|
||||||
$(document).on('show-login-popup', function () {
|
$(document).on('show-login-popup', function () {
|
||||||
injectBanner();
|
injectBanner();
|
||||||
|
|||||||
@@ -438,7 +438,7 @@ function updateHeaderMenu(){
|
|||||||
$('#my_join2').html(
|
$('#my_join2').html(
|
||||||
isLoggedIn && isAdmin
|
isLoggedIn && isAdmin
|
||||||
? "<a href='/egbim/adm/index.php' target='_blank'>관리페이지</a>"
|
? "<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); }
|
} catch(e){ console.error(e); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ function updateHeaderMenu(){
|
|||||||
$('#my_join2').html(
|
$('#my_join2').html(
|
||||||
isLoggedIn && isAdmin
|
isLoggedIn && isAdmin
|
||||||
? "<a href='/egbim/adm/index.php' target='_blank'>관리페이지</a>"
|
? "<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); }
|
} catch(e){ console.error(e); }
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-16
@@ -1,24 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
// /egbim/bbs/admin_guard.php
|
// /egbim/bbs/admin_guard.php
|
||||||
if (session_status() !== PHP_SESSION_ACTIVE) @session_start();
|
if (session_status() !== PHP_SESSION_ACTIVE) @session_start();
|
||||||
|
require_once dirname(__DIR__, 2) . '/auth/common.php';
|
||||||
|
|
||||||
if (!function_exists('is_qna_admin')) {
|
if (!function_exists('is_qna_admin')) {
|
||||||
function is_qna_admin(): bool {
|
function is_qna_admin(?string $loginId = null): bool {
|
||||||
$loginId = $_SESSION['user']['loginIds'][0] ?? '';
|
$user = $_SESSION['user'] ?? [];
|
||||||
$allow = [
|
return is_array($user) && auth_is_admin_user($user, $loginId);
|
||||||
'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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+34
-19
@@ -1185,27 +1185,42 @@ $(function () {
|
|||||||
$(".menu_my").mouseout(function () {
|
$(".menu_my").mouseout(function () {
|
||||||
$(".menu_my_list").hide();
|
$(".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
|
// 사용 클래스 : js__mouse_mark , js__mouse_area
|
||||||
// + TODO 진슬 추가_ addEventListener error debugging
|
// + TODO 진슬 추가_ addEventListener error debugging
|
||||||
|
|||||||
+12
-1
@@ -660,8 +660,19 @@ function html_purifier($html)
|
|||||||
include_once(G5_PLUGIN_PATH . '/htmlpurifier/extend.video.php');
|
include_once(G5_PLUGIN_PATH . '/htmlpurifier/extend.video.php');
|
||||||
|
|
||||||
$config = HTMLPurifier_Config::createDefault();
|
$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 디렉토리 등을 만든다.
|
// 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.SafeEmbed', false);
|
||||||
$config->set('HTML.SafeObject', false);
|
$config->set('HTML.SafeObject', false);
|
||||||
$config->set('Output.FlashCompat', false);
|
$config->set('Output.FlashCompat', false);
|
||||||
|
|||||||
@@ -268,19 +268,6 @@ echo '<div id="faq_hhtml">'.conv_content($fm['fm_head_html'], 1).'</div>';
|
|||||||
|
|
||||||
<!-- 상단(헤더) 스크립트 구문 -->
|
<!-- 상단(헤더) 스크립트 구문 -->
|
||||||
<script>
|
<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() {
|
function member_leave() {
|
||||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||||
|
|||||||
@@ -112,19 +112,6 @@
|
|||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
<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() {
|
function member_leave() {
|
||||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||||
|
|||||||
@@ -1,25 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||||
|
|
||||||
function set_descope_session_cookies(array $user): void {
|
auth_session_start_if_needed();
|
||||||
$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);
|
|
||||||
}
|
|
||||||
|
|
||||||
$email = trim($_POST['mb_id'] ?? '');
|
$email = trim($_POST['mb_id'] ?? '');
|
||||||
$password = trim($_POST['mb_password'] ?? '');
|
$password = trim($_POST['mb_password'] ?? '');
|
||||||
@@ -60,16 +42,24 @@ if ($http_code === 200 && isset($res_data['sessionJwt'])) {
|
|||||||
// 로그인 성공 → 실패횟수 초기화
|
// 로그인 성공 → 실패횟수 초기화
|
||||||
$_SESSION['login_fail_count'] = 0;
|
$_SESSION['login_fail_count'] = 0;
|
||||||
//session으로도 저장
|
//session으로도 저장
|
||||||
$_SESSION['user'] = $res_data['user']; // userId, email 등
|
$normalizedUser = auth_apply_user_session(
|
||||||
$_SESSION['sessionJwt'] = $res_data['sessionJwt'];
|
$res_data['user'] ?? [],
|
||||||
$_SESSION['refreshJwt'] = $res_data['refreshJwt'];
|
'descope',
|
||||||
set_descope_session_cookies($res_data['user'] ?? []);
|
[
|
||||||
|
'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([
|
echo json_encode([
|
||||||
'status' => 'ok',
|
'status' => 'ok',
|
||||||
'sessionJwt' => $res_data['sessionJwt'] ?? '',
|
'sessionJwt' => $res_data['sessionJwt'] ?? '',
|
||||||
// 'refreshJwt' => $res_data['refreshJwt'] ?? '',
|
// 'refreshJwt' => $res_data['refreshJwt'] ?? '',
|
||||||
'user' => $res_data['user'] ?? [],
|
'user' => $normalizedUser,
|
||||||
'session' => $_SESSION,
|
'session' => $_SESSION,
|
||||||
]);
|
]);
|
||||||
// } else {
|
// } else {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||||
// descope_logout.php
|
// descope_logout.php
|
||||||
// session_start();
|
// session_start();
|
||||||
// $_SESSION = [];
|
// $_SESSION = [];
|
||||||
@@ -16,17 +17,9 @@
|
|||||||
// http_response_code(200);
|
// http_response_code(200);
|
||||||
// echo json_encode(['status'=>'ok']);
|
// echo json_encode(['status'=>'ok']);
|
||||||
// exit;
|
// exit;
|
||||||
session_start();
|
auth_session_start_if_needed();
|
||||||
$_SESSION = [];
|
$_SESSION = [];
|
||||||
|
auth_clear_all_cookies();
|
||||||
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',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
session_destroy();
|
session_destroy();
|
||||||
|
|
||||||
http_response_code(200);
|
http_response_code(200);
|
||||||
|
|||||||
@@ -1,148 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
// // descope_session.php
|
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||||
|
|
||||||
// // 1) 에러 출력 (개발용)
|
$authState = auth_sync_gnuboard_session();
|
||||||
// // ini_set('display_errors', 1);
|
|
||||||
// // error_reporting(E_ALL);
|
|
||||||
|
|
||||||
// // 2) 세션 시작 (중복 호출 방지)
|
$loginId = $authState['loginId'];
|
||||||
// if (session_status() === PHP_SESSION_NONE) {
|
$isAdmin = $authState['isAdmin'];
|
||||||
// session_start();
|
$is_member = $authState['is_member'];
|
||||||
// }
|
$is_admin = $authState['is_admin'];
|
||||||
|
$member = $authState['member'];
|
||||||
// // 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 = [];
|
|
||||||
}
|
|
||||||
?>
|
?>
|
||||||
@@ -1,66 +1,38 @@
|
|||||||
<?php
|
<?php
|
||||||
// header('Content-Type: application/json');
|
require_once dirname(__DIR__, 4) . '/auth/common.php';
|
||||||
|
|
||||||
// $management_key = 'P2wON5fy1K6kyia269VpeIzYP8oP:K2ycqpjeh1voPBdxXxzB3ScZOQ6v9aiLmU2cIj70X1H8Kcoz0KWfCWmofUwAsAkJroXA8QC';
|
auth_session_start_if_needed();
|
||||||
// $loginId = $_POST['loginId'] ?? '';
|
header('Content-Type: application/json; charset=UTF-8');
|
||||||
|
|
||||||
// $url = "https://api.descope.com/v2/mgmt/user/search";
|
$loginId = trim((string) ($_POST['loginId'] ?? ''));
|
||||||
// $payload = json_encode([
|
$sessionUser = $_SESSION['user'] ?? [];
|
||||||
// "loginId" => $loginId,
|
$sessionLoginId = trim((string) ($sessionUser['loginIds'][0] ?? ''));
|
||||||
// "limit" => 1
|
|
||||||
// ]);
|
|
||||||
|
|
||||||
// $headers = [
|
if ($loginId !== '' && $sessionLoginId !== '' && strcasecmp($loginId, $sessionLoginId) === 0) {
|
||||||
// "Authorization: Bearer $management_key",
|
echo json_encode(['status' => 'ok', 'user' => auth_normalize_user($sessionUser)]);
|
||||||
// "Content-Type: application/json"
|
exit;
|
||||||
// ];
|
}
|
||||||
|
|
||||||
// $ch = curl_init($url);
|
$cookieUser = auth_baron_user_from_cookies();
|
||||||
// curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
if ($loginId !== '' && $cookieUser !== null) {
|
||||||
// curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
$cookieLoginId = trim((string) ($cookieUser['loginIds'][0] ?? ''));
|
||||||
// curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
if ($cookieLoginId !== '' && strcasecmp($loginId, $cookieLoginId) === 0) {
|
||||||
// $response = curl_exec($ch);
|
echo json_encode(['status' => 'ok', 'user' => $cookieUser]);
|
||||||
// $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
exit;
|
||||||
// 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');
|
|
||||||
|
|
||||||
$management_key = 'P2wON5fy1K6kyia269VpeIzYP8oP:K32l5ORmzy32OvaaPvpdZsMY3JmKQb7a3vvrl10PgjlJUGk3K7EssMH3uW5VGQSbrgtEdPj';
|
$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([
|
$payload = json_encode([
|
||||||
"loginId" => $loginId,
|
'loginId' => $loginId,
|
||||||
"limit" => 1
|
'limit' => 1,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$headers = [
|
$headers = [
|
||||||
"Authorization: Bearer $management_key",
|
"Authorization: Bearer $management_key",
|
||||||
"Content-Type: application/json"
|
'Content-Type: application/json',
|
||||||
];
|
];
|
||||||
|
|
||||||
$ch = curl_init($url);
|
$ch = curl_init($url);
|
||||||
@@ -76,18 +48,17 @@ if ($http_code === 200) {
|
|||||||
|
|
||||||
if (!empty($res['users'][0])) {
|
if (!empty($res['users'][0])) {
|
||||||
$user = $res['users'][0];
|
$user = $res['users'][0];
|
||||||
|
|
||||||
// ✅ 실제 값이 있으면 그대로 사용, 없으면 빈 문자열
|
|
||||||
$user['name'] = $user['name'] ?? '';
|
$user['name'] = $user['name'] ?? '';
|
||||||
$user['phone'] = $user['phone'] ?? '';
|
$user['phone'] = $user['phone'] ?? '';
|
||||||
$user['customAttributes']['company'] = $user['customAttributes']['company'] ?? '';
|
$user['customAttributes']['company'] = $user['customAttributes']['company'] ?? '';
|
||||||
|
$user['customAttributes']['familyCompany'] = $user['customAttributes']['familyCompany'] ?? '';
|
||||||
$user['customAttributes']['team'] = $user['customAttributes']['team'] ?? '';
|
$user['customAttributes']['team'] = $user['customAttributes']['team'] ?? '';
|
||||||
$user['customAttributes']['employeeId'] = $user['customAttributes']['employeeId'] ?? '';
|
$user['customAttributes']['employeeId'] = $user['customAttributes']['employeeId'] ?? '';
|
||||||
|
$user['customAttributes']['familyUniqueKey'] = $user['customAttributes']['familyUniqueKey'] ?? '';
|
||||||
|
|
||||||
echo json_encode(['status' => 'ok', 'user' => $user]);
|
echo json_encode(['status' => 'ok', 'user' => $user]);
|
||||||
} else {
|
exit;
|
||||||
echo json_encode(['status' => 'fail', 'message' => '사용자를 찾을 수 없음']);
|
|
||||||
}
|
}
|
||||||
} 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>
|
</body>
|
||||||
|
|
||||||
<script>
|
<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() {
|
function member_leave() {
|
||||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||||
|
|||||||
@@ -198,19 +198,6 @@
|
|||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
<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() {
|
function member_leave() {
|
||||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||||
|
|||||||
@@ -219,19 +219,6 @@
|
|||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
<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() {
|
function member_leave() {
|
||||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
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">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
|
||||||
|
|
||||||
<!--popup_wrap-->
|
<!--popup_wrap-->
|
||||||
@@ -57,8 +51,12 @@
|
|||||||
<div class="join_btn_wrap">
|
<div class="join_btn_wrap">
|
||||||
<button type="submit" id="btn_login">Login</button>
|
<button type="submit" id="btn_login">Login</button>
|
||||||
</div>
|
</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="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 class="go_find"><a href="javascript:search();"><span>Reset Password</span><i class="arrow_r"></i></a></div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -132,6 +130,10 @@ $(document).ready(function () {
|
|||||||
e.preventDefault(); // 기본 제출 막기
|
e.preventDefault(); // 기본 제출 막기
|
||||||
$('#btn_login').click(); // 버튼 클릭 이벤트 재사용
|
$('#btn_login').click(); // 버튼 클릭 이벤트 재사용
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.signup-disabled-link').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
|||||||
@@ -151,19 +151,6 @@ include_once __DIR__ . '/asset_urls.php';
|
|||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
<script>
|
<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() {
|
function member_leave() {
|
||||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
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>
|
<a href="javascript:descopeLogout();">Logout</a>
|
||||||
</li>
|
</li>
|
||||||
<li id="my_join" class="my_join">
|
<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>
|
||||||
<li id="my_login" class="my_login">
|
<li id="my_login" class="my_login">
|
||||||
<a href="javascript:login();">Login</a>
|
<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(); // 링크 이동 막기
|
// e.preventDefault(); // 링크 이동 막기
|
||||||
// alert('다운로드 기능은 준비중입니다.');
|
// 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 () {
|
(function () {
|
||||||
const ADMIN_ICON_IDS = [
|
const ADMIN_ICON_IDS = [
|
||||||
'b24014@hanmaceng.co.kr',
|
'b24014@hanmaceng.co.kr',
|
||||||
@@ -179,6 +254,10 @@ $download_url = "https://release-download.hmac.kr/eg-bim/{$version}/setup_eg-bim
|
|||||||
}, 50);
|
}, 50);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.signup-disabled-link', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
// 혹시 로그인 팝업을 다른 곳에서 열어도 배너가 필요하면, 팝업 열릴 때 한 번 더 시도
|
// 혹시 로그인 팝업을 다른 곳에서 열어도 배너가 필요하면, 팝업 열릴 때 한 번 더 시도
|
||||||
$(document).on('show-login-popup', function () {
|
$(document).on('show-login-popup', function () {
|
||||||
injectBanner();
|
injectBanner();
|
||||||
|
|||||||
@@ -312,19 +312,6 @@
|
|||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
<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() {
|
function member_leave() {
|
||||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ if (!isset($register_action_url)) {
|
|||||||
<link rel="stylesheet" href="../css/style.css" />
|
<link rel="stylesheet" href="../css/style.css" />
|
||||||
<script src="../js/jquery-3.6.1.min.js"></script> -->
|
<script src="../js/jquery-3.6.1.min.js"></script> -->
|
||||||
<!-- </head> -->
|
<!-- </head> -->
|
||||||
<script src="<?php echo G5_URL ?>/js/common.js"></script>
|
|
||||||
<script src="<?php echo G5_URL ?>/js/popup.js?v=20260723"></script>
|
|
||||||
|
|
||||||
<!-- 회원가입약관 동의 시작 { -->
|
<!-- 회원가입약관 동의 시작 { -->
|
||||||
<!-- <body>
|
<!-- <body>
|
||||||
<div class="register"> -->
|
<div class="register"> -->
|
||||||
|
|||||||
@@ -256,19 +256,6 @@
|
|||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
<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() {
|
function member_leave() {
|
||||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||||
|
|||||||
@@ -556,19 +556,6 @@ foreach ($partials as $p) {
|
|||||||
</html>
|
</html>
|
||||||
|
|
||||||
<script>
|
<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() {
|
function member_leave() {
|
||||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||||
|
|||||||
@@ -307,19 +307,6 @@ function getQueryString(name){
|
|||||||
if (writer) writer.addEventListener('change', submitForm);
|
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 (기존 로직 유지)
|
// Cookie helpers (기존 로직 유지)
|
||||||
function deleteCookie(name){
|
function deleteCookie(name){
|
||||||
document.cookie = name+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/eng;";
|
document.cookie = name+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/eng;";
|
||||||
@@ -437,7 +424,7 @@ function updateHeaderMenu(){
|
|||||||
$('#my_join2').html(
|
$('#my_join2').html(
|
||||||
isLoggedIn && isAdmin
|
isLoggedIn && isAdmin
|
||||||
? "<a href='/eng/adm/index.php' target='_blank'>관리페이지</a>"
|
? "<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); }
|
} catch(e){ console.error(e); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ function updateHeaderMenu(){
|
|||||||
$('#my_join2').html(
|
$('#my_join2').html(
|
||||||
isLoggedIn && isAdmin
|
isLoggedIn && isAdmin
|
||||||
? "<a href='/eng/adm/index.php' target='_blank'>관리페이지</a>"
|
? "<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); }
|
} catch(e){ console.error(e); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -363,19 +363,6 @@
|
|||||||
|
|
||||||
<!-- 상단(헤더) 스크립트 구문 -->
|
<!-- 상단(헤더) 스크립트 구문 -->
|
||||||
<script>
|
<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() {
|
function member_leave() {
|
||||||
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
if (confirm("회원에서 탈퇴 하시겠습니까?"))
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ if [[ -f .env ]]; then
|
|||||||
set +a
|
set +a
|
||||||
fi
|
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
|
mkdir -p docker/mysql/init
|
||||||
|
|
||||||
docker compose up -d --build
|
docker compose up -d --build
|
||||||
|
|||||||
Reference in New Issue
Block a user