def keycloak(): if not current_app.config['KEYCLOAK_URL']: return jsonify(status="error", message="Must define KEYCLOAK_URL setting in server configuration."), 503 access_token_url = "{0}/auth/realms/{1}/protocol/openid-connect/token".format(current_app.config['KEYCLOAK_URL'], current_app.config['KEYCLOAK_REALM']) payload = { 'client_id': request.json['clientId'], 'client_secret': current_app.config['OAUTH2_CLIENT_SECRET'], 'redirect_uri': request.json['redirectUri'], 'grant_type': 'authorization_code', 'code': request.json['code'], } try: r = requests.post(access_token_url, data=payload) except Exception: return jsonify(status="error", message="Failed to call Keycloak API over HTTPS") access_token = r.json() headers = {"Authorization": "{0} {1}".format(access_token['token_type'], access_token['access_token'])} r = requests.get("{0}/auth/realms/{1}/protocol/openid-connect/userinfo".format(current_app.config['KEYCLOAK_URL'], current_app.config['KEYCLOAK_REALM']), headers=headers) profile = r.json() roles = profile['roles'] login = profile['preferred_username'] if is_authorized('ALLOWED_KEYCLOAK_ROLES', roles): raise ApiError("User %s is not authorized" % login, 403) customer = get_customer(login, groups=roles) token = create_token(profile['sub'], profile['name'], login, provider='keycloak', customer=customer, roles=roles) return jsonify(token=token.tokenize)
def gitlab(): access_token_url = current_app.config['GITLAB_URL'] + '/oauth/token' gitlab_api_url = current_app.config['GITLAB_URL'] + '/api/v3' payload = { 'client_id': request.json['clientId'], 'client_secret': current_app.config['OAUTH2_CLIENT_SECRET'], 'redirect_uri': request.json['redirectUri'], 'grant_type': 'authorization_code', 'code': request.json['code'], } try: r = requests.post(access_token_url, data=payload) except Exception: return jsonify(status="error", message="Failed to call Gitlab API over HTTPS") access_token = r.json() r = requests.get(gitlab_api_url+'/user', params=access_token) profile = r.json() r = requests.get(gitlab_api_url+'/groups', params=access_token) groups = [g['path'] for g in r.json()] login = profile['username'] if is_authorized('ALLOWED_GITLAB_GROUPS', groups): raise ApiError("User %s is not authorized" % login, 403) customer = get_customer(login, groups) token = create_token(profile['id'], profile.get('name', '@'+login), login, provider='gitlab', customer=customer, groups=groups, email=profile.get('email', None), email_verified=True if profile.get('email', None) else False) return jsonify(token=token.tokenize)
def github(): if current_app.config['GITHUB_URL']: access_token_url = current_app.config['GITHUB_URL'] + '/login/oauth/access_token' github_api_url = current_app.config['GITHUB_URL'] + '/api/v3' else: access_token_url = 'https://github.com/login/oauth/access_token' github_api_url = 'https://api.github.com' params = { 'client_id': request.json['clientId'], 'redirect_uri': request.json['redirectUri'], 'client_secret': current_app.config['OAUTH2_CLIENT_SECRET'], 'code': request.json['code'] } headers = {'Accept': 'application/json'} r = requests.get(access_token_url, headers=headers, params=params) access_token = r.json() r = requests.get(github_api_url+'/user', params=access_token) profile = r.json() r = requests.get(github_api_url+'/user/orgs', params=access_token) # list public and private Github orgs organizations = [o['login'] for o in r.json()] login = profile['login'] if is_authorized('ALLOWED_GITHUB_ORGS', organizations): raise ApiError("User %s is not authorized" % login, 403) customer = get_customer(login, organizations) token = create_token(profile['id'], profile.get('name', '@'+login), login, provider='github', customer=customer, orgs=organizations, email=profile.get('email', None), email_verified=True if 'email' in profile else False) return jsonify(token=token.tokenize)
def pingfederate(): access_token_url = current_app.config['PINGFEDERATE_OPENID_ACCESS_TOKEN_URL'] payload = { 'client_id': request.json['clientId'], 'client_secret': current_app.config['OAUTH2_CLIENT_SECRET'], 'redirect_uri': request.json['redirectUri'], 'grant_type': 'authorization_code', 'code': request.json['code'], 'scope': 'openid email', } try: r = requests.post(access_token_url, data=payload) except Exception: return jsonify(status="error", message="Failed to call sso API over HTTPS") access_token = r.json() encoded = access_token['access_token'] keyfile = open (current_app.config['PINGFEDERATE_PUBKEY_LOCATION'], 'r') keystring = keyfile.read() decoded = jwt.decode(encoded, keystring, algorithms=current_app.config['PINGFEDERATE_TOKEN_ALGORITHM']) login = decoded[current_app.config['PINGFEDERATE_OPENID_PAYLOAD_USERNAME']] email = decoded[current_app.config['PINGFEDERATE_OPENID_PAYLOAD_EMAIL']] customer = get_customer(login, current_app.config['PINGFEDERATE_OPENID_PAYLOAD_GROUP']) token = create_token(login, email, email, provider='openid', customer=customer) return jsonify(token=token.tokenize)
def saml_response_from_idp(): def _make_response(resp_obj, resp_code): if 'usePostMessage' in request.form.get( 'RelayState', '') and 'text/html' in request.headers.get( 'Accept', ''): origins = current_app.config.get('CORS_ORIGINS', []) response = make_response( '''<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Authenticating...</title> <script type="application/javascript"> var origins = {origins}; // in case when API and WebUI are on the same origin if (origins.indexOf(window.location.origin) < 0) origins.push(window.location.origin); // only one will succeed origins.forEach(origin => window.opener.postMessage({msg_data}, origin)); window.close(); </script> </head> <body></body> </html>'''.format(msg_data=json.dumps(resp_obj), origins=json.dumps(origins)), resp_code) response.headers['Content-Type'] = 'text/html' return response else: return jsonify(**resp_obj), resp_code authn_response = saml_client().parse_authn_request_response( request.form['SAMLResponse'], saml2.entity.BINDING_HTTP_POST) identity = authn_response.get_identity() email = identity['emailAddress'][0] domain = email.split('@')[1] name = (current_app.config.get( 'SAML2_USER_NAME_FORMAT', '{givenName} {surname}')).format( **dict(map(lambda x: (x[0], x[1][0]), identity.items()))) groups = identity.get('groups', []) if is_authorized('ALLOWED_SAML2_GROUPS', groups): return _make_response( { 'status': 'error', 'message': 'User {} is not authorized'.format(email) }, 403) customer = get_customer(email, groups=[domain]) token = create_token(email, name, email, provider='saml2', customer=customer, groups=groups) return _make_response({'status': 'ok', 'token': token.tokenize}, 200)
def keycloak(): if not current_app.config['KEYCLOAK_URL']: return jsonify( status="error", message="Must define KEYCLOAK_URL setting in server configuration." ), 503 access_token_url = "{0}/auth/realms/{1}/protocol/openid-connect/token".format( current_app.config['KEYCLOAK_URL'], current_app.config['KEYCLOAK_REALM']) payload = { 'client_id': request.json['clientId'], 'client_secret': current_app.config['OAUTH2_CLIENT_SECRET'], 'redirect_uri': request.json['redirectUri'], 'grant_type': 'authorization_code', 'code': request.json['code'], } try: r = requests.post(access_token_url, data=payload) except Exception: return jsonify(status="error", message="Failed to call Keycloak API over HTTPS") access_token = r.json() headers = { "Authorization": "{0} {1}".format(access_token['token_type'], access_token['access_token']) } r = requests.get( "{0}/auth/realms/{1}/protocol/openid-connect/userinfo".format( current_app.config['KEYCLOAK_URL'], current_app.config['KEYCLOAK_REALM']), headers=headers) profile = r.json() roles = profile['roles'] login = profile['preferred_username'] if is_authorized('ALLOWED_KEYCLOAK_ROLES', roles): raise ApiError("User %s is not authorized" % login, 403) customer = get_customer(login, groups=roles) token = create_token(profile['sub'], profile['name'], login, provider='keycloak', customer=customer, roles=roles) return jsonify(token=token.tokenize)
def saml_response_from_idp(): def _make_response(resp_obj, resp_code): if 'usePostMessage' in request.form.get('RelayState', '') and 'text/html' in request.headers.get('Accept', ''): origins = current_app.config.get('CORS_ORIGINS', []) response = make_response( '''<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Authenticating...</title> <script type="application/javascript"> var origins = {origins}; // in case when API and WebUI are on the same origin if (origins.indexOf(window.location.origin) < 0) origins.push(window.location.origin); // only one will succeed origins.forEach(origin => window.opener.postMessage({msg_data}, origin)); window.close(); </script> </head> <body></body> </html>'''.format(msg_data=json.dumps(resp_obj), origins=json.dumps(origins)), resp_code ) response.headers['Content-Type'] = 'text/html' return response else: return jsonify(**resp_obj), resp_code authn_response = saml_client().parse_authn_request_response( request.form['SAMLResponse'], saml2.entity.BINDING_HTTP_POST ) identity = authn_response.get_identity() email = identity['emailAddress'][0] domain = email.split('@')[1] name = (current_app.config.get('SAML2_USER_NAME_FORMAT', '{givenName} {surname}')).format(**dict(map(lambda x: (x[0], x[1][0]), identity.items()))) groups = identity.get('groups', []) if is_authorized('ALLOWED_SAML2_GROUPS', groups): return _make_response({'status': 'error', 'message': 'User {} is not authorized'.format(email)}, 403) customer = get_customer(email, groups=[domain]) token = create_token(email, name, email, provider='saml2', customer=customer, groups=groups) return _make_response({'status': 'ok', 'token': token.tokenize}, 200)
def google(): access_token_url = 'https://accounts.google.com/o/oauth2/token' people_api_url = 'https://www.googleapis.com/plus/v1/people/me/openIdConnect' payload = { 'client_id': request.json['clientId'], 'client_secret': current_app.config['OAUTH2_CLIENT_SECRET'], 'redirect_uri': request.json['redirectUri'], 'grant_type': 'authorization_code', 'code': request.json['code'], } r = requests.post(access_token_url, data=payload) token = r.json() id_token = Jwt.parse(token['id_token'], key='', verify=False, algorithm='RS256') domain = id_token.email.split('@')[1] if is_authorized('ALLOWED_EMAIL_DOMAINS', groups=[domain]): raise ApiError("User %s is not authorized" % id_token.email, 403) # Get Google+ profile for Full name headers = {'Authorization': 'Bearer ' + token['access_token']} r = requests.get(people_api_url, headers=headers) profile = r.json() if not profile: raise ApiError("Google+ API is not enabled for this Client ID", 400) customer = get_customer(id_token.email, groups=[domain]) name = profile.get('name', id_token.email.split('@')[0]) token = create_token(id_token.subject, name, id_token.email, provider='google', customer=customer, orgs=[domain], email=id_token.email, email_verified=id_token.email_verified) return jsonify(token=token.tokenize)
def login(): # lookup user from username/email try: username = request.json.get('username', None) or request.json['email'] password = request.json['password'] except KeyError: raise ApiError("must supply 'username' and 'password'", 401) user = User.find_by_email(email=username) if not user: raise ApiError("invalid username or password", 401) if not user.verify_password(password): raise ApiError("invalid username or password", 401) # if email verification is enforced, deny login and send email if current_app.config['EMAIL_VERIFICATION'] and not user.email_verified: hash = str(uuid4()) send_confirmation(user, hash) user.set_email_hash(hash) raise ApiError('email not verified', 401) # check user is active if user.status != 'active': raise ApiError('user not active', 403) # check allowed domain if is_authorized('ALLOWED_EMAIL_DOMAINS', groups=[user.domain]): raise ApiError("unauthorized domain", 403) # assign customer & update last login time customer = get_customer(user.email, groups=[user.domain]) user.update_last_login() # generate token token = create_token(user.id, user.name, user.email, provider='basic', customer=customer, roles=user.roles, email=user.email, email_verified=user.email_verified) return jsonify(token=token.tokenize)
def signup(): try: user = User.parse(request.json) except Exception as e: raise ApiError(str(e), 400) # check allowed domain if is_authorized('ALLOWED_EMAIL_DOMAINS', groups=[user.domain]): raise ApiError("unauthorized domain", 403) if User.find_by_email(email=user.email): raise ApiError("username already exists", 409) try: user = user.create() except Exception as e: ApiError(str(e), 500) # if email verification is enforced, deny login and send email if current_app.config['EMAIL_VERIFICATION'] and not user.email_verified: hash = str(uuid4()) send_confirmation(user, hash) user.set_email_hash(hash) raise ApiError('email not verified', 401) # check user is active if user.status != 'active': raise ApiError('user not active', 403) # assign customer & update last login time customer = get_customer(user.email, groups=[user.domain]) user.update_last_login() # generate token token = create_token(user.id, user.name, user.email, provider='basic', customer=customer, roles=user.roles, email=user.email, email_verified=user.email_verified) return jsonify(token=token.tokenize)
def gitlab(): access_token_url = current_app.config['GITLAB_URL'] + '/oauth/token' gitlab_api_url = current_app.config['GITLAB_URL'] + '/api/v3' payload = { 'client_id': request.json['clientId'], 'client_secret': current_app.config['OAUTH2_CLIENT_SECRET'], 'redirect_uri': request.json['redirectUri'], 'grant_type': 'authorization_code', 'code': request.json['code'], } try: r = requests.post(access_token_url, data=payload) except Exception: return jsonify(status="error", message="Failed to call Gitlab API over HTTPS") access_token = r.json() r = requests.get(gitlab_api_url + '/user', params=access_token) profile = r.json() r = requests.get(gitlab_api_url + '/groups', params=access_token) groups = [g['path'] for g in r.json()] login = profile['username'] if is_authorized('ALLOWED_GITLAB_GROUPS', groups): raise ApiError("User %s is not authorized" % login, 403) customer = get_customer(login, groups) token = create_token( profile['id'], profile.get('name', '@' + login), login, provider='gitlab', customer=customer, groups=groups, email=profile.get('email', None), email_verified=True if profile.get('email', None) else False) return jsonify(token=token.tokenize)