feat(api): ldap and OAuth2.0

This commit is contained in:
pycook 2023-12-14 19:49:31 +08:00
parent 21bc6631f9
commit e7dfe6c573
7 changed files with 31 additions and 13 deletions

View File

@ -5,6 +5,7 @@ from api.lib.resp_format import CommonErrFormat
class ErrFormat(CommonErrFormat): class ErrFormat(CommonErrFormat):
login_succeed = "登录成功" login_succeed = "登录成功"
ldap_connection_failed = "连接LDAP服务失败"
invalid_password = "密码验证失败" invalid_password = "密码验证失败"
auth_only_with_app_token_failed = "应用 Token验证失败" auth_only_with_app_token_failed = "应用 Token验证失败"
session_invalid = "您不是应用管理员 或者 session失效(尝试一下退出重新登录)" session_invalid = "您不是应用管理员 或者 session失效(尝试一下退出重新登录)"

View File

@ -93,6 +93,9 @@ def _auth_with_token():
def _auth_with_ip_white_list(): def _auth_with_ip_white_list():
if request.url.endswith("acl/users/info"):
return False
ip = request.headers.get('X-Real-IP') or request.remote_addr ip = request.headers.get('X-Real-IP') or request.remote_addr
key = request.values.get('_key') key = request.values.get('_key')
secret = request.values.get('_secret') secret = request.values.get('_secret')

View File

@ -57,7 +57,7 @@ def login():
if request.args.get('ticket'): if request.args.get('ticket'):
if validate(request.args['ticket']): if validate(request.args['ticket']):
redirect_url = session.get("next") or config.get("cas_after_login") redirect_url = session.get("next") or config.get("cas_after_login") or "/"
username = session.get("CAS_USERNAME") username = session.get("CAS_USERNAME")
user = UserCache.get(username) user = UserCache.get(username)
login_user(user) login_user(user)

View File

@ -2,6 +2,7 @@
import uuid import uuid
from flask import abort
from flask import current_app from flask import current_app
from flask import session from flask import session
from ldap3 import ALL from ldap3 import ALL
@ -10,20 +11,25 @@ from ldap3 import Connection
from ldap3 import Server from ldap3 import Server
from ldap3.core.exceptions import LDAPBindError from ldap3.core.exceptions import LDAPBindError
from ldap3.core.exceptions import LDAPCertificateError from ldap3.core.exceptions import LDAPCertificateError
from ldap3.core.exceptions import LDAPSocketOpenError
from api.lib.common_setting.common_data import AuthenticateDataCRUD
from api.lib.common_setting.const import AuthenticateType
from api.lib.perm.acl.audit import AuditCRUD from api.lib.perm.acl.audit import AuditCRUD
from api.lib.perm.acl.resp_format import ErrFormat from api.lib.perm.acl.resp_format import ErrFormat
from api.models.acl import User from api.models.acl import User
def authenticate_with_ldap(username, password): def authenticate_with_ldap(username, password):
server = Server(current_app.config.get('LDAP').get('ldap_server'), get_info=ALL, connect_timeout=3) config = AuthenticateDataCRUD(AuthenticateType.CAS).get()
server = Server(config.get('LDAP').get('ldap_server'), get_info=ALL, connect_timeout=3)
if '@' in username: if '@' in username:
email = username email = username
who = current_app.config['LDAP'].get('ldap_user_dn').format(username.split('@')[0]) who = config['LDAP'].get('ldap_user_dn').format(username.split('@')[0])
else: else:
who = current_app.config['LDAP'].get('ldap_user_dn').format(username) who = config['LDAP'].get('ldap_user_dn').format(username)
email = "{}@{}".format(who, current_app.config['LDAP'].get('ldap_domain')) email = "{}@{}".format(who, config['LDAP'].get('ldap_domain'))
username = username.split('@')[0] username = username.split('@')[0]
user = User.query.get_by_username(username) user = User.query.get_by_username(username)
@ -35,7 +41,7 @@ def authenticate_with_ldap(username, password):
conn = Connection(server, user=who, password=password, auto_bind=AUTO_BIND_NO_TLS) conn = Connection(server, user=who, password=password, auto_bind=AUTO_BIND_NO_TLS)
except LDAPBindError: except LDAPBindError:
conn = Connection(server, conn = Connection(server,
user=f"{username}@{current_app.config['LDAP'].get('ldap_domain')}", user=f"{username}@{config['LDAP'].get('ldap_domain')}",
password=password, password=password,
auto_bind=AUTO_BIND_NO_TLS) auto_bind=AUTO_BIND_NO_TLS)
@ -51,6 +57,11 @@ def authenticate_with_ldap(username, password):
user = UserCRUD.add(username=username, email=email, password=uuid.uuid4().hex) user = UserCRUD.add(username=username, email=email, password=uuid.uuid4().hex)
return user, True return user, True
except LDAPBindError as e: except LDAPBindError as e:
current_app.logger.info(e) current_app.logger.info(e)
return user, False return user, False
except LDAPSocketOpenError as e:
current_app.logger.info(e)
return abort(403, ErrFormat.ldap_connection_failed)

View File

@ -36,7 +36,7 @@ def login(auth_type):
qs = urlencode({ qs = urlencode({
'client_id': config['client_id'], 'client_id': config['client_id'],
'redirect_uri': url_for('oauth2.callback', _external=True), 'redirect_uri': url_for('oauth2.callback', auth_type=auth_type.lower(), _external=True),
'response_type': current_app.config[f'{auth_type}_RESPONSE_TYPE'], 'response_type': current_app.config[f'{auth_type}_RESPONSE_TYPE'],
'scope': ' '.join(config['scopes'] or []), 'scope': ' '.join(config['scopes'] or []),
'state': session[f'{auth_type.lower()}_state'], 'state': session[f'{auth_type.lower()}_state'],
@ -50,7 +50,7 @@ def callback(auth_type):
auth_type = auth_type.upper() auth_type = auth_type.upper()
config = AuthenticateDataCRUD(auth_type).get() config = AuthenticateDataCRUD(auth_type).get()
redirect_url = session.get("next") or config.get('after_login') redirect_url = session.get("next") or config.get('after_login') or '/'
if request.values['state'] != session.get(f'{auth_type.lower()}_state'): if request.values['state'] != session.get(f'{auth_type.lower()}_state'):
return abort(401, "state is invalid") return abort(401, "state is invalid")
@ -63,7 +63,7 @@ def callback(auth_type):
'client_secret': config['client_secret'], 'client_secret': config['client_secret'],
'code': request.values['code'], 'code': request.values['code'],
'grant_type': current_app.config[f'{auth_type}_GRANT_TYPE'], 'grant_type': current_app.config[f'{auth_type}_GRANT_TYPE'],
'redirect_uri': url_for('oauth2.callback', _external=True), 'redirect_uri': url_for('oauth2.callback', auth_type=auth_type.lower(), _external=True),
}, headers={'Accept': 'application/json'}) }, headers={'Accept': 'application/json'})
if response.status_code != 200: if response.status_code != 200:
current_app.logger.error(response.text) current_app.logger.error(response.text)
@ -123,7 +123,7 @@ def logout(auth_type):
f'{auth_type}_state' in session and session.pop(f'{auth_type}_state') f'{auth_type}_state' in session and session.pop(f'{auth_type}_state')
"next" in session and session.pop("next") "next" in session and session.pop("next")
redirect_url = url_for('oauth2.login', _external=True, next=request.referrer) redirect_url = url_for('oauth2.login', auth_type=auth_type, _external=True, next=request.referrer)
logout_user() logout_user()

View File

@ -11,6 +11,8 @@ from flask import session
from flask_login import login_user from flask_login import login_user
from flask_login import logout_user from flask_login import logout_user
from api.lib.common_setting.common_data import AuthenticateDataCRUD
from api.lib.common_setting.const import AuthenticateType
from api.lib.decorator import args_required from api.lib.decorator import args_required
from api.lib.decorator import args_validate from api.lib.decorator import args_validate
from api.lib.perm.acl.acl import ACLManager from api.lib.perm.acl.acl import ACLManager
@ -36,7 +38,8 @@ class LoginView(APIView):
username = request.values.get("username") or request.values.get("email") username = request.values.get("username") or request.values.get("email")
password = request.values.get("password") password = request.values.get("password")
_role = None _role = None
if current_app.config.get('LDAP', {}).get('enabled'): config = AuthenticateDataCRUD(AuthenticateType.LDAP).get()
if config.get('LDAP', {}).get('enabled'):
from api.lib.perm.authentication.ldap import authenticate_with_ldap from api.lib.perm.authentication.ldap import authenticate_with_ldap
user, authenticated = authenticate_with_ldap(username, password) user, authenticated = authenticate_with_ldap(username, password)
else: else:

View File

@ -2,7 +2,7 @@ version: '3.5'
services: services:
cmdb-db: cmdb-db:
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-db:3.0 image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-db:2.3
container_name: cmdb-db container_name: cmdb-db
environment: environment:
TZ: Asia/Shanghai TZ: Asia/Shanghai
@ -22,7 +22,7 @@ services:
- '23306:3306' - '23306:3306'
cmdb-cache: cmdb-cache:
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-cache:3.0 image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-cache:2.3
container_name: cmdb-cache container_name: cmdb-cache
environment: environment:
TZ: Asia/Shanghai TZ: Asia/Shanghai