본문 바로가기
기술자료 (KB)/Active Directory (AD)

07. Streamlit AD 프로그램 코드 분리

by 이완주 2024. 8. 5.

 

 

06. Streamlit AD 로그인 후 패스워드 변경

05. Streamlit으로 AD 로그인을 SSL로 인증하기Active Directory로 Password를 초기화하려면 LDAPS로 인증을 해야 만 한다. Active Directory와 LDAPS(LDAP over SSL) 연결을 설정하려면 서버에 적절한 SSL 인증서가 설치

leemcse.tistory.com

 

코드를 여러 개의 파일로 나누면 유지보수와 확장이 용이해집니다. 다음과 같이 파일을 분리할 수 있습니다.

코드 분리하면서 소스코드 안에 search_base = 'dc=gsoft,dc=local' 값을 로그인할 때 서버의 FQDN 값을 받아 처리 하도록 변경 함.

 

 

FQDN 값을 받아 DN 값 추출하기

Active Directory DN(distinguishedName) 은 유니크 값으로 설정 됩니다.DN의 표기는 DN='CN=이 완주,OU=TestOU,DC=gsoft,DC=local' 형식으로 표시된다.서버의 FQDN은 ServerName + Domain Name으로 구성된다.ServerName은 CN (CommonN

leemcse.tistory.com

 

  1. authenticate.py: 인증 관련 함수
  2. login_page.py: 로그인 페이지
  3. main_page.py: 메인 페이지
  4. change_password_page.py: 비밀번호 변경 페이지
  5. app.py: 메인 어플리케이션 실행 파일

각 파일의 내용은 다음과 같습니다:

 

authenticate.py: 인증 관련 함수

import ssl
from ldap3 import Server, Connection, ALL, Tls
import streamlit as st

def authenticate(server, username, password, baseDN):
    try:
        tls_configuration = Tls(validate=ssl.CERT_REQUIRED, version=ssl.PROTOCOL_TLSv1_2)
        server = Server(server, use_ssl=True, get_info=ALL, tls=tls_configuration)
        conn = Connection(server, user=username, password=password)
        if conn.bind():
            return True
        else:
            return False
    except Exception as e:
        st.error(f"인증 오류 : {str(e)}")
        return False

 

 

login_page.py: 로그인 페이지

import streamlit as st
from authenticate import authenticate

def login_page():
    st.title('Active Directory 로그인 페이지')
    username = st.text_input('로그인 ID', placeholder='AccountID@domain.local 형식으로 입력해 주세요.')
    password = st.text_input('비밀번호', type='password')
    server = st.text_input('서버 FQDN', placeholder='servername.domain.local 형식으로 입력해 주세요.')
   
    # server 값으로 받은 FQDN 값을 . 으로 분류하여 part 변수에 배열 값으로 설정
    parts = server.split(".")
   
    # 서버명은 cn 임으로 배열 0 번째 값에는 cn= 를 붙이고 나머지 값은 for 문으로 dc= 값을
    # 결과 ['cn=server', 'dc=gsoft', 'dc=local']
    modified_strings = [f'cn={parts[0]}'] + [f'dc={parts}' for parts in parts[1:]]
   
    # dc= 값만 추출하여 dc 변수에 저장
    # 결과 ['dc=gsoft', 'dc=local']
    dc = [part for part in modified_strings if part.startswith('dc=')]
    # baseDN 변수에 join 함수를 사용하여 , 로 합치기
    # baseDN 변수에 dc=gsoft,dc=local 값이 저장
    baseDN = ',' .join(dc)  


    if st.button('로그인'):
        if not username:
            st.error("로그인 ID를 입력하세요.")
        elif not password:
            st.error("비밀번호를 입력하세요.")
        elif not server:
            st.error("서버 이름 혹은 IP를 입력하세요.")
        else:
            if authenticate(server, username, password, baseDN):
                st.session_state['authenticated'] = True
                st.session_state['server'] = server
                st.session_state['username'] = username
                st.session_state['password'] = password
                st.session_state['baseDN'] = baseDN
                st.session_state['page'] = 'main'
                st.rerun()
            else:
                st.error('로그인 실패')

 

 

main_page.py: 메인 페이지

import ssl
from ldap3 import Server, Connection, ALL, Tls
import streamlit as st

def main_page(server, username, password, baseDN):
    if st.button('로그아웃'):
        st.session_state['authenticated'] = False
        st.session_state['server'] = None
        st.session_state['username'] = None
        st.session_state['password'] = None
        st.session_state['baseDN'] = None
        st.rerun()

    st.title('Active Directory 메인 페이지')
    st.markdown(f"성공적으로 로그인했습니다.\n\n현재 로그인한 사용자: {username}")

    tls_configuration = Tls(validate=ssl.CERT_REQUIRED, version=ssl.PROTOCOL_TLSv1_2)
    server = Server(server, use_ssl=True, get_info=ALL, tls=tls_configuration)
    conn = Connection(server, user=username, password=password)

    if conn.bind():
        #search_base = 'dc=gsoft,dc=local'
        search_base = baseDN
        search_filter = f'(UserPrincipalName={username})'
        conn.search(search_base, search_filter, attributes=['displayName', 'description'])
        if conn.entries:
            entry = conn.entries[0]
            st.write(f"displayname : {entry.displayName.value}")
            st.write(f"description : {entry.description.value}")

    if st.button("비밀번호 변경"):
        st.session_state['page'] = 'change_password'
        st.rerun()

 

 

change_password_page.py: 비밀번호 변경 페이지

import ssl
from ldap3 import Server, Connection, ALL, Tls
import streamlit as st

def change_password_page(server, username, baseDN):
    if st.button("이전"):
        st.session_state['page'] = 'main'
        st.rerun()

    st.title('비밀번호 변경 페이지')

    old_password = st.text_input("현재 비밀번호", type='password')
    new_password = st.text_input("새 비밀번호", type='password')
    confirm_password = st.text_input("새 비밀번호 확인", type='password')

    if st.button("비밀번호 변경"):
        if new_password != confirm_password:
            st.error("새 비밀번호와 확인 비밀번호가 일치하지 않습니다.")
        else:
            try:
                tls_configuration = Tls(validate=ssl.CERT_REQUIRED, version=ssl.PROTOCOL_TLSv1_2)
                server = Server(server, use_ssl=True, get_info=ALL, tls=tls_configuration)
                conn = Connection(server, user=username, password=old_password)
                if not conn.bind():
                    st.error("현재 비밀번호가 잘못되었습니다.")
                else:
                    #search_base = 'dc=gsoft,dc=local'
                    search_base = baseDN
                    search_filter = f'(UserPrincipalName={username})'
                    conn.search(search_base, search_filter, attributes=['displayName'])
                    if conn.entries:
                        user_dn = conn.entries[0].entry_dn
                        conn.extend.microsoft.modify_password(user_dn, new_password, old_password)
                        if conn.result['description'] == 'success':
                            st.success("비밀번호가 성공적으로 변경되었습니다.")
                            st.session_state['password'] = new_password
                        else:
                            st.error(f"비밀번호 변경 실패: {conn.result}")
            except Exception as e:
                st.error(f"비밀번호 변경 중 오류 발생: {str(e)}")

 

 

app.py: 메인 어플리케이션 실행 파일

import streamlit as st
from login_page import login_page
from main_page import main_page
from change_password_page import change_password_page

# 세션 상태 초기화
if 'authenticated' not in st.session_state:
    st.session_state['authenticated'] = False
    st.session_state['server'] = None
    st.session_state['username'] = None
    st.session_state['password'] = None
    st.session_state['baseDN'] = None

# 로그인 상태에 따라 페이지 표시
if st.session_state['authenticated']:
    if st.session_state['page'] == 'main':
        main_page(st.session_state['server'], st.session_state['username'], st.session_state['password'], st.session_state['baseDN'])
    elif st.session_state['page'] == 'change_password':
        change_password_page(st.session_state['server'], st.session_state['username'], st.session_state['baseDN'])
else:
    login_page()

 

 

Streamlit run app.py

실행을 하면 기존과 같이 동일하게 실행 됨을 확인 할 수 있다.

댓글