def _validate_user(username, email, sso_mode, org_id, groups, remote_addr, http_redirect=False, yubico_id=None): usr = user.find_user_auth(name=username, auth_type=sso_mode) if not usr: org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) else: if usr.org_id != org_id: logger.info( 'User organization changed, moving user', 'sso', user_name=username, user_email=email, remote_ip=remote_addr, cur_org_id=usr.org_id, new_org_id=org_id, ) org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr.remove() old_org_id = usr.org_id new_usr = org.new_user( name=usr.name, email=usr.email, pin=usr.pin, type=usr.type, groups=usr.groups, auth_type=usr.auth_type, yubico_id=usr.yubico_id, disabled=usr.disabled, resource_id=usr.resource_id, bypass_secondary=usr.bypass_secondary, client_to_client=usr.client_to_client, mac_addresses=usr.mac_addresses, dns_servers=usr.dns_servers, dns_suffix=usr.dns_suffix, port_forwarding=usr.port_forwarding, ) new_usr.otp_secret = usr.otp_secret usr = new_usr usr.commit() event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=old_org_id) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, yubico_id=yubico_id, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=remote_addr) journal.entry( journal.USER_CREATE, usr.journal_data, event_long='User created with single sign-on', remote_address=remote_addr, ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if yubico_id and usr.yubico_id and yubico_id != usr.yubico_id: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_INVALID_YUBIKEY, reason_long='Invalid username', ) return utils.jsonify( { 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) if usr.disabled: return flask.abort(403) changed = False if yubico_id and not usr.yubico_id: changed = True usr.yubico_id = yubico_id usr.commit('yubico_id') if groups and groups - set(usr.groups or []): changed = True usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: changed = True usr.auth_type = sso_mode usr.commit('auth_type') usr.clear_auth_cache() usr.disconnect() if changed: event.Event(type=USERS_UPDATED, resource_id=org.id) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=remote_addr, ) journal.entry( journal.SSO_AUTH_SUCCESS, usr.journal_data, key_id_hash=hashlib.md5(key_link['id'].encode()).hexdigest(), remote_address=remote_addr, ) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User profile viewed from single sign-on', ) if http_redirect: return utils.redirect(utils.get_url_root() + key_link['view_url']) else: return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def _validate_user(username, email, sso_mode, org_id, groups, http_redirect=False, yubico_id=None): usr = user.find_user_auth(name=username, auth_type=sso_mode) if not usr: org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) else: if usr.org_id != org_id: logger.info( 'User organization changed, moving user', 'sso', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), cur_org_id=usr.org_id, new_org_id=org_id, ) org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr.remove() old_org_id = usr.org_id usr = org.new_user( name=usr.name, email=usr.email, type=usr.type, groups=usr.groups, auth_type=usr.auth_type, yubico_id=usr.yubico_id, disabled=usr.disabled, bypass_secondary=usr.bypass_secondary, client_to_client=usr.client_to_client, dns_servers=usr.dns_servers, dns_suffix=usr.dns_suffix, port_forwarding=usr.port_forwarding, ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=old_org_id) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, yubico_id=yubico_id, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if yubico_id and usr.yubico_id and yubico_id != usr.yubico_id: return utils.jsonify( { 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) if usr.disabled: return flask.abort(403) changed = False if yubico_id and not usr.yubico_id: changed = True usr.yubico_id = yubico_id usr.commit('yubico_id') if groups and groups - set(usr.groups or []): changed = True usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: changed = True usr.auth_type = sso_mode usr.commit('auth_type') if changed: event.Event(type=USERS_UPDATED, resource_id=org.id) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) if http_redirect: return utils.redirect(utils.get_url_root() + key_link['view_url']) else: return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def _validate_user(username, email, sso_mode, org_id, groups, remote_addr, http_redirect=False, yubico_id=None): usr = user.find_user_auth(name=username, auth_type=sso_mode) if not usr: org = organization.get_by_id(org_id) if not org: logger.error('Organization for sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) else: if usr.org_id != org_id: logger.info('User organization changed, moving user', 'sso', user_name=username, user_email=email, remote_ip=remote_addr, cur_org_id=usr.org_id, new_org_id=org_id, ) org = organization.get_by_id(org_id) if not org: logger.error('Organization for sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr.remove() old_org_id = usr.org_id usr = org.new_user( name=usr.name, email=usr.email, type=usr.type, groups=usr.groups, auth_type=usr.auth_type, yubico_id=usr.yubico_id, disabled=usr.disabled, bypass_secondary=usr.bypass_secondary, client_to_client=usr.client_to_client, dns_servers=usr.dns_servers, dns_suffix=usr.dns_suffix, port_forwarding=usr.port_forwarding, ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=old_org_id) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, yubico_id=yubico_id, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=remote_addr) journal.entry( journal.USER_CREATE, usr.journal_data, event_long='User created with single sign-on', remote_address=remote_addr, ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if yubico_id and usr.yubico_id and yubico_id != usr.yubico_id: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_INVALID_YUBIKEY, reason_long='Invalid username', ) return utils.jsonify({ 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) if usr.disabled: return flask.abort(403) changed = False if yubico_id and not usr.yubico_id: changed = True usr.yubico_id = yubico_id usr.commit('yubico_id') if groups and groups - set(usr.groups or []): changed = True usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: changed = True usr.auth_type = sso_mode usr.commit('auth_type') if changed: event.Event(type=USERS_UPDATED, resource_id=org.id) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=remote_addr, ) journal.entry( journal.SSO_AUTH_SUCCESS, usr.journal_data, key_id_hash=hashlib.md5(key_link['id']).hexdigest(), remote_address=remote_addr, ) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User profile viewed from single sign-on', ) if http_redirect: return utils.redirect(utils.get_url_root() + key_link['view_url']) else: return utils.jsonify({ 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def sso_duo_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None passcode = utils.filter_str(flask.request.json.get('passcode')) or '' if sso_mode not in (DUO_AUTH, GOOGLE_DUO_AUTH, SLACK_DUO_AUTH, SAML_DUO_AUTH, SAML_OKTA_DUO_AUTH, SAML_ONELOGIN_DUO_AUTH, RADIUS_DUO_AUTH): return flask.abort(404) if not token: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_one({ '_id': token, }) if not doc or doc['_id'] != token or doc['type'] != DUO_AUTH: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = doc['groups'] if settings.app.sso_duo_mode == 'passcode': duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', passcode=passcode, ) valid = duo_auth.authenticate() if not valid: logger.error('Duo authentication not valid', 'sso', username=username, ) return utils.jsonify({ 'error': PASSCODE_INVALID, 'error_msg': PASSCODE_INVALID_MSG, }, 401) else: duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', ) valid = duo_auth.authenticate() if not valid: logger.error('Duo authentication not valid', 'sso', username=username, ) return utils.jsonify({ 'error': DUO_FAILED, 'error_msg': DUO_FAILED_MSG, }, 401) valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='duo', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error('Duo plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) groups = ((groups or set()) | (groups2 or set())) or None usr = user.find_user_auth(name=username, auth_type=sso_mode) if not usr: org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) else: org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify({ 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def sso_callback_get(): sso_mode = settings.app.sso if sso_mode not in (GOOGLE_AUTH, GOOGLE_DUO_AUTH, GOOGLE_YUBICO_AUTH, SLACK_AUTH, SLACK_DUO_AUTH, SLACK_YUBICO_AUTH, SAML_AUTH, SAML_DUO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_AUTH, SAML_OKTA_DUO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_AUTH, SAML_ONELOGIN_DUO_AUTH, SAML_ONELOGIN_YUBICO_AUTH): return flask.abort(405) state = flask.request.args.get('state') sig = flask.request.args.get('sig') tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_and_modify(query={ '_id': state, }, remove=True) if not doc: return flask.abort(404) query = flask.request.query_string.split('&sig=')[0] test_sig = base64.urlsafe_b64encode(hmac.new(str(doc['secret']), query, hashlib.sha512).digest()) if not utils.const_compare(sig, test_sig): return flask.abort(401) params = urlparse.parse_qs(query) if doc.get('type') == SAML_AUTH: username = params.get('username')[0] email = params.get('email', [None])[0] org_name = params.get('org', [None])[0] if not username: return flask.abort(406) org_id = settings.app.sso_org if org_name: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='saml', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), sso_org_names=[org_name], ) if valid: org_id = org_id_new or org_id else: logger.error('Saml plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) elif doc.get('type') == SLACK_AUTH: username = params.get('username')[0] email = None user_team = params.get('team')[0] org_names = params.get('orgs', [''])[0] org_names = org_names.split(',') valid = sso.verify_slack(username, user_team) if not valid: return flask.abort(401) org_id = settings.app.sso_org for org_name in org_names: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id break valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='slack', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), sso_org_names=org_names, ) if valid: org_id = org_id_new or org_id else: logger.error('Slack plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) else: username = params.get('username')[0] email = username valid = sso.verify_google(username) if not valid: return flask.abort(401) org_id = settings.app.sso_org valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='google', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error('Google plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) if DUO_AUTH in sso_mode: token = utils.generate_secret() tokens_collection = mongo.get_collection('sso_tokens') tokens_collection.insert({ '_id': token, 'type': DUO_AUTH, 'username': username, 'email': email, 'org_id': org_id, 'groups': groups, 'timestamp': utils.now(), }) duo_page = static.StaticFile(settings.conf.www_path, 'duo.html', cache=False, gzip=False) sso_duo_mode = settings.app.sso_duo_mode if sso_duo_mode == 'passcode': duo_mode = 'passcode' elif sso_duo_mode == 'phone': duo_mode = 'phone' else: duo_mode = 'push' body_class = duo_mode if settings.app.theme == 'dark': body_class += ' dark' duo_page.data = duo_page.data.replace('<%= body_class %>', body_class) duo_page.data = duo_page.data.replace('<%= token %>', token) duo_page.data = duo_page.data.replace('<%= duo_mode %>', duo_mode) return duo_page.get_response() if YUBICO_AUTH in sso_mode: token = utils.generate_secret() tokens_collection = mongo.get_collection('sso_tokens') tokens_collection.insert({ '_id': token, 'type': YUBICO_AUTH, 'username': username, 'email': email, 'org_id': org_id, 'groups': groups, 'timestamp': utils.now(), }) yubico_page = static.StaticFile(settings.conf.www_path, 'yubico.html', cache=False, gzip=False) if settings.app.theme == 'dark': yubico_page.data = yubico_page.data.replace( '<body>', '<body class="dark">') yubico_page.data = yubico_page.data.replace('<%= token %>', token) return yubico_page.get_response() usr = user.find_user_auth(name=username, auth_type=sso_mode) if not usr: org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) else: org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.redirect(utils.get_url_root() + key_link['view_url'])
def sso_authenticate_post(): if settings.app.sso != DUO_AUTH or \ settings.app.sso_duo_mode == 'passcode': return flask.abort(405) username = utils.json_filter_str('username') usernames = [username] email = None if '@' in username: email = username usernames.append(username.split('@')[0]) valid = False for i, username in enumerate(usernames): try: duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', ) valid = duo_auth.authenticate() break except InvalidUser: if i == len(usernames) - 1: logger.error('Invalid duo username', 'sso', username=username, ) if valid: valid, org_id, groups = sso.plugin_sso_authenticate( sso_type='duo', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if not valid: logger.error('Duo plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) else: logger.error('Duo authentication not valid', 'sso', username=username, ) return flask.abort(401) if not org_id: org_id = settings.app.sso_org usr = user.find_user_auth(name=username, auth_type=DUO_AUTH) if not usr: org = organization.get_by_id(org_id) if not org: logger.error('Organization for Duo sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) else: org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=DUO_AUTH, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != DUO_AUTH: usr.auth_type = DUO_AUTH usr.commit('auth_type') event.Event(type=USERS_UPDATED, resource_id=org.id) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.get_url_root() + key_link['view_url']
def sso_yubico_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None key = utils.filter_str(flask.request.json.get('key')) or None if sso_mode not in (GOOGLE_YUBICO_AUTH, SLACK_YUBICO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_YUBICO_AUTH): return flask.abort(404) if not token or not key: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_one({ '_id': token, }) if not doc or doc['_id'] != token or doc['type'] != YUBICO_AUTH: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = doc['groups'] valid, yubico_id = sso.auth_yubico(key) if not valid: return utils.jsonify({ 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) usr = user.find_user_auth(name=username, auth_type=sso_mode) if not usr: org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) else: org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, yubico_id=yubico_id, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if yubico_id != usr.yubico_id: return utils.jsonify({ 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify({ 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)