示例#1
0
def login():
    # Retrieve required fields from client request
    try:
        email = request.json.get('username', None) or request.json['email']
        password = request.json['password']
    except KeyError:
        raise ApiError("must supply 'username' and 'password'", 401)

    username = email.split("@")[0]
    domain = email.split("@")[1]

    # Validate LDAP domain
    if domain not in current_app.config["LDAP_DOMAINS"]:
        raise ApiError("unauthorized domain", 403)

    userdn = current_app.config["LDAP_DOMAINS"][domain] % username

    # Attempt LDAP AUTH
    try:
        trace_level = 2 if current_app.debug else 0
        ldap_connection = ldap.initialize(current_app.config['LDAP_URL'],
                                          trace_level=trace_level)
        ldap_connection.simple_bind_s(userdn, password)
    except ldap.INVALID_CREDENTIALS:
        raise ApiError("invalid username or password", 401)
    except Exception as e:
        raise ApiError(str(e), 500)

    # Create user if not yet there
    user = User.find_by_email(email=email)
    if not user:
        user = User(username,
                    email,
                    "", ["user"],
                    "LDAP user",
                    email_verified=True)
        user.create()

    # Check user is active
    if user.status != 'active':
        raise ApiError('user not active', 403)

    # Assign customers & update last login time
    customers = get_customers(user.email, groups=[user.domain])
    user.update_last_login()

    # Generate token
    token = create_token(user.id,
                         user.name,
                         user.email,
                         provider='basic_ldap',
                         customers=customers,
                         roles=user.roles,
                         email=user.email,
                         email_verified=user.email_verified)
    return jsonify(token=token.tokenize)
示例#2
0
 def create_user(admin):
     user = User(name=admin,
                 email=admin,
                 password=generate_password_hash(password),
                 roles=None,
                 text='Admin user created by alertad script',
                 email_verified=True)
     try:
         db.get_db()  # init db on global app context
         user = user.create()
     except Exception as e:
         click.echo('ERROR: {}'.format(e))
     else:
         click.echo('{} {}'.format(user.id, user.name))
示例#3
0
 def create_user(name, login):
     email = login if '@' in login else None
     user = User(name=name or login,
                 login=login,
                 password=generate_password_hash(password),
                 roles=[DEFAULT_ADMIN_ROLE],
                 text=text,
                 email=email,
                 email_verified=bool(email))
     try:
         user = user.create()
     except Exception as e:
         click.echo('ERROR: {}'.format(e))
     else:
         return user
示例#4
0
 def create_user(name, login):
     email = login if '@' in login else None
     user = User(name=name or login,
                 login=login,
                 password=generate_password_hash(password),
                 roles=current_app.config['ADMIN_ROLES'],
                 text=text,
                 email=email,
                 email_verified=bool(email))
     try:
         user = user.create()
     except Exception as e:
         click.echo(f'ERROR: {e}')
     else:
         return user
示例#5
0
 def create_user(admin):
     email = admin if '@' in admin else None
     user = User(name='Admin user',
                 login=admin,
                 password=generate_password_hash(password),
                 roles=['admin'],
                 text='Created by alertad script',
                 email=email,
                 email_verified=bool(email))
     try:
         db.get_db()  # init db on global app context
         user = user.create()
     except Exception as e:
         click.echo('ERROR: {}'.format(e))
     else:
         click.echo('{} {}'.format(user.id, user.name))
示例#6
0
def saml_response_from_idp():
    origin = request.form['RelayState']

    authn_response = saml_client().parse_authn_request_response(
        xmlstr=request.form['SAMLResponse'],
        binding=saml2.entity.BINDING_HTTP_POST
    )
    identity = authn_response.get_identity()
    subject = authn_response.get_subject()

    name = current_app.config['SAML2_USER_NAME_FORMAT'].format(**dict(map(lambda x: (x[0], x[1][0]), identity.items())))
    login = subject.text
    email = identity[current_app.config['SAML2_EMAIL_ATTRIBUTE']][0]

    # Create user if not yet there
    user = User.find_by_username(username=email)
    if not user:
        user = User(name=name, login=login, password='', email=email,
                    roles=[], text='SAML2 user', email_verified=True)
        try:
            user = user.create()
        except Exception as e:
            ApiError(str(e), 500)

    if user.status != 'active':
        raise ApiError('User {} is not active'.format(email), 403)

    groups = identity.get('groups', [])
    if not_authorized('ALLOWED_SAML2_GROUPS', groups) or not_authorized('ALLOWED_EMAIL_DOMAINS', groups=[user.domain]):
        message = {'status': 'error', 'message': 'User {} is not authorized'.format(email)}
        return render_template('auth/saml2.html', message=message, origin=origin), 403

    user.update_last_login()

    scopes = Permission.lookup(login=user.email, roles=user.roles + groups)
    customers = get_customers(login=user.email, groups=[user.domain] + groups)

    auth_audit_trail.send(current_app._get_current_object(), event='saml2-login', message='user login via SAML2',
                          user=user.email, customers=customers, scopes=scopes, resource_id=user.id, type='user',
                          request=request)

    token = create_token(user_id=user.id, name=user.name, login=user.email, provider='saml2', customers=customers,
                         scopes=scopes, roles=user.roles, email=user.email, email_verified=user.email_verified)

    message = {'status': 'ok', 'token': token.tokenize}
    return render_template('auth/saml2.html', message=message, origin=origin), 200
示例#7
0
def login():
    # Retrieve required fields from client request
    try:
        login = request.json.get('username', None) or request.json['email']
        password = request.json['password']
    except KeyError:
        raise ApiError("must supply 'username' and 'password'", 401)

    if '\\' in login:
        domain, username = login.split('\\')
        email = ''
        email_verified = False
    else:
        username, domain = login.split('@')
        email = login
        email_verified = True

    # Validate LDAP domain
    if domain not in current_app.config['LDAP_DOMAINS']:
        raise ApiError('unauthorized domain', 403)

    userdn = current_app.config['LDAP_DOMAINS'][domain] % username

    # Attempt LDAP AUTH
    try:
        trace_level = 2 if current_app.debug else 0
        ldap_connection = ldap.initialize(current_app.config['LDAP_URL'],
                                          trace_level=trace_level)
        ldap_connection.simple_bind_s(userdn, password)
    except ldap.INVALID_CREDENTIALS:
        raise ApiError('invalid username or password', 401)
    except Exception as e:
        raise ApiError(str(e), 500)

    # Get email address from LDAP
    if not email_verified:
        try:
            ldap_result = ldap_connection.search_s(userdn, ldap.SCOPE_SUBTREE,
                                                   '(objectClass=*)', ['mail'])
            email = ldap_result[0][1]['mail'][0].decode(sys.stdout.encoding)
            email_verified = True
        except:
            email = '{}@{}'.format(username, domain)

    # Create user if not yet there
    user = User.find_by_username(username=login)
    if not user:
        user = User(name=username,
                    login=login,
                    password='',
                    email=email,
                    roles=[],
                    text='LDAP user',
                    email_verified=email_verified)
        try:
            user = user.create()
        except Exception as e:
            ApiError(str(e), 500)

    # Assign customers & update last login time
    groups = list()
    try:
        groups_filters = current_app.config.get('LDAP_DOMAINS_GROUP', {})
        base_dns = current_app.config.get('LDAP_DOMAINS_BASEDN', {})
        if domain in groups_filters and domain in base_dns:
            resultID = ldap_connection.search(
                base_dns[domain], ldap.SCOPE_SUBTREE,
                groups_filters[domain].format(username=username,
                                              email=email,
                                              userdn=userdn), ['cn'])
            resultTypes, results = ldap_connection.result(resultID)
            for _dn, attributes in results:
                groups.append(attributes['cn'][0].decode('utf-8'))
    except ldap.LDAPError as e:
        raise ApiError(str(e), 500)

    # Check user is active
    if user.status != 'active':
        raise ApiError('User {} not active'.format(login), 403)
    user.update_last_login()

    scopes = Permission.lookup(login=login, roles=user.roles + groups)
    customers = get_customers(login=login, groups=[user.domain] + groups)

    auth_audit_trail.send(current_app._get_current_object(),
                          event='basic-ldap-login',
                          message='user login via LDAP',
                          user=login,
                          customers=customers,
                          scopes=scopes,
                          resource_id=user.id,
                          type='user',
                          request=request)

    # Generate token
    token = create_token(user_id=user.id,
                         name=user.name,
                         login=user.email,
                         provider='ldap',
                         customers=customers,
                         scopes=scopes,
                         roles=user.roles,
                         email=user.email,
                         email_verified=user.email_verified)
    return jsonify(token=token.tokenize)
示例#8
0
def login():

    try:
        login = request.json.get('username') or request.json['email']
        password = request.json['password']
    except KeyError:
        raise ApiError("must supply 'username' and 'password'", 401)

    if not password:
        raise ApiError('password not allowed to be empty', 401)

    try:
        if '\\' in login:
            domain, username = login.split('\\')
        else:
            username, domain = login.split('@')
    except ValueError:
        if current_app.config['LDAP_DEFAULT_DOMAIN']:
            username = login
            domain = current_app.config['LDAP_DEFAULT_DOMAIN']
        else:
            raise ApiError('expected username with domain', 401)

    # Validate LDAP domain
    if (domain not in current_app.config['ALLOWED_EMAIL_DOMAINS']
            and domain not in current_app.config['LDAP_DOMAINS']):
        raise ApiError('unauthorized domain', 403)

    # LDAP certificate settings
    if current_app.config['LDAP_CACERT']:
        ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_HARD)
        ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,
                        current_app.config['LDAP_CACERT'])

    # Allow LDAP server to use a self-signed certificate
    if current_app.config['LDAP_ALLOW_SELF_SIGNED_CERT']:
        ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW)

    # Set LDAP Timeout
    if current_app.config['LDAP_TIMEOUT']:
        ldap.set_option(ldap.OPT_NETWORK_TIMEOUT,
                        current_app.config['LDAP_TIMEOUT'])

    # Set custom config options
    for k, v in current_app.config['LDAP_CONFIG'].items():
        ldap.set_option(getattr(ldap, k), v)

    # Initialise ldap connection
    try:
        trace_level = 2 if current_app.debug else 0  # XXX - do not set in production environments
        ldap_connection = ldap.initialize(current_app.config['LDAP_URL'],
                                          trace_level=trace_level)
    except Exception as e:
        raise ApiError(str(e), 500)

    # bind user credentials
    ldap_bind_username = current_app.config['LDAP_BIND_USERNAME']
    ldap_bind_password = current_app.config['LDAP_BIND_PASSWORD']
    if ldap_bind_username:
        try:
            ldap_connection.simple_bind_s(ldap_bind_username,
                                          ldap_bind_password)
        except ldap.INVALID_CREDENTIALS:
            raise ApiError('invalid ldap bind credentials', 500)

    # Set default base DN for user and group search
    base_dn = current_app.config['LDAP_BASEDN']

    # If user search filter exist
    #   Search the user using the provided User Search filter for the current domain
    #   If one user is found
    #       Set the DN as the one found
    #       Set email retreived from AD
    #   If more than one user is found
    #       Except: Search query is bad defined
    # Else
    #   Set the DN as the one found in LDAP_DOMAINS variable
    user_filter = current_app.config['LDAP_USER_FILTER']
    user_base_dn = current_app.config['LDAP_USER_BASEDN']
    user_attrs = [
        current_app.config['LDAP_USER_NAME_ATTR'],
        current_app.config['LDAP_USER_EMAIL_ATTR']
    ]
    if user_filter:
        result = [
            r for r in ldap_connection.search_s(base=user_base_dn or base_dn,
                                                scope=ldap.SCOPE_SUBTREE,
                                                filterstr=user_filter.format(
                                                    username=username),
                                                attrlist=user_attrs)
            if None not in r
        ]

        if len(result) > 1:
            raise ApiError(f'invalid search query for domain "{domain}"', 500)
        elif len(result) == 0:
            raise ApiError('invalid username or password', 401)
        user_dn = result[0][0]
        name = result[0][1][
            current_app.config['LDAP_USER_NAME_ATTR']][0].decode(
                'utf-8', 'ignore')
        email = result[0][1][
            current_app.config['LDAP_USER_EMAIL_ATTR']][0].decode(
                'utf-8', 'ignore')
        email_verified = bool(email)
    else:
        if '%' in current_app.config['LDAP_DOMAINS'][domain]:
            user_dn = current_app.config['LDAP_DOMAINS'][domain] % username
        else:
            user_dn = current_app.config['LDAP_DOMAINS'][domain].format(
                username)
        name = username
        email = f'{username}@{domain}'
        email_verified = False

    # Authenticate user logging in
    try:
        ldap_connection.simple_bind_s(user_dn, password)
    except ldap.INVALID_CREDENTIALS:
        raise ApiError('invalid username or password', 401)

    login = email or username
    user = User.find_by_username(username=login)
    if not user:
        user = User(name=name,
                    login=login,
                    password='',
                    email=email,
                    roles=current_app.config['USER_ROLES'],
                    text='LDAP user',
                    email_verified=email_verified)
        user = user.create()
    else:
        user.update(login=login, email=email, email_verified=email_verified)

    if ldap_bind_username:
        try:
            ldap_connection.simple_bind_s(ldap_bind_username,
                                          ldap_bind_password)
        except ldap.INVALID_CREDENTIALS:
            raise ApiError('invalid ldap bind credentials', 500)

    # Assign customers & update last login time
    group_filter = current_app.config['LDAP_GROUP_FILTER']
    group_base_dn = current_app.config['LDAP_GROUP_BASEDN']
    groups = list()
    if group_filter:
        result = ldap_connection.search_s(
            base=group_base_dn or base_dn,
            scope=ldap.SCOPE_SUBTREE,
            filterstr=group_filter.format(username=username,
                                          email=email,
                                          userdn=user_dn),
            attrlist=[current_app.config['LDAP_GROUP_NAME_ATTR']])
        for group_dn, group_attrs in result:
            if current_app.config['LDAP_GROUP_NAME_ATTR'] in group_attrs.keys(
            ):
                groups.extend([
                    g.decode('utf-8', 'ignore') for g in group_attrs[
                        current_app.config['LDAP_GROUP_NAME_ATTR']]
                ])
            else:
                groups.append(group_dn)

    # Check user is active
    if user.status != 'active':
        raise ApiError(f'User {login} not active', 403)
    if not_authorized('ALLOWED_LDAP_GROUPS', groups):
        raise ApiError(f'User {login} is not authorized', 403)
    user.update_last_login()

    scopes = Permission.lookup(login=login, roles=user.roles + groups)
    customers = get_customers(login=login, groups=[user.domain] + groups)

    auth_audit_trail.send(current_app._get_current_object(),
                          event='basic-ldap-login',
                          message='user login via LDAP',
                          user=login,
                          customers=customers,
                          scopes=scopes,
                          roles=user.roles,
                          groups=groups,
                          resource_id=user.id,
                          type='user',
                          request=request)

    # Generate token
    token = create_token(user_id=user.id,
                         name=user.name,
                         login=user.email,
                         provider='ldap',
                         customers=customers,
                         scopes=scopes,
                         roles=user.roles,
                         groups=groups,
                         email=user.email,
                         email_verified=user.email_verified)
    return jsonify(token=token.tokenize)
示例#9
0
def github():

    if current_app.config['GITHUB_URL'] == 'https://github.com':
        access_token_url = 'https://github.com/login/oauth/access_token'
        github_api_url = 'https://api.github.com'
    else:
        access_token_url = current_app.config[
            'GITHUB_URL'] + '/login/oauth/access_token'
        github_api_url = current_app.config['GITHUB_URL'] + '/api/v3'

    client_lookup = dict(
        zip(current_app.config['OAUTH2_CLIENT_ID'].split(','),
            current_app.config['OAUTH2_CLIENT_SECRET'].split(',')))
    client_secret = client_lookup.get(request.json['clientId'], None)
    data = {
        'grant_type': 'authorization_code',
        'code': request.json['code'],
        'redirect_uri': request.json['redirectUri'],
        'client_id': request.json['clientId'],
        'client_secret': client_secret,
    }
    r = requests.post(access_token_url,
                      data,
                      headers={'Accept': 'application/json'})
    token = r.json()

    headers = {'Authorization': 'token {}'.format(token['access_token'])}
    r = requests.get(github_api_url + '/user', headers=headers)
    profile = r.json()

    r = requests.get(github_api_url + '/user/orgs',
                     headers=headers)  # list public and private Github orgs
    organizations = [o['login'] for o in r.json()]

    subject = str(profile['id'])
    name = profile['name']
    username = '******' + profile['login']
    email = profile['email']
    email_verified = True if email else False
    picture = profile['avatar_url']

    login = username or email
    if not login:
        raise ApiError(
            "Must allow access to GitHub user profile information: 'login' or 'email'",
            400)

    user = User.find_by_id(id=subject)
    if not user:
        user = User(id=subject,
                    name=name,
                    login=login,
                    password='',
                    email=email,
                    roles=[],
                    text='',
                    email_verified=email_verified)
        user.create()
    else:
        user.update(login=login, email=email)

    roles = organizations or user.roles
    groups = organizations

    if user.status != 'active':
        raise ApiError('User {} is not active'.format(login), 403)

    if not_authorized('ALLOWED_GITHUB_ORGS', organizations):
        raise ApiError('User {} is not authorized'.format(login), 403)
    user.update_last_login()

    scopes = Permission.lookup(login, roles)
    customers = get_customers(login, groups=[user.domain] + groups)

    auth_audit_trail.send(current_app._get_current_object(),
                          event='github-login',
                          message='user login via GitHub',
                          user=login,
                          customers=customers,
                          scopes=scopes,
                          resource_id=subject,
                          type='user',
                          request=request)

    token = create_token(user_id=subject,
                         name=name,
                         login=login,
                         provider='github',
                         customers=customers,
                         scopes=scopes,
                         orgs=organizations,
                         email=email,
                         email_verified=email_verified,
                         picture=picture)
    return jsonify(token=token.tokenize)
示例#10
0
文件: oidc.py 项目: proffust/alerta
def openid():

    oidc_configuration, jwt_key_set = get_oidc_configuration(current_app)
    token_endpoint = oidc_configuration['token_endpoint']
    userinfo_endpoint = oidc_configuration['userinfo_endpoint']

    data = {
        'grant_type': 'authorization_code',
        'code': request.json['code'],
        'redirect_uri': request.json['redirectUri'],
        'client_id': request.json['clientId'],
        'client_secret': current_app.config['OAUTH2_CLIENT_SECRET'],
    }
    r = requests.post(token_endpoint, data)
    token = r.json()

    if 'error' in token:
        error_text = token.get('error_description') or token['error']
        raise ApiError(error_text)

    try:
        if current_app.config['OIDC_VERIFY_TOKEN']:
            jwt_header = jwt.get_unverified_header(token['id_token'])
            public_key = jwt_key_set[jwt_header['kid']]

            id_token = jwt.decode(token['id_token'],
                                  key=public_key,
                                  algorithms=jwt_header['alg'])
        else:
            id_token = jwt.decode(token['id_token'], verify=False)
    except Exception:
        current_app.logger.warning(
            'No ID token in OpenID Connect token response.')
        id_token = {}

    try:
        headers = {
            'Authorization':
            '{} {}'.format(token.get('token_type', 'Bearer'),
                           token['access_token'])
        }
        r = requests.get(userinfo_endpoint, headers=headers)
        userinfo = r.json()
    except Exception:
        raise ApiError('No access token in OpenID Connect token response.')

    subject = userinfo['sub']
    name = userinfo.get('name') or id_token.get('name')
    username = userinfo.get('preferred_username') or id_token.get(
        'preferred_username')
    nickname = userinfo.get('nickname') or id_token.get('nickname')
    email = userinfo.get('email') or id_token.get('email')
    email_verified = userinfo.get('email_verified',
                                  id_token.get('email_verified', bool(email)))
    email_verified = True if email_verified == 'true' else email_verified  # Cognito returns string boolean
    picture = userinfo.get('picture') or id_token.get('picture')

    role_claim = current_app.config['OIDC_ROLE_CLAIM']
    group_claim = current_app.config['OIDC_GROUP_CLAIM']
    custom_claims = {
        role_claim: userinfo.get(role_claim) or id_token.get(role_claim, []),
        group_claim: userinfo.get(group_claim)
        or id_token.get(group_claim, []),
    }

    login = username or nickname or email
    if not login:
        raise ApiError(
            "Must support one of the following OpenID claims: 'preferred_username', 'nickname' or 'email'",
            400)

    if current_app.config['OIDC_LINK_USER_EMAIL'] and email and email_verified:
        user = User.find_by_email(email=email)
    else:
        user = User.find_by_id(id=subject)

    if not user:
        user = User(id=subject,
                    name=name,
                    login=login,
                    password='',
                    email=email,
                    roles=current_app.config['USER_ROLES'],
                    text='',
                    email_verified=email_verified)
        user.create()
    else:
        user.update(login=login, email=email, email_verified=email_verified)

    roles = custom_claims[role_claim] + user.roles
    groups = custom_claims[group_claim]

    if user.id != subject:
        custom_claims[
            'oid'] = user.id  # if subject differs store the original subject as "oid" claim

    if user.status != 'active':
        raise ApiError('User {} is not active'.format(login), 403)

    if not_authorized('ALLOWED_OIDC_ROLES', roles) or not_authorized(
            'ALLOWED_EMAIL_DOMAINS', groups=[user.domain]):
        raise ApiError('User {} is not authorized'.format(login), 403)
    user.update_last_login()

    scopes = Permission.lookup(login, roles=roles)
    customers = get_customers(login, groups=[user.domain] + groups)

    auth_audit_trail.send(current_app._get_current_object(),
                          event='openid-login',
                          message='user login via OpenID Connect',
                          user=login,
                          customers=customers,
                          scopes=scopes,
                          **custom_claims,
                          resource_id=subject,
                          type='user',
                          request=request)

    token = create_token(user_id=subject,
                         name=name,
                         login=login,
                         provider=current_app.config['AUTH_PROVIDER'],
                         customers=customers,
                         scopes=scopes,
                         **custom_claims,
                         email=email,
                         email_verified=email_verified,
                         picture=picture)
    return jsonify(token=token.tokenize)
示例#11
0
def login():
    # Retrieve required fields from client request
    try:
        email = request.json.get('username', None) or request.json['email']
        password = request.json['password']
    except KeyError:
        raise ApiError("must supply 'username' and 'password'", 401)

    username = email.split('@')[0]
    domain = email.split('@')[1]

    # Validate LDAP domain
    if domain not in current_app.config['LDAP_DOMAINS']:
        raise ApiError('unauthorized domain', 403)

    userdn = current_app.config['LDAP_DOMAINS'][domain] % username

    # Attempt LDAP AUTH
    try:
        trace_level = 2 if current_app.debug else 0
        ldap_connection = ldap.initialize(current_app.config['LDAP_URL'], trace_level=trace_level)
        ldap_connection.simple_bind_s(userdn, password)
    except ldap.INVALID_CREDENTIALS:
        raise ApiError('invalid username or password', 401)
    except Exception as e:
        raise ApiError(str(e), 500)

    # Create user if not yet there
    user = User.find_by_email(email=email)
    if not user:
        user = User(username, email, '', ['user'], 'LDAP user', email_verified=True)
        user.create()

    # Check user is active
    if user.status != 'active':
        raise ApiError('user not active', 403)

    # Assign customers & update last login time
    groups = [user.domain]
    try:
        groups_filters = current_app.config.get('LDAP_DOMAINS_GROUP', {})
        base_dns = current_app.config.get('LDAP_DOMAINS_BASEDN', {})
        if domain in groups_filters and domain in base_dns:
            resultID = ldap_connection.search(base_dns[domain], ldap.SCOPE_SUBTREE, \
                                              groups_filters[domain].format(username=username, email=email, userdn=userdn), \
                                              ['cn'])
            resultTypes, results = ldap_connection.result(resultID)
            for _dn, attributes in results:
                groups.append(attributes['cn'][0].decode("utf-8"))
    except ldap.LDAPError as e:
        raise ApiError(str(e), 500)

    customers = get_customers(user.email, groups=groups)
    user.update_last_login()

    auth_audit_trail.send(current_app._get_current_object(), event='basic-ldap-login', message='user login via LDAP',
                          user=user.email, customers=customers, scopes=Permission.lookup(login, groups=user.roles),
                          resource_id=user.id, type='user', request=request)

    # Generate token
    token = create_token(user_id=user.id, name=user.name, login=user.email, provider='basic_ldap',
                         customers=customers, roles=user.roles, email=user.email, email_verified=user.email_verified)
    return jsonify(token=token.tokenize)
示例#12
0
def login():
    # Allow LDAP server to use a self signed certificate
    if current_app.config['LDAP_ALLOW_SELF_SIGNED_CERT']:
        ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW)

    # Retrieve required fields from client request
    try:
        login = request.json.get('username', None) or request.json['email']
        password = request.json['password']
    except KeyError:
        raise ApiError("must supply 'username' and 'password'", 401)

    try:
        if '\\' in login:
            domain, username = login.split('\\')
            email = ''
            email_verified = False
        else:
            username, domain = login.split('@')
            email = login
            email_verified = True
    except ValueError:
        raise ApiError('expected username with domain', 401)

    # Validate LDAP domain
    if domain not in current_app.config['LDAP_DOMAINS'] and \
       domain not in current_app.config['LDAP_DOMAINS_SEARCH_QUERY']:
        raise ApiError('unauthorized domain', 403)

    # Initialise ldap connection
    try:
        trace_level = 2 if current_app.debug else 0
        ldap_connection = ldap.initialize(current_app.config['LDAP_URL'],
                                          trace_level=trace_level)
    except Exception as e:
        raise ApiError(str(e), 500)

    # If user search filter exist
    #   Search the user using the provided User Search filter for the current domain
    #   If one user is found
    #       Set the DN as the one found
    #       Set email retreived from AD
    #   If more than one user is found
    #       Except: Search query is bad defined
    # Else
    #   Set the DN as the one found in LDAP_DOMAINS variable
    domain_search_query = current_app.config.get('LDAP_DOMAINS_SEARCH_QUERY',
                                                 {})
    base_dns = current_app.config.get('LDAP_DOMAINS_BASEDN', {})
    user_base_dn = current_app.config.get('LDAP_DOMAINS_USER_BASEDN', {})
    if domain in domain_search_query:
        ldap_bind_username = current_app.config.get('LDAP_BIND_USERNAME', '')
        ldap_bind_password = current_app.config.get('LDAP_BIND_PASSWORD', '')

        try:
            ldap_connection.simple_bind_s(ldap_bind_username,
                                          ldap_bind_password)
        except ldap.INVALID_CREDENTIALS:
            raise ApiError('invalid ldap bind username or password', 401)

        ldap_users = [(_dn, user) for _dn, user in ldap_connection.search_s(
            base_dns[domain]
            if user_base_dn.get(domain) is None else user_base_dn[domain],
            ldap.SCOPE_SUBTREE, domain_search_query[domain].format(
                username=username, email=email), ['mail']) if _dn is not None]

        if len(ldap_users) > 1:
            raise ApiError(
                'invalid search query for domain "{}"'.format(domain), 500)
        elif len(ldap_users) == 0:
            raise ApiError('invalid username or password', 401)

        for _dn, _email in ldap_users:
            userdn = _dn
            email_attr = _email.get('mail')
            if email_attr is not None:
                email = email_attr[0].decode(sys.stdout.encoding)
                email_verified = True
    else:
        userdn = current_app.config['LDAP_DOMAINS'][domain] % username

    # Attempt LDAP AUTH
    try:
        ldap_connection.simple_bind_s(userdn, password)
    except ldap.INVALID_CREDENTIALS:
        raise ApiError('invalid username or password', 401)
    except Exception as e:
        raise ApiError(str(e), 500)

    # Get email address from LDAP
    if not email_verified:
        try:
            ldap_result = ldap_connection.search_s(userdn, ldap.SCOPE_SUBTREE,
                                                   '(objectClass=*)', ['mail'])
            email = ldap_result[0][1]['mail'][0].decode(sys.stdout.encoding)
            email_verified = True
        except Exception:
            email = '{}@{}'.format(username, domain)

    # Create user if not yet there
    user = User.find_by_username(username=login)
    if not user:
        user = User(name=username,
                    login=login,
                    password='',
                    email=email,
                    roles=[],
                    text='LDAP user',
                    email_verified=email_verified)
        try:
            user = user.create()
        except Exception as e:
            ApiError(str(e), 500)

    # Assign customers & update last login time
    groups = list()
    try:
        groups_filters = current_app.config.get('LDAP_DOMAINS_GROUP', {})
        groups_base_dn = current_app.config.get('LDAP_DOMAINS_GROUP_BASEDN',
                                                {})
        if domain in groups_filters and (domain in base_dns
                                         or domain in groups_base_dn):
            resultID = ldap_connection.search(
                base_dns[domain] if groups_base_dn.get(domain) is None else
                groups_base_dn[domain], ldap.SCOPE_SUBTREE,
                groups_filters[domain].format(username=username,
                                              email=email,
                                              userdn=userdn), ['cn'])
            resultTypes, results = ldap_connection.result(resultID)
            for _dn, attributes in results:
                if _dn is not None:
                    groups.append(attributes['cn'][0].decode('utf-8'))
    except ldap.LDAPError as e:
        raise ApiError(str(e), 500)

    # Check user is active
    if user.status != 'active':
        raise ApiError('User {} not active'.format(login), 403)
    user.update_last_login()

    scopes = Permission.lookup(login=login, roles=user.roles + groups)
    customers = get_customers(login=login, groups=[user.domain] + groups)

    auth_audit_trail.send(current_app._get_current_object(),
                          event='basic-ldap-login',
                          message='user login via LDAP',
                          user=login,
                          customers=customers,
                          scopes=scopes,
                          roles=user.roles,
                          groups=groups,
                          resource_id=user.id,
                          type='user',
                          request=request)

    # Generate token
    token = create_token(user_id=user.id,
                         name=user.name,
                         login=user.email,
                         provider='ldap',
                         customers=customers,
                         scopes=scopes,
                         roles=user.roles,
                         groups=groups,
                         email=user.email,
                         email_verified=user.email_verified)
    return jsonify(token=token.tokenize)
示例#13
0
def login():
    # Retrieve required fields from client request
    try:
        email = request.json.get('username', None) or request.json['email']
        password = request.json['password']
    except KeyError:
        raise ApiError("must supply 'username' and 'password'", 401)

    # Define ldap filter use %s for username
    ldapfilter = f'(mail={email})'

    # Attempt LDAP AUTH with binddn
    try:
        ldap_connection = ldap.initialize(ldapurl)
        ldap_connection.simple_bind_s(binddn, binddnpw)
    except ldap.INVALID_CREDENTIALS:
        raise ApiError("invalid username or password for binddn", 401)
    except Exception as e:
        raise ApiError(str(e), 500)

    # Start LDAP search
    try:
        ldapquery = ldap_connection.search_s(ldapbasedn, ldap.SCOPE_SUBTREE,
                                             ldapfilter, ['cn'])
        userdn = ldapquery[0][0]
        usercn = str(b''.join(ldapquery[0][1]['cn']), 'utf-8')

    except Exception:
        raise ApiError("invalid username or basedn", 401)

    # Attempt LDAP AUTH
    try:
        ldap_connection.simple_bind_s(userdn, password)
    except ldap.INVALID_CREDENTIALS:
        raise ApiError("invalid password", 401)
    except Exception as e:
        raise ApiError(str(e), 500)

    # Create user if not yet there
    user = User.find_by_email(email=email)
    if not user:
        user = User(usercn,
                    email,
                    "",
                    ldaprole.split(),
                    "LDAP user",
                    email_verified=True)
        user.create()

    # Check user is active
    if user.status != 'active':
        raise ApiError('user not active', 403)

    # Assign customers & update last login time
    customers = get_customers(user.email, groups=[user.domain])
    user.update_last_login()

    # Generate token
    token = create_token(user.id,
                         user.name,
                         user.email,
                         provider='basic_ldap',
                         customers=customers,
                         roles=user.roles,
                         email=user.email,
                         email_verified=user.email_verified)
    return jsonify(token=token.tokenize)
示例#14
0
def openid():

    oidc_configuration, jwt_key_set = get_oidc_configuration(current_app)
    token_endpoint = oidc_configuration['token_endpoint']
    userinfo_endpoint = oidc_configuration['userinfo_endpoint']

    data = {
        'grant_type': 'authorization_code',
        'code': request.json['code'],
        'redirect_uri': request.json['redirectUri'],
        'client_id': request.json['clientId'],
        'client_secret': current_app.config['OAUTH2_CLIENT_SECRET'],
    }
    r = requests.post(token_endpoint, data)
    token = r.json()

    try:
        if current_app.config['OIDC_VERIFY_TOKEN']:
            jwt_header = jwt.get_unverified_header(token['id_token'])
            public_key = jwt_key_set[jwt_header['kid']]

            id_token = jwt.decode(
                token['id_token'],
                key=public_key,
                algorithms=jwt_header['alg']
            )
        else:
            id_token = jwt.decode(
                token['id_token'],
                verify=False
            )
    except Exception:
        current_app.logger.warning('No ID token in OpenID Connect token response.')
        id_token = {}

    try:
        headers = {'Authorization': '{} {}'.format(token.get('token_type', 'Bearer'), token['access_token'])}
        r = requests.get(userinfo_endpoint, headers=headers)
        userinfo = r.json()
    except Exception:
        raise ApiError('No access token in OpenID Connect token response.')

    subject = userinfo['sub']
    name = userinfo.get('name') or id_token.get('name')
    nickname = userinfo.get('nickname')
    email = userinfo.get('email') or id_token.get('email')
    email_verified = userinfo.get('email_verified', id_token.get('email_verified', bool(email)))

    role_claim = current_app.config['OIDC_ROLE_CLAIM']
    group_claim = current_app.config['OIDC_GROUP_CLAIM']
    custom_claims = {
        role_claim: userinfo.get(role_claim) or id_token.get(role_claim, []),
        group_claim: userinfo.get(group_claim) or id_token.get(group_claim, []),
    }

    user = User.find_by_id(id=subject)
    if not user:
        user = User(id=subject, name=name, email=email, password='', roles=[], text='', email_verified=email_verified)
        user.create()

    login = userinfo.get('preferred_username', nickname or email)
    roles = custom_claims[role_claim] or user.roles
    groups = custom_claims[group_claim]

    if user.status != 'active':
        raise ApiError('User {} is not active'.format(login), 403)

    if not_authorized('ALLOWED_OIDC_ROLES', roles) and not_authorized('ALLOWED_EMAIL_DOMAINS', groups=[user.domain]):
        raise ApiError('User {} is not authorized'.format(login), 403)
    user.update_last_login()

    scopes = Permission.lookup(login, roles)
    customers = get_customers(login, groups=[user.domain] + groups)

    auth_audit_trail.send(current_app._get_current_object(), event='openid-login', message='user login via OpenID Connect',
                          user=login, customers=customers, scopes=scopes, resource_id=subject, type='user', request=request)

    token = create_token(user_id=subject, name=name, login=login, provider='openid', customers=customers,
                         scopes=scopes, email=email, email_verified=email_verified, **custom_claims)
    return jsonify(token=token.tokenize)