def _check_sso(self): if self.user.bypass_secondary or settings.vpn.stress_test: return if not self.user.sso_auth_check(self.password, self.remote_ip): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'Single sign-on authentication failed') % (self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed secondary authentication', ) raise AuthError('Failed secondary authentication') if not self.server.check_groups(self.user.groups): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. User not in ' + 'servers groups') % (self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='User not in servers groups', ) raise AuthError('User not in servers groups')
def get_link_user(self, org_ids): from pritunl import organization for org_id in org_ids: org = organization.get_by_id(org_id) if not org: continue usr = org.find_user(resource_id=self.id) if not usr: logger.info('Creating host link user', 'host', host_id=self.id, ) usr = org.new_user(name=HOST_USER_PREFIX + str(self.id), type=CERT_SERVER, resource_id=self.id) journal.entry( journal.USER_CREATE, usr.journal_data, event_long='User created for host linking', ) usr.audit_event('user_created', 'User created for host linking') return usr raise ValueError('No orgs exists in link server')
def user_uri_key_page_get(short_code): remote_addr = utils.get_remote_addr() doc = _find_doc({ 'short_id': short_code, }, one_time=True) if not doc: return flask.abort(404) org = organization.get_by_id(doc['org_id']) usr = org.get_user(id=doc['user_id']) if usr.disabled: return flask.abort(403) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User temporary profile downloaded from pritunl client', ) usr.audit_event('user_profile', 'User temporary profile downloaded from pritunl client', remote_addr=remote_addr, ) keys = {} for server in usr.iter_servers(): key = usr.build_key_conf(server.id) keys[key['name']] = key['conf'] return utils.jsonify(keys)
def admin_delete(admin_id): if settings.app.demo_mode: return utils.demo_blocked() if not flask.g.administrator.super_user: return utils.jsonify({ 'error': REQUIRES_SUPER_USER, 'error_msg': REQUIRES_SUPER_USER_MSG, }, 400) admin = auth.get_by_id(admin_id) remote_addr = utils.get_remote_addr() if admin.super_user and auth.super_user_count() < 2: return utils.jsonify({ 'error': NO_ADMINS, 'error_msg': NO_ADMINS_MSG, }, 400) journal.entry( journal.ADMIN_DELETE, admin.journal_data, event_long='Administrator deleted', remote_addr=remote_addr, ) admin.remove() event.Event(type=ADMINS_UPDATED) return utils.jsonify({})
def admin_delete(admin_id): if settings.app.demo_mode: return utils.demo_blocked() if not flask.g.administrator.super_user: return utils.jsonify( { 'error': REQUIRES_SUPER_USER, 'error_msg': REQUIRES_SUPER_USER_MSG, }, 400) admin = auth.get_by_id(admin_id) remote_addr = utils.get_remote_addr() if admin.super_user and auth.super_user_count() < 2: return utils.jsonify({ 'error': NO_ADMINS, 'error_msg': NO_ADMINS_MSG, }, 400) journal.entry( journal.ADMIN_DELETE, admin.journal_data, event_long='Administrator deleted', remote_addr=remote_addr, ) admin.remove() event.Event(type=ADMINS_UPDATED) return utils.jsonify({})
def user_delete(org_id, user_id): if settings.app.demo_mode: return utils.demo_blocked() remote_addr = utils.get_remote_addr() org = organization.get_by_id(org_id) user = org.get_user(user_id) name = user.name journal.entry( journal.USER_DELETE, user.journal_data, event_long='User deleted', remote_address=remote_addr, ) user.remove() event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) user.clear_auth_cache() user.disconnect() logger.LogEntry(message='Deleted user "%s".' % name) return utils.jsonify({})
def user_otp_secret_put(org_id, user_id): if settings.app.demo_mode: return utils.demo_blocked() org = organization.get_by_id(org_id) user = org.get_user(user_id) remote_addr = utils.get_remote_addr() user.audit_event( 'user_updated', 'User two step secret reset', remote_addr=remote_addr, ) journal.entry( journal.USER_UPDATE, user.journal_data, event_long='User two step secret reset', remote_address=remote_addr, ) user.generate_otp_secret() user.commit() event.Event(type=USERS_UPDATED, resource_id=org.id) return utils.jsonify(user.dict())
def user_linked_key_conf_get(org_id, user_id, server_id): remote_addr = utils.get_remote_addr() org = organization.get_by_id(org_id) usr = org.get_user(user_id) key_conf = usr.build_key_conf(server_id) usr.audit_event( 'user_profile', 'User key profile downloaded from web console', remote_addr=remote_addr, ) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User key profile downloaded from web console', ) response = flask.Response(response=key_conf['conf'], mimetype='application/ovpn') response.headers.add('Content-Disposition', 'attachment; filename="%s"' % key_conf['name']) return response
def _check_sso(self): if self.user.bypass_secondary or settings.vpn.stress_test: return if not self.user.sso_auth_check(self.password, self.remote_ip): self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'Single sign-on authentication failed') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed secondary authentication', ) raise AuthError('Failed secondary authentication') if not self.server.check_groups(self.user.groups): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. User not in ' + 'servers groups') % (self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='User not in servers groups', ) raise AuthError('User not in servers groups')
def user_uri_key_page_get(short_code): remote_addr = utils.get_remote_addr() doc = _find_doc({ 'short_id': short_code, }, one_time=True) if not doc: return flask.abort(404) org = organization.get_by_id(doc['org_id']) usr = org.get_user(id=doc['user_id']) if usr.disabled: return flask.abort(403) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User temporary profile downloaded from pritunl client', ) usr.audit_event( 'user_profile', 'User temporary profile downloaded from pritunl client', remote_addr=remote_addr, ) keys = {} for server in usr.iter_servers(): key = usr.build_key_conf(server.id) keys[key['name']] = key['conf'] return utils.jsonify(keys)
def sso_yubico_post(): remote_addr = utils.get_remote_addr() 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_and_modify(query={ '_id': token, }, remove=True) if not doc or doc['_id'] != token or doc['type'] != YUBICO_AUTH: journal.entry( journal.SSO_AUTH_FAILURE, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_INVALID_TOKEN, reason_long='Invalid Yubikey authentication token', ) return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = set(doc['groups'] or []) valid, yubico_id = sso.auth_yubico(key) if not valid or not yubico_id: journal.entry( journal.SSO_AUTH_FAILURE, username=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_YUBIKEY_FAILED, reason_long='Yubikey authentication failed', ) return utils.jsonify({ 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) return _validate_user(username, email, sso_mode, org_id, groups, remote_addr, yubico_id=yubico_id)
def sso_yubico_post(): remote_addr = utils.get_remote_addr() 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 (AZURE_YUBICO_AUTH, GOOGLE_YUBICO_AUTH, AUTHZERO_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_and_modify(query={ '_id': token, }, remove=True) if not doc or doc['_id'] != token or doc['type'] != YUBICO_AUTH: journal.entry( journal.SSO_AUTH_FAILURE, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_INVALID_TOKEN, reason_long='Invalid Yubikey authentication token', ) return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = set(doc['groups'] or []) valid, yubico_id = sso.auth_yubico(key) if not valid or not yubico_id: journal.entry( journal.SSO_AUTH_FAILURE, username=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_YUBIKEY_FAILED, reason_long='Yubikey authentication failed', ) return utils.jsonify({ 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) return _validate_user(username, email, sso_mode, org_id, groups, remote_addr, yubico_id=yubico_id)
def admin_post(): if settings.app.demo_mode: return utils.demo_blocked() if not flask.g.administrator.super_user: return utils.jsonify( { 'error': REQUIRES_SUPER_USER, 'error_msg': REQUIRES_SUPER_USER_MSG, }, 400) username = utils.filter_str(flask.request.json['username']).lower() password = flask.request.json['password'] yubikey_id = flask.request.json.get('yubikey_id') or None yubikey_id = yubikey_id[:12] if yubikey_id else None otp_auth = flask.request.json.get('otp_auth', False) auth_api = flask.request.json.get('auth_api', False) disabled = flask.request.json.get('disabled', False) super_user = flask.request.json.get('super_user', False) remote_addr = utils.get_remote_addr() try: admin = auth.new_admin( username=username, password=password, yubikey_id=yubikey_id, default=True, otp_auth=otp_auth, auth_api=auth_api, disabled=disabled, super_user=super_user, ) except pymongo.errors.DuplicateKeyError: return utils.jsonify( { 'error': ADMIN_USERNAME_EXISTS, 'error_msg': ADMIN_USERNAME_EXISTS_MSG, }, 400) admin.audit_event( 'admin_created', 'Administrator created', remote_addr=remote_addr, ) journal.entry( journal.ADMIN_CREATE, admin.journal_data, event_long='Administrator created', remote_addr=remote_addr, ) event.Event(type=ADMINS_UPDATED) return utils.jsonify(admin.dict())
def admin_post(): if settings.app.demo_mode: return utils.demo_blocked() if not flask.g.administrator.super_user: return utils.jsonify({ 'error': REQUIRES_SUPER_USER, 'error_msg': REQUIRES_SUPER_USER_MSG, }, 400) username = utils.filter_str(flask.request.json['username']).lower() password = flask.request.json['password'] yubikey_id = flask.request.json.get('yubikey_id') or None yubikey_id = yubikey_id[:12] if yubikey_id else None otp_auth = flask.request.json.get('otp_auth', False) auth_api = flask.request.json.get('auth_api', False) disabled = flask.request.json.get('disabled', False) super_user = flask.request.json.get('super_user', False) remote_addr = utils.get_remote_addr() try: admin = auth.new_admin( username=username, password=password, yubikey_id=yubikey_id, default=True, otp_auth=otp_auth, auth_api=auth_api, disabled=disabled, super_user=super_user, ) except pymongo.errors.DuplicateKeyError: return utils.jsonify({ 'error': ADMIN_USERNAME_EXISTS, 'error_msg': ADMIN_USERNAME_EXISTS_MSG, }, 400) admin.audit_event('admin_created', 'Administrator created', remote_addr=remote_addr, ) journal.entry( journal.ADMIN_CREATE, admin.journal_data, event_long='Administrator created', remote_addr=remote_addr, ) event.Event(type=ADMINS_UPDATED) return utils.jsonify(admin.dict())
def _callback(self, allow, reason=None): if allow: journal.entry( journal.USER_CONNECT_SUCCESS, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='User connected', ) try: self._check_call(self._update_token) except: return self.callback(allow, reason)
def user_linked_key_page_delete(short_code): utils.rand_sleep() remote_addr = utils.get_remote_addr() journal.entry( journal.USER_PROFILE_DELETE, remote_address=remote_addr, event_long='Temporary profile link deleted', ) collection = mongo.get_collection('users_key_link') collection.remove({ 'short_id': short_code, }) return utils.jsonify({})
def user_key_onc_archive_get(org_id, user_id): remote_addr = utils.get_remote_addr() usr, resp = _get_onc_archive(org_id, user_id) usr.audit_event('user_profile', 'User onc profile downloaded from web console', remote_addr=remote_addr, ) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User onc profile downloaded from web console', ) return resp
def user_key_tar_archive_get(org_id, user_id): remote_addr = utils.get_remote_addr() usr, resp = _get_key_tar_archive(org_id, user_id) usr.audit_event('user_profile', 'User tar profile downloaded from web console', remote_addr=remote_addr, ) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User tar profile downloaded from web console', ) return resp
def user_key_link_get(org_id, user_id): remote_addr = utils.get_remote_addr() org = organization.get_by_id(org_id) usr = org.get_user(user_id) usr.audit_event('user_profile', 'User temporary profile links created from web console', remote_addr=remote_addr, ) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User temporary profile links created from web console', ) return utils.jsonify(org.create_user_key_link(user_id))
def auth_delete(): admin_id = utils.session_opt_str('admin_id') session_id = utils.session_opt_str('session_id') remote_addr = utils.get_remote_addr() journal.entry( journal.ADMIN_SESSION_END, admin_id=admin_id, session_id=session_id, remote_address=remote_addr, ) if admin_id and session_id: admin_id = utils.ObjectId(admin_id) auth.clear_session(admin_id, str(session_id)) flask.session.clear() return utils.jsonify({ 'authenticated': False, })
def user_linked_key_conf_get(org_id, user_id, server_id): remote_addr = utils.get_remote_addr() org = organization.get_by_id(org_id) usr = org.get_user(user_id) key_conf = usr.build_key_conf(server_id) usr.audit_event('user_profile', 'User key profile downloaded from web console', remote_addr=remote_addr, ) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User key profile downloaded from web console', ) response = flask.Response(response=key_conf['conf'], mimetype='application/ovpn') response.headers.add('Content-Disposition', 'attachment; filename="%s"' % key_conf['name']) return response
def user_linked_key_onc_archive_get(key_id): key_id = key_id[:128] remote_addr = utils.get_remote_addr() doc = _find_doc({ 'key_id': key_id, }) if not doc: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='Key ID not found', ) return flask.abort(404) if settings.user.restrict_import: return flask.abort(404) usr, resp = _get_onc_archive(doc['org_id'], doc['user_id']) if usr.disabled: journal.entry( journal.USER_PROFILE_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User onc profile downloaded with temporary profile link', ) usr.audit_event( 'user_profile', 'User onc profile downloaded with temporary profile link', remote_addr=remote_addr, ) return resp
def user_linked_key_onc_archive_get(key_id): remote_addr = utils.get_remote_addr() doc = _find_doc({ 'key_id': key_id, }) if not doc: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='Key ID not found', ) return flask.abort(404) usr, resp = _get_onc_archive(doc['org_id'], doc['user_id']) if usr.disabled: journal.entry( journal.USER_PROFILE_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User onc profile downloaded with temporary profile link', ) usr.audit_event('user_profile', 'User onc profile downloaded with temporary profile link', remote_addr=remote_addr, ) return resp
def sso_callback_get(): sso_mode = settings.app.sso if sso_mode not in (AZURE_AUTH, AZURE_DUO_AUTH, AZURE_YUBICO_AUTH, GOOGLE_AUTH, GOOGLE_DUO_AUTH, GOOGLE_YUBICO_AUTH, AUTHZERO_AUTH, AUTHZERO_DUO_AUTH, AUTHZERO_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) remote_addr = utils.get_remote_addr() 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): journal.entry( journal.SSO_AUTH_FAILURE, state=state, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_INVALID_CALLBACK, reason_long='Signature mismatch', ) return flask.abort(401) params = urlparse.parse_qs(query) if doc.get('type') == SAML_AUTH: username = params.get('username')[0].lower() email = params.get('email', [None])[0] org_names = [] if params.get('org'): org_names_param = params.get('org')[0] if ';' in org_names_param: org_names = org_names_param.split(';') else: org_names = org_names_param.split(',') org_names = [x for x in org_names if x] org_names = sorted(org_names) groups = [] if params.get('groups'): groups_param = params.get('groups')[0] if ';' in groups_param: groups = groups_param.split(';') else: groups = groups_param.split(',') groups = [x for x in groups if x] groups = set(groups) if not username: return flask.abort(406) org_id = settings.app.sso_org if org_names: not_found = False for org_name in org_names: org = organization.get_by_name( utils.filter_unicode(org_name), fields=('_id'), ) if org: not_found = False org_id = org.id break else: not_found = True if not_found: logger.warning( 'Supplied org names do not exists', 'sso', sso_type=doc.get('type'), user_name=username, user_email=email, org_names=org_names, ) valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='saml', user_name=username, user_email=email, remote_ip=remote_addr, sso_org_names=org_names, ) if valid: org_id = org_id_new or org_id else: logger.error( 'Saml plugin authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Saml plugin authentication failed', ) return flask.abort(401) groups = groups | set(groups2 or []) elif doc.get('type') == SLACK_AUTH: username = params.get('username')[0].lower() email = None user_team = params.get('team')[0] org_names = params.get('orgs', [''])[0] org_names = sorted(org_names.split(',')) if user_team != settings.app.sso_match[0]: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_SLACK_FAILED, reason_long='Slack team not valid', ) return flask.abort(401) not_found = False org_id = settings.app.sso_org for org_name in org_names: org = organization.get_by_name( utils.filter_unicode(org_name), fields=('_id'), ) if org: not_found = False org_id = org.id break else: not_found = True if not_found: logger.warning( 'Supplied org names do not exists', 'sso', sso_type=doc.get('type'), user_name=username, user_email=email, org_names=org_names, ) valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='slack', user_name=username, user_email=email, remote_ip=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, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Slack plugin authentication failed', ) return flask.abort(401) groups = set(groups or []) elif doc.get('type') == GOOGLE_AUTH: username = params.get('username')[0].lower() email = username valid, google_groups = sso.verify_google(username) if not valid: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_GOOGLE_FAILED, reason_long='Google authentication failed', ) 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=remote_addr, ) if valid: org_id = org_id_new or org_id else: logger.error( 'Google plugin authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Google plugin authentication failed', ) return flask.abort(401) groups = set(groups or []) if settings.app.sso_google_mode == 'groups': groups = groups | set(google_groups) else: not_found = False google_groups = sorted(google_groups) for org_name in google_groups: org = organization.get_by_name( utils.filter_unicode(org_name), fields=('_id'), ) if org: not_found = False org_id = org.id break else: not_found = True if not_found: logger.warning( 'Supplied org names do not exists', 'sso', sso_type=doc.get('type'), user_name=username, user_email=email, org_names=google_groups, ) elif doc.get('type') == AZURE_AUTH: username = params.get('username')[0].lower() email = None tenant, username = username.split('/', 2) if tenant != settings.app.sso_azure_directory_id: logger.error( 'Azure directory ID mismatch', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, azure_tenant=tenant, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_AZURE_FAILED, reason_long='Azure directory ID mismatch', ) return flask.abort(401) valid, azure_groups = sso.verify_azure(username) if not valid: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_AZURE_FAILED, reason_long='Azure authentication failed', ) return flask.abort(401) org_id = settings.app.sso_org valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='azure', user_name=username, user_email=email, remote_ip=remote_addr, ) if valid: org_id = org_id_new or org_id else: logger.error( 'Azure plugin authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Azure plugin authentication failed', ) return flask.abort(401) groups = set(groups or []) if settings.app.sso_azure_mode == 'groups': groups = groups | set(azure_groups) else: not_found = False azure_groups = sorted(azure_groups) for org_name in azure_groups: org = organization.get_by_name( utils.filter_unicode(org_name), fields=('_id'), ) if org: not_found = False org_id = org.id break else: not_found = True if not_found: logger.warning( 'Supplied org names do not exists', 'sso', sso_type=doc.get('type'), user_name=username, user_email=email, org_names=azure_groups, ) elif doc.get('type') == AUTHZERO_AUTH: username = params.get('username')[0].lower() email = None valid, authzero_groups = sso.verify_authzero(username) if not valid: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_AUTHZERO_FAILED, reason_long='Auth0 authentication failed', ) return flask.abort(401) org_id = settings.app.sso_org valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='authzero', user_name=username, user_email=email, remote_ip=remote_addr, ) if valid: org_id = org_id_new or org_id else: logger.error( 'Auth0 plugin authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Auth0 plugin authentication failed', ) return flask.abort(401) groups = set(groups or []) if settings.app.sso_authzero_mode == 'groups': groups = groups | set(authzero_groups) else: not_found = False authzero_groups = sorted(authzero_groups) for org_name in authzero_groups: org = organization.get_by_name( utils.filter_unicode(org_name), fields=('_id'), ) if org: not_found = False org_id = org.id break else: not_found = True if not_found: logger.warning( 'Supplied org names do not exists', 'sso', sso_type=doc.get('type'), user_name=username, user_email=email, org_names=authzero_groups, ) else: logger.error( 'Unknown sso type', 'sso', sso_type=doc.get('type'), ) 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': list(groups) if groups else None, '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': list(groups) if groups else None, '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() return _validate_user(username, email, sso_mode, org_id, groups, remote_addr, http_redirect=True)
def admin_put(admin_id): if settings.app.demo_mode: return utils.demo_blocked() if not flask.g.administrator.super_user: return utils.jsonify({ 'error': REQUIRES_SUPER_USER, 'error_msg': REQUIRES_SUPER_USER_MSG, }, 400) admin = auth.get_by_id(admin_id) remote_addr = utils.get_remote_addr() if 'username' in flask.request.json: username = utils.filter_str(flask.request.json['username']) or \ 'undefined' if username: username = username.lower() if username != admin.username: admin.audit_event('admin_updated', 'Administrator username changed', remote_addr=remote_addr, ) journal.entry( journal.ADMIN_UPDATE, admin.journal_data, event_long='Administrator username changed', remote_addr=remote_addr, ) admin.username = username if 'password' in flask.request.json and flask.request.json['password']: password = flask.request.json['password'] if password != admin.password: admin.audit_event('admin_updated', 'Administrator password changed', remote_addr=remote_addr, ) journal.entry( journal.ADMIN_UPDATE, admin.journal_data, event_long='Administrator password changed', remote_addr=remote_addr, ) admin.password = password if 'yubikey_id' in flask.request.json: yubikey_id = flask.request.json['yubikey_id'] or None if yubikey_id != admin.yubikey_id: admin.audit_event('admin_updated', 'Administrator YubiKey ID changed', remote_addr=remote_addr, ) journal.entry( journal.ADMIN_UPDATE, admin.journal_data, event_long='Administrator YubiKey ID changed', remote_addr=remote_addr, ) admin.yubikey_id = yubikey_id[:12] if yubikey_id else None super_user = flask.request.json.get('super_user') if super_user is not None: if super_user != admin.super_user: if not super_user and auth.super_user_count() < 2: return utils.jsonify({ 'error': NO_SUPER_USERS, 'error_msg': NO_SUPER_USERS_MSG, }, 400) admin.audit_event('admin_updated', 'Administrator super user %s' % ( 'disabled' if super_user else 'enabled'), remote_addr=remote_addr, ) journal.entry( journal.ADMIN_UPDATE, admin.journal_data, event_long='Administrator super user %s' % ( 'disabled' if super_user else 'enabled'), remote_addr=remote_addr, ) admin.super_user = super_user auth_api = flask.request.json.get('auth_api') if auth_api is not None: if auth_api != admin.auth_api: if not auth_api: admin.token = None admin.secret = None elif not admin.token or not admin.secret: admin.generate_token() admin.generate_secret() admin.audit_event('admin_updated', 'Administrator token authentication %s' % ( 'disabled' if auth_api else 'enabled'), remote_addr=remote_addr, ) journal.entry( journal.ADMIN_UPDATE, admin.journal_data, event_long='Administrator token authentication %s' % ( 'disabled' if auth_api else 'enabled'), remote_addr=remote_addr, ) admin.auth_api = auth_api if 'token' in flask.request.json and flask.request.json['token']: admin.generate_token() admin.audit_event('admin_updated', 'Administrator api token changed', remote_addr=remote_addr, ) journal.entry( journal.ADMIN_UPDATE, admin.journal_data, event_long='Administrator api token changed', remote_addr=remote_addr, ) if 'secret' in flask.request.json and flask.request.json['secret']: admin.generate_secret() admin.audit_event('admin_updated', 'Administrator api secret changed', remote_addr=remote_addr, ) journal.entry( journal.ADMIN_UPDATE, admin.journal_data, event_long='Administrator api secret changed', remote_addr=remote_addr, ) disabled = flask.request.json.get('disabled') if disabled is not None: if disabled != admin.disabled: if disabled and admin.super_user and auth.super_user_count() < 2: return utils.jsonify({ 'error': NO_ADMINS_ENABLED, 'error_msg': NO_ADMINS_ENABLED_MSG, }, 400) admin.audit_event('admin_updated', 'Administrator %s' % ('disabled' if disabled else 'enabled'), remote_addr=remote_addr, ) journal.entry( journal.ADMIN_UPDATE, admin.journal_data, event_long='Administrator %s' % ( 'disabled' if disabled else 'enabled'), remote_addr=remote_addr, ) admin.disabled = disabled otp_auth = flask.request.json.get('otp_auth') if otp_auth is not None: if otp_auth != admin.otp_auth: if not otp_auth: admin.otp_secret = None elif not admin.otp_secret: admin.generate_otp_secret() admin.audit_event('admin_updated', 'Administrator two-step authentication %s' % ( 'disabled' if otp_auth else 'enabled'), remote_addr=remote_addr, ) journal.entry( journal.ADMIN_UPDATE, admin.journal_data, event_long='Administrator two-step authentication %s' % ( 'disabled' if otp_auth else 'enabled'), remote_addr=remote_addr, ) admin.otp_auth = otp_auth otp_secret = flask.request.json.get('otp_secret') if otp_secret == True: admin.audit_event('admin_updated', 'Administrator two-factor authentication secret reset', remote_addr=remote_addr, ) journal.entry( journal.ADMIN_UPDATE, admin.journal_data, event_long='Administrator two-factor authentication secret reset', remote_addr=remote_addr, ) admin.generate_otp_secret() try: admin.commit() except pymongo.errors.DuplicateKeyError: return utils.jsonify({ 'error': ADMIN_USERNAME_EXISTS, 'error_msg': ADMIN_USERNAME_EXISTS_MSG, }, 400) event.Event(type=ADMINS_UPDATED) return utils.jsonify(admin.dict())
def user_put(org_id, user_id): if settings.app.demo_mode: return utils.demo_blocked() org = organization.get_by_id(org_id) user = org.get_user(user_id) reset_user = False reset_user_cache = False port_forwarding_event = False remote_addr = utils.get_remote_addr() if 'name' in flask.request.json: name = utils.filter_str(flask.request.json['name']) or 'undefined' if name != user.name: user.audit_event( 'user_updated', 'User name changed', remote_addr=remote_addr, ) journal.entry( journal.USER_UPDATE, user.journal_data, event_long='User name changed', remote_address=remote_addr, ) user.name = name if 'email' in flask.request.json: email = utils.filter_str(flask.request.json['email']) or None if email != user.email: user.audit_event( 'user_updated', 'User email changed', remote_addr=remote_addr, ) journal.entry( journal.USER_UPDATE, user.journal_data, event_long='User email changed', remote_address=remote_addr, ) user.email = email if 'auth_type' in flask.request.json: auth_type = utils.filter_str(flask.request.json['auth_type']) or None if auth_type in AUTH_TYPES: if auth_type != user.auth_type: reset_user = True reset_user_cache = True user.auth_type = auth_type if 'yubico_id' in flask.request.json and user.auth_type == YUBICO_AUTH: yubico_id = utils.filter_str(flask.request.json['yubico_id']) or None yubico_id = yubico_id[:12] if yubico_id else None if yubico_id != user.yubico_id: reset_user = True reset_user_cache = True user.yubico_id = yubico_id if 'groups' in flask.request.json: groups = flask.request.json['groups'] or [] for i, group in enumerate(groups): groups[i] = utils.filter_str(group) groups = set(groups) if groups != set(user.groups or []): user.audit_event( 'user_updated', 'User groups changed', remote_addr=remote_addr, ) journal.entry( journal.USER_UPDATE, user.journal_data, event_long='User groups changed', remote_address=remote_addr, ) user.groups = list(groups) if 'pin' in flask.request.json: pin = flask.request.json['pin'] or None if pin is not True: if pin: if settings.user.pin_mode == PIN_DISABLED: return utils.jsonify( { 'error': PIN_IS_DISABLED, 'error_msg': PIN_IS_DISABLED_MSG, }, 400) if RADIUS_AUTH in user.auth_type: return utils.jsonify( { 'error': PIN_RADIUS, 'error_msg': PIN_RADIUS_MSG, }, 400) if settings.user.pin_digits_only and not pin.isdigit(): return utils.jsonify( { 'error': PIN_NOT_DIGITS, 'error_msg': PIN_NOT_DIGITS_MSG, }, 400) if len(pin) < settings.user.pin_min_length: return utils.jsonify( { 'error': PIN_TOO_SHORT, 'error_msg': PIN_TOO_SHORT_MSG, }, 400) if user.set_pin(pin): reset_user = True reset_user_cache = True user.audit_event( 'user_updated', 'User pin changed', remote_addr=remote_addr, ) journal.entry( journal.USER_UPDATE, user.journal_data, event_long='User pin changed', remote_address=remote_addr, ) if 'network_links' in flask.request.json: network_links_cur = set(user.get_network_links()) network_links_new = set() for network_link in flask.request.json['network_links'] or []: try: network_link = str(ipaddress.IPNetwork(network_link)) except (ipaddress.AddressValueError, ValueError): return _network_link_invalid() network_links_new.add(network_link) network_links_add = network_links_new - network_links_cur network_links_rem = network_links_cur - network_links_new if len(network_links_add) or len(network_links_rem): reset_user = True user.audit_event( 'user_updated', 'User network links updated', remote_addr=remote_addr, ) journal.entry( journal.USER_UPDATE, user.journal_data, event_long='User network links updated', remote_address=remote_addr, ) try: for network_link in network_links_add: user.add_network_link(network_link) except ServerOnlineError: return utils.jsonify( { 'error': NETWORK_LINK_NOT_OFFLINE, 'error_msg': NETWORK_LINK_NOT_OFFLINE_MSG, }, 400) for network_link in network_links_rem: user.remove_network_link(network_link) if 'port_forwarding' in flask.request.json: port_forwarding = [] for data in flask.request.json['port_forwarding'] or []: port_forwarding.append({ 'protocol': utils.filter_str(data.get('protocol')), 'port': utils.filter_str(data.get('port')), 'dport': utils.filter_str(data.get('dport')), }) if port_forwarding != user.port_forwarding: port_forwarding_event = True user.audit_event( 'user_updated', 'User port forwarding changed', remote_addr=remote_addr, ) journal.entry( journal.USER_UPDATE, user.journal_data, event_long='User port forwarding changed', remote_address=remote_addr, ) user.port_forwarding = port_forwarding disabled = True if flask.request.json.get('disabled') else False if disabled != user.disabled: user.audit_event( 'user_updated', 'User %s' % ('disabled' if disabled else 'enabled'), remote_addr=remote_addr, ) journal.entry( journal.USER_UPDATE, user.journal_data, event_long='User %s' % ('disabled' if disabled else 'enabled'), remote_address=remote_addr, ) if disabled: reset_user = True reset_user_cache = True user.disabled = disabled user.bypass_secondary = True if flask.request.json.get( 'bypass_secondary') else False user.client_to_client = True if flask.request.json.get( 'client_to_client') else False if user.bypass_secondary: if user.pin: return utils.jsonify( { 'error': PIN_BYPASS_SECONDARY, 'error_msg': PIN_BYPASS_SECONDARY_MSG, }, 400) if user.yubico_id: return utils.jsonify( { 'error': YUBIKEY_BYPASS_SECONDARY, 'error_msg': YUBIKEY_BYPASS_SECONDARY_MSG, }, 400) if 'dns_servers' in flask.request.json: dns_servers = flask.request.json['dns_servers'] or None if user.dns_servers != dns_servers: user.audit_event( 'user_updated', 'User dns servers changed', remote_addr=remote_addr, ) journal.entry( journal.USER_UPDATE, user.journal_data, event_long='User dns servers changed', remote_address=remote_addr, ) reset_user = True user.dns_servers = dns_servers if 'dns_suffix' in flask.request.json: dns_suffix = utils.filter_str(flask.request.json['dns_suffix']) or None if user.dns_suffix != dns_suffix: user.audit_event( 'user_updated', 'User dns suffix changed', remote_addr=remote_addr, ) journal.entry( journal.USER_UPDATE, user.journal_data, event_long='User dns suffix changed', remote_address=remote_addr, ) reset_user = True user.dns_suffix = dns_suffix user.commit() event.Event(type=USERS_UPDATED, resource_id=user.org.id) if port_forwarding_event: messenger.publish('port_forwarding', { 'org_id': org.id, 'user_id': user.id, }) if reset_user_cache: user.clear_auth_cache() if reset_user: user.disconnect() send_key_email = flask.request.json.get('send_key_email') if send_key_email and user.email: user.audit_event( 'user_emailed', 'User key email sent to "%s"' % user.email, remote_addr=remote_addr, ) journal.entry( journal.USER_PROFILE_EMAIL, user.journal_data, event_long='User key email sent to "%s"' % user.email, remote_address=remote_addr, ) try: user.send_key_email(utils.get_url_root()) except EmailNotConfiguredError: return utils.jsonify( { 'error': EMAIL_NOT_CONFIGURED, 'error_msg': EMAIL_NOT_CONFIGURED_MSG, }, 400) except EmailFromInvalid: return utils.jsonify( { 'error': EMAIL_FROM_INVALID, 'error_msg': EMAIL_FROM_INVALID_MSG, }, 400) except EmailAuthInvalid: return utils.jsonify( { 'error': EMAIL_AUTH_INVALID, 'error_msg': EMAIL_AUTH_INVALID_MSG, }, 400) return utils.jsonify(user.dict())
def auth_session_post(): username = utils.json_filter_str('username')[:128] password = flask.request.json['password'] if password: password = password[:128] otp_code = utils.json_opt_filter_str('otp_code') if otp_code: otp_code = otp_code[:64] yubico_key = utils.json_opt_filter_str('yubico_key') if yubico_key: yubico_key = yubico_key[:128] remote_addr = utils.get_remote_addr() time.sleep(random.randint(50, 100) / 1000.) admin = auth.get_by_username(username) if not admin: if settings.app.sso and RADIUS_AUTH in settings.app.sso: return _auth_radius(username, password, remote_addr) time.sleep(random.randint(0, 100) / 1000.) return _auth_plugin(username, password, remote_addr) if (not otp_code and admin.otp_auth) or \ (not yubico_key and admin.yubikey_id): return utils.jsonify( { 'error': AUTH_OTP_REQUIRED, 'error_msg': AUTH_OTP_REQUIRED_MSG, 'otp_auth': admin.otp_auth, 'yubico_auth': bool(admin.yubikey_id), }, 402) if not limiter.auth_check(admin.id): journal.entry( journal.ADMIN_AUTH_FAILURE, admin.journal_data, remote_address=remote_addr, reason=journal.ADMIN_AUTH_REASON_RATE_LIMIT, reason_long='Too many authentication attempts', ) return utils.jsonify( { 'error': AUTH_TOO_MANY, 'error_msg': AUTH_TOO_MANY_MSG, }, 400) if not admin.auth_check(password, otp_code, yubico_key, remote_addr): time.sleep(random.randint(0, 100) / 1000.) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) flask.session['session_id'] = admin.new_session() flask.session['admin_id'] = str(admin.id) flask.session['timestamp'] = int(utils.time_now()) if not settings.app.server_ssl: flask.session['source'] = remote_addr journal.entry( journal.ADMIN_SESSION_START, admin.journal_data, remote_address=remote_addr, session_id=flask.session['session_id'], ) utils.set_flask_sig() return utils.jsonify({ 'authenticated': True, 'default': admin.default or False, })
def _auth_radius(username, password, remote_addr): sso_mode = settings.app.sso valid, org_names, groups = sso.verify_radius(username, password) if not valid: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_RADIUS_FAILED, reason_long='Radius authentication failed', ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) org_id = settings.app.sso_org if org_names: not_found = False for org_name in org_names: org = organization.get_by_name(org_name, fields=('_id')) if org: not_found = False org_id = org.id break else: not_found = True if not_found: logger.warning( 'Supplied org names do not exist', 'sso', sso_type='radius', user_name=username, org_names=org_names, ) valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='radius', user_name=username, user_email=None, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Radius plugin authentication failed', ) logger.error( 'Radius plugin authentication not valid', 'sso', username=username, ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) groups = ((groups or set()) | (groups2 or set())) or None if DUO_AUTH in sso_mode: 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() except InvalidUser: logger.error( 'Duo authentication username not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_DUO_FAILED, reason_long='Duo authentication invalid username', ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if valid: valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='duo', user_name=username, user_email=None, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Duo plugin authentication failed', ) logger.error( 'Duo plugin authentication not valid', 'sso', username=username, ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) groups = ((groups or set()) | (groups2 or set())) or None else: logger.error( 'Duo authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_DUO_FAILED, reason_long='Duo authentication failed', ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) groups = ((groups or set()) | (groups2 or set())) or None org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'auth', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, 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=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 usr.disabled: return utils.jsonify( { 'error': AUTH_DISABLED, 'error_msg': AUTH_DISABLED_MSG, }, 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.set_pin(None) usr.commit(('auth_type', 'pin')) key_link = org.create_user_key_link(usr.id, one_time=True) journal.entry( journal.SSO_AUTH_SUCCESS, usr.journal_data, key_id_hash=hashlib.md5(key_link['id'].encode()).hexdigest(), remote_address=remote_addr, ) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, event_long='User profile viewed from single sign-on', remote_address=remote_addr, ) return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 202)
def user_key_pin_put(key_id): if settings.app.demo_mode: return utils.demo_blocked() remote_addr = utils.get_remote_addr() doc = _find_doc({ 'key_id': key_id, }) if not doc: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='Key ID not found', ) return flask.abort(404) if settings.user.pin_mode == PIN_DISABLED: return utils.jsonify( { 'error': PIN_IS_DISABLED, 'error_msg': PIN_IS_DISABLED_MSG, }, 400) org = organization.get_by_id(doc['org_id']) usr = org.get_user(doc['user_id']) if usr.disabled: return flask.abort(403) if RADIUS_AUTH in usr.auth_type: return utils.jsonify( { 'error': PIN_RADIUS, 'error_msg': PIN_RADIUS_MSG, }, 400) current_pin = utils.filter_str( flask.request.json.get('current_pin')) or None pin = utils.filter_str(flask.request.json.get('pin')) or None if pin: if settings.user.pin_digits_only and not pin.isdigit(): return utils.jsonify( { 'error': PIN_NOT_DIGITS, 'error_msg': PIN_NOT_DIGITS_MSG, }, 400) if len(pin) < settings.user.pin_min_length: return utils.jsonify( { 'error': PIN_TOO_SHORT, 'error_msg': PIN_TOO_SHORT_MSG, }, 400) if usr.pin: if not limiter.auth_check(usr.id): return utils.jsonify( { 'error': AUTH_TOO_MANY, 'error_msg': AUTH_TOO_MANY_MSG, }, 400) if not usr.check_pin(current_pin): return utils.jsonify( { 'error': PIN_INVALID, 'error_msg': PIN_INVALID_MSG, }, 400) if usr.set_pin(pin): journal.entry( journal.USER_PIN_UPDATE, usr.journal_data, remote_address=remote_addr, event_long='User pin changed with temporary profile link', ) usr.audit_event( 'user_updated', 'User pin changed with temporary profile link', remote_addr=remote_addr, ) usr.commit() event.Event(type=USERS_UPDATED, resource_id=org.id) return utils.jsonify({})
def sso_callback_get(): sso_mode = settings.app.sso if sso_mode not in (AZURE_AUTH, AZURE_DUO_AUTH, AZURE_YUBICO_AUTH, GOOGLE_AUTH, GOOGLE_DUO_AUTH, GOOGLE_YUBICO_AUTH, AUTHZERO_AUTH, AUTHZERO_DUO_AUTH, AUTHZERO_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) remote_addr = utils.get_remote_addr() 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): journal.entry( journal.SSO_AUTH_FAILURE, state=state, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_INVALID_CALLBACK, reason_long='Signature mismatch', ) 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_names = [] if params.get('org'): org_names_param = params.get('org')[0] if ';' in org_names_param: org_names = org_names_param.split(';') else: org_names = org_names_param.split(',') org_names = [x for x in org_names if x] org_names = sorted(org_names) groups = [] if params.get('groups'): groups_param = params.get('groups')[0] if ';' in groups_param: groups = groups_param.split(';') else: groups = groups_param.split(',') groups = [x for x in groups if x] groups = set(groups) if not username: return flask.abort(406) org_id = settings.app.sso_org if org_names: not_found = False for org_name in org_names: org = organization.get_by_name( utils.filter_unicode(org_name), fields=('_id'), ) if org: not_found = False org_id = org.id break else: not_found = True if not_found: logger.warning('Supplied org names do not exists', 'sso', sso_type=doc.get('type'), user_name=username, user_email=email, org_names=org_names, ) valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='saml', user_name=username, user_email=email, remote_ip=remote_addr, sso_org_names=org_names, ) if valid: org_id = org_id_new or org_id else: logger.error('Saml plugin authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Saml plugin authentication failed', ) return flask.abort(401) groups = groups | set(groups2 or []) 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 = sorted(org_names.split(',')) if user_team != settings.app.sso_match[0]: return flask.abort(401) not_found = False org_id = settings.app.sso_org for org_name in org_names: org = organization.get_by_name( utils.filter_unicode(org_name), fields=('_id'), ) if org: not_found = False org_id = org.id break else: not_found = True if not_found: logger.warning('Supplied org names do not exists', 'sso', sso_type=doc.get('type'), user_name=username, user_email=email, org_names=org_names, ) valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='slack', user_name=username, user_email=email, remote_ip=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, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Slack plugin authentication failed', ) return flask.abort(401) groups = set(groups or []) elif doc.get('type') == GOOGLE_AUTH: username = params.get('username')[0] email = username valid, google_groups = sso.verify_google(username) if not valid: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_GOOGLE_FAILED, reason_long='Google authentication failed', ) 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=remote_addr, ) if valid: org_id = org_id_new or org_id else: logger.error('Google plugin authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Google plugin authentication failed', ) return flask.abort(401) groups = set(groups or []) if settings.app.sso_google_mode == 'groups': groups = groups | set(google_groups) else: not_found = False google_groups = sorted(google_groups) for org_name in google_groups: org = organization.get_by_name( utils.filter_unicode(org_name), fields=('_id'), ) if org: not_found = False org_id = org.id break else: not_found = True if not_found: logger.warning('Supplied org names do not exists', 'sso', sso_type=doc.get('type'), user_name=username, user_email=email, org_names=google_groups, ) elif doc.get('type') == AZURE_AUTH: username = params.get('username')[0] email = None tenant, username = username.split('/', 2) if tenant != settings.app.sso_azure_directory_id: logger.error('Azure directory ID mismatch', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, azure_tenant=tenant, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_AZURE_FAILED, reason_long='Azure directory ID mismatch', ) return flask.abort(401) valid, azure_groups = sso.verify_azure(username) if not valid: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_AZURE_FAILED, reason_long='Azure authentication failed', ) return flask.abort(401) org_id = settings.app.sso_org valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='azure', user_name=username, user_email=email, remote_ip=remote_addr, ) if valid: org_id = org_id_new or org_id else: logger.error('Azure plugin authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Azure plugin authentication failed', ) return flask.abort(401) groups = set(groups or []) if settings.app.sso_azure_mode == 'groups': groups = groups | set(azure_groups) else: not_found = False azure_groups = sorted(azure_groups) for org_name in azure_groups: org = organization.get_by_name( utils.filter_unicode(org_name), fields=('_id'), ) if org: not_found = False org_id = org.id break else: not_found = True if not_found: logger.warning('Supplied org names do not exists', 'sso', sso_type=doc.get('type'), user_name=username, user_email=email, org_names=azure_groups, ) elif doc.get('type') == AUTHZERO_AUTH: username = params.get('username')[0] email = None valid, authzero_groups = sso.verify_authzero(username) if not valid: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_AUTHZERO_FAILED, reason_long='Auth0 authentication failed', ) return flask.abort(401) org_id = settings.app.sso_org valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='authzero', user_name=username, user_email=email, remote_ip=remote_addr, ) if valid: org_id = org_id_new or org_id else: logger.error('Auth0 plugin authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Auth0 plugin authentication failed', ) return flask.abort(401) groups = set(groups or []) if settings.app.sso_authzero_mode == 'groups': groups = groups | set(authzero_groups) else: not_found = False authzero_groups = sorted(authzero_groups) for org_name in authzero_groups: org = organization.get_by_name( utils.filter_unicode(org_name), fields=('_id'), ) if org: not_found = False org_id = org.id break else: not_found = True if not_found: logger.warning('Supplied org names do not exists', 'sso', sso_type=doc.get('type'), user_name=username, user_email=email, org_names=authzero_groups, ) else: logger.error('Unknown sso type', 'sso', sso_type=doc.get('type'), ) 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': list(groups) if groups else None, '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': list(groups) if groups else None, '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() return _validate_user(username, email, sso_mode, org_id, groups, remote_addr, http_redirect=True)
def sso_duo_post(): remote_addr = utils.get_remote_addr() 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, AZURE_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_and_modify(query={ '_id': token, }, remove=True) if not doc or doc['_id'] != token or doc['type'] != DUO_AUTH: journal.entry( journal.SSO_AUTH_FAILURE, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_INVALID_TOKEN, reason_long='Invalid Duo authentication token', ) return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = set(doc['groups'] or []) if settings.app.sso_duo_mode == 'passcode': duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=remote_addr, auth_type='Key', passcode=passcode, ) valid = duo_auth.authenticate() if not valid: logger.warning('Duo authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, username=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_DUO_FAILED, reason_long='Duo passcode authentication failed', ) 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=remote_addr, auth_type='Key', ) valid = duo_auth.authenticate() if not valid: logger.warning('Duo authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_DUO_FAILED, reason_long='Duo authentication failed', ) 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=remote_addr, ) if valid: org_id = org_id_new or org_id else: logger.warning('Duo plugin authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Duo plugin authentication failed', ) return flask.abort(401) groups = groups | set(groups2 or []) return _validate_user(username, email, sso_mode, org_id, groups, remote_addr)
def _check_push(self): self.push_type = self.user.get_push_type() if not self.push_type: return if settings.vpn.stress_test: return if self.user.bypass_secondary: logger.info('Bypass secondary enabled, skipping push', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) return if self.has_token: logger.info('Client authentication cached, skipping push', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_CACHE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Client authentication cached, skipping push', ) return if self.whitelisted: logger.info('Client network whitelisted, skipping push', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_WHITELIST, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Client network whitelisted, skipping push', ) return if settings.app.sso_cache and not self.server_auth_token: doc = self.sso_push_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, }) if doc: self.sso_push_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'timestamp': utils.now(), }) logger.info('Authentication cached, skipping push', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_CACHE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Authentication cached, skipping push', ) return def thread_func(): try: self._check_call(self._auth_push_thread) self._callback(True) except: pass thread = threading.Thread(target=thread_func) thread.daemon = True thread.start() raise AuthForked()
def user_linked_key_conf_get(key_id, server_id): remote_addr = utils.get_remote_addr() doc = _find_doc({ 'key_id': key_id, }) if not doc: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='Key ID not found', ) return flask.abort(404) if settings.user.restrict_import: return flask.abort(404) org = organization.get_by_id(doc['org_id']) if not org: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='Organization not found', ) return flask.abort(404) usr = org.get_user(id=doc['user_id']) if not usr: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='User not found', ) return flask.abort(404) if usr.disabled: journal.entry( journal.USER_PROFILE_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) key_conf = usr.build_key_conf(server_id) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User profile downloaded with temporary profile link', ) usr.audit_event( 'user_profile', 'User profile downloaded with temporary profile link', remote_addr=remote_addr, ) response = flask.Response(response=key_conf['conf'], mimetype='application/ovpn') response.headers.add('Content-Disposition', 'attachment; filename="%s"' % key_conf['name']) return response
def _check_password(self): if settings.vpn.stress_test or self.user.link_server_id: return if self.user.bypass_secondary: logger.info( 'Bypass secondary enabled, skipping password', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_BYPASS, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Bypass secondary enabled, skipping password', ) return if self.has_token: logger.info( 'Client authentication cached, skipping password', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_CACHE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Client authentication cached, skipping password', ) return if self.whitelisted: logger.info( 'Client network whitelisted, skipping password', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_WHITELIST, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Client network whitelisted, skipping password', ) return if not limiter.auth_check(self.user.id): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. Too many ' + 'authentication attempts') % (self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Too many authentication attempts', ) raise AuthError('Too many authentication attempts') sso_mode = settings.app.sso or '' duo_mode = settings.app.sso_duo_mode onelogin_mode = utils.get_onelogin_mode() okta_mode = utils.get_okta_mode() auth_type = self.user.auth_type or '' has_duo_passcode = DUO_AUTH in sso_mode and \ DUO_AUTH in auth_type and duo_mode == 'passcode' has_onelogin_passcode = SAML_ONELOGIN_AUTH == sso_mode and \ SAML_ONELOGIN_AUTH in auth_type and onelogin_mode == 'passcode' has_okta_passcode = SAML_OKTA_AUTH == sso_mode and \ SAML_OKTA_AUTH in auth_type and okta_mode == 'passcode' if has_duo_passcode or has_onelogin_passcode or has_okta_passcode: if not self.password and self.has_challenge() and \ self.user.has_pin(): journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed pin authentication', ) self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % ( self.server.name), remote_addr=self.remote_ip, ) self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password passcode_len = settings.app.sso_duo_passcode_length orig_password = self.password passcode = self.password[-passcode_len:] self.password = self.password[:-passcode_len] allow = False if settings.app.sso_cache and not self.server_auth_token: doc = self.sso_passcode_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, }) if doc: self.sso_passcode_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, 'timestamp': utils.now(), }) allow = True logger.info( 'Authentication cached, skipping secondary passcode', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_CACHE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Authentication cached, ' + \ 'skipping secondary passcode', ) if not allow: if DUO_AUTH in sso_mode: label = 'Duo' duo_auth = sso.Duo( username=self.user.name, factor=duo_mode, remote_ip=self.remote_ip, auth_type='Connection', passcode=passcode, ) allow = duo_auth.authenticate() elif SAML_ONELOGIN_AUTH == sso_mode: label = 'OneLogin' allow = sso.auth_onelogin_secondary( username=self.user.name, passcode=passcode, remote_ip=self.remote_ip, onelogin_mode=onelogin_mode, ) elif SAML_OKTA_AUTH == sso_mode: label = 'Okta' allow = sso.auth_okta_secondary( username=self.user.name, passcode=passcode, remote_ip=self.remote_ip, okta_mode=okta_mode, ) else: raise AuthError('Unknown secondary passcode challenge') if not allow: self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed %s passcode authentication') % ( self.server.name, label), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed passcode authentication', ) if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge( orig_password, 'Enter %s Passcode' % label, True) else: self.set_challenge( None, 'Enter %s Passcode' % label, True) raise AuthError('Challenge secondary passcode') raise AuthError('Invalid secondary passcode') if settings.app.sso_cache and not self.server_auth_token: self.sso_passcode_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': passcode, 'timestamp': utils.now(), }, upsert=True) elif YUBICO_AUTH in sso_mode and YUBICO_AUTH in auth_type: if not self.password and self.has_challenge() and \ self.user.has_pin(): self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed pin authentication', ) self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password orig_password = self.password yubikey = self.password[-44:] self.password = self.password[:-44] yubikey_hash = hashlib.sha512() yubikey_hash.update(yubikey) yubikey_hash = base64.b64encode(yubikey_hash.digest()) allow = False if settings.app.sso_cache and not self.server_auth_token: doc = self.sso_passcode_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, }) if doc: self.sso_passcode_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, 'timestamp': utils.now(), }) allow = True logger.info( 'Authentication cached, skipping Yubikey', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_CACHE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Authentication cached, ' + \ 'skipping Yubikey', ) if not allow: valid, yubico_id = sso.auth_yubico(yubikey) if yubico_id != self.user.yubico_id: valid = False if not valid: self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed Yubico authentication') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed Yubico authentication', ) if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge( orig_password, 'YubiKey', True) else: self.set_challenge( None, 'YubiKey', True) raise AuthError('Challenge YubiKey') raise AuthError('Invalid YubiKey') if settings.app.sso_cache and not self.server_auth_token: self.sso_passcode_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': yubikey_hash, 'timestamp': utils.now(), }, upsert=True) elif self.server.otp_auth and self.user.type == CERT_CLIENT: if not self.password and self.has_challenge() and \ self.user.has_pin(): self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed pin authentication', ) self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') challenge = self.get_challenge() if challenge: self.password = challenge + self.password orig_password = self.password otp_code = self.password[-6:] self.password = self.password[:-6] allow = False if settings.app.sso_cache and not self.server_auth_token: doc = self.otp_cache_collection.find_one({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, }) if doc: self.otp_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, 'timestamp': utils.now(), }) allow = True logger.info( 'Authentication cached, skipping OTP', 'sso', user_name=self.user.name, org_name=self.user.org.name, server_name=self.server.name, ) journal.entry( journal.USER_CONNECT_CACHE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Authentication cached, ' + \ 'skipping OTP', ) if not allow: if not self.user.verify_otp_code(otp_code): self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed two-step authentication') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed two-step authentication', ) if self.has_challenge(): if self.user.has_password(self.server): self.set_challenge( orig_password, 'Enter OTP Code', True) else: self.set_challenge( None, 'Enter OTP Code', True) raise AuthError('Challenge OTP code') raise AuthError('Invalid OTP code') if settings.app.sso_cache and not self.server_auth_token: self.otp_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'passcode': otp_code, 'timestamp': utils.now(), }, upsert=True) if self.user.has_pin(): if not self.user.check_pin(self.password): self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User failed pin authentication') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Failed pin authentication', ) if self.has_challenge(): self.set_challenge(None, 'Enter Pin', False) raise AuthError('Challenge pin') raise AuthError('Invalid pin') elif settings.user.pin_mode == PIN_REQUIRED: self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'User does not have a pin set') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='User does not have a pin set', ) raise AuthError('User does not have a pin set')
def sso_duo_post(): remote_addr = utils.get_remote_addr() 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, AZURE_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_and_modify(query={ '_id': token, }, remove=True) if not doc or doc['_id'] != token or doc['type'] != DUO_AUTH: journal.entry( journal.SSO_AUTH_FAILURE, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_INVALID_TOKEN, reason_long='Invalid Duo authentication token', ) return utils.jsonify( { 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'].lower() email = doc['email'] org_id = doc['org_id'] groups = set(doc['groups'] or []) if settings.app.sso_duo_mode == 'passcode': duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=remote_addr, auth_type='Key', passcode=passcode, ) valid = duo_auth.authenticate() if not valid: logger.warning( 'Duo authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, username=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_DUO_FAILED, reason_long='Duo passcode authentication failed', ) 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=remote_addr, auth_type='Key', ) valid = duo_auth.authenticate() if not valid: logger.warning( 'Duo authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_DUO_FAILED, reason_long='Duo authentication failed', ) 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=remote_addr, ) if valid: org_id = org_id_new or org_id else: logger.warning( 'Duo plugin authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Duo plugin authentication failed', ) return flask.abort(401) groups = groups | set(groups2 or []) return _validate_user(username, email, sso_mode, org_id, groups, remote_addr)
def _check_primary(self): org_matched = False for org_id in self.server.organizations: if self.user.org_id == org_id: org_matched = True break if not org_matched: journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Unknown organization', ) raise AuthError('Unknown organization') if self.user.disabled: self.user.audit_event('user_connection', 'User connection to "%s" denied. User is disabled' % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='User disabled', ) raise AuthError('User is disabled') if not self.user.name: journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='User name empty', ) raise AuthError('User name empty') user_lower = self.user.name.lower() if user_lower in INVALID_NAMES: journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='User name invalid', ) raise AuthError('User name invalid') if self.user.type == CERT_CLIENT: if self.user.link_server_id: journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Link user client type', ) raise AuthError('Link user client type') elif self.user.type == CERT_SERVER: if not self.user.link_server_id: journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Link user missing server id', ) raise AuthError('Link user missing server id') link_matched = False for link in self.server.links: if link.get('server_id') == self.user.link_server_id: link_matched = True break if not link_matched: journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Unknown link user', ) raise AuthError('Unknown link user') return else: journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Unknown user type', ) raise AuthError('Unknown user type') if not self.server.check_groups(self.user.groups): self.user.audit_event( 'user_connection', ('User connection to "%s" denied. User not in ' + 'servers groups') % (self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='User not in servers groups', ) raise AuthError('User not in servers groups') if self.server.allowed_devices: if self.server.allowed_devices == 'mobile': platforms = MOBILE_PLATFORMS elif self.server.allowed_devices == 'desktop': platforms = DESKTOP_PLATFORMS else: logger.error('Unknown allowed devices option', 'server', server_id=self.server.id, allowed_devices=self.server.allowed_devices, ) platforms = {} if self.platform not in platforms: self.user.audit_event( 'user_connection', ('User connection to "%s" denied. User platform ' + 'not allowed') % (self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='User platform not allowed', ) raise AuthError( 'User platform %s not allowed' % self.platform)
def settings_put(): if settings.app.demo_mode: return utils.demo_blocked() org_event = False admin_event = False admin = flask.g.administrator changes = set() settings_commit = False update_server = False update_acme = False update_cert = False if 'username' in flask.request.json and flask.request.json['username']: username = utils.filter_str(flask.request.json['username']).lower() if username != admin.username: changes.add('username') admin.username = username if 'password' in flask.request.json and flask.request.json['password']: password = flask.request.json['password'] changes.add('password') admin.password = password if 'server_cert' in flask.request.json: settings_commit = True server_cert = flask.request.json['server_cert'] if server_cert: server_cert = server_cert.strip() else: server_cert = None if server_cert != settings.app.server_cert: update_server = True settings.app.server_cert = server_cert if 'server_key' in flask.request.json: settings_commit = True server_key = flask.request.json['server_key'] if server_key: server_key = server_key.strip() else: server_key = None if server_key != settings.app.server_key: update_server = True settings.app.server_key = server_key if 'server_port' in flask.request.json: settings_commit = True server_port = flask.request.json['server_port'] if not server_port: server_port = 443 try: server_port = int(server_port) if server_port < 1 or server_port > 65535: raise ValueError('Port invalid') except ValueError: return utils.jsonify( { 'error': PORT_INVALID, 'error_msg': PORT_INVALID_MSG, }, 400) if server_port != settings.app.server_port: update_server = True settings.app.server_port = server_port if 'acme_domain' in flask.request.json: settings_commit = True acme_domain = utils.filter_str(flask.request.json['acme_domain'] or None) if acme_domain: acme_domain = acme_domain.replace('https://', '') acme_domain = acme_domain.replace('http://', '') acme_domain = acme_domain.replace('/', '') if acme_domain != settings.app.acme_domain: if not acme_domain: settings.app.acme_key = None settings.app.acme_timestamp = None settings.app.server_key = None settings.app.server_cert = None update_server = True update_cert = True else: update_acme = True settings.app.acme_domain = acme_domain if 'auditing' in flask.request.json: settings_commit = True auditing = flask.request.json['auditing'] or None if settings.app.auditing == ALL and auditing != ALL: return utils.jsonify( { 'error': CANNOT_DISABLE_AUTIDING, 'error_msg': CANNOT_DISABLE_AUTIDING_MSG, }, 400) if settings.app.auditing != auditing: if not flask.g.administrator.super_user: return utils.jsonify( { 'error': REQUIRES_SUPER_USER, 'error_msg': REQUIRES_SUPER_USER_MSG, }, 400) admin_event = True org_event = True settings.app.auditing = auditing if 'monitoring' in flask.request.json: settings_commit = True monitoring = flask.request.json['monitoring'] or None settings.app.monitoring = monitoring if 'influxdb_uri' in flask.request.json: settings_commit = True influxdb_uri = flask.request.json['influxdb_uri'] or None settings.app.influxdb_uri = influxdb_uri if 'email_from' in flask.request.json: settings_commit = True email_from = flask.request.json['email_from'] or None if email_from != settings.app.email_from: changes.add('smtp') settings.app.email_from = email_from if 'email_server' in flask.request.json: settings_commit = True email_server = flask.request.json['email_server'] or None if email_server != settings.app.email_server: changes.add('smtp') settings.app.email_server = email_server if 'email_username' in flask.request.json: settings_commit = True email_username = flask.request.json['email_username'] or None if email_username != settings.app.email_username: changes.add('smtp') settings.app.email_username = email_username if 'email_password' in flask.request.json: settings_commit = True email_password = flask.request.json['email_password'] or None if email_password != settings.app.email_password: changes.add('smtp') settings.app.email_password = email_password if 'pin_mode' in flask.request.json: settings_commit = True pin_mode = flask.request.json['pin_mode'] or None if pin_mode != settings.user.pin_mode: changes.add('pin_mode') settings.user.pin_mode = pin_mode if 'sso' in flask.request.json: org_event = True settings_commit = True sso = flask.request.json['sso'] or None if sso != settings.app.sso: changes.add('sso') settings.app.sso = sso if 'sso_match' in flask.request.json: settings_commit = True sso_match = flask.request.json['sso_match'] or None if sso_match != settings.app.sso_match: changes.add('sso') if isinstance(sso_match, list): settings.app.sso_match = sso_match else: settings.app.sso_match = None if 'sso_azure_directory_id' in flask.request.json: settings_commit = True sso_azure_directory_id = flask.request.json[ 'sso_azure_directory_id'] or None if sso_azure_directory_id != settings.app.sso_azure_directory_id: changes.add('sso') settings.app.sso_azure_directory_id = sso_azure_directory_id if 'sso_azure_app_id' in flask.request.json: settings_commit = True sso_azure_app_id = flask.request.json['sso_azure_app_id'] or None if sso_azure_app_id != settings.app.sso_azure_app_id: changes.add('sso') settings.app.sso_azure_app_id = sso_azure_app_id if 'sso_azure_app_secret' in flask.request.json: settings_commit = True sso_azure_app_secret = flask.request.json[ 'sso_azure_app_secret'] or None if sso_azure_app_secret != settings.app.sso_azure_app_secret: changes.add('sso') settings.app.sso_azure_app_secret = sso_azure_app_secret if 'sso_authzero_domain' in flask.request.json: settings_commit = True sso_authzero_domain = flask.request.json['sso_authzero_domain'] or None if sso_authzero_domain != settings.app.sso_authzero_domain: changes.add('sso') settings.app.sso_authzero_domain = sso_authzero_domain if 'sso_authzero_app_id' in flask.request.json: settings_commit = True sso_authzero_app_id = flask.request.json['sso_authzero_app_id'] or None if sso_authzero_app_id != settings.app.sso_authzero_app_id: changes.add('sso') settings.app.sso_authzero_app_id = sso_authzero_app_id if 'sso_authzero_app_secret' in flask.request.json: settings_commit = True sso_authzero_app_secret = flask.request.json[ 'sso_authzero_app_secret'] or None if sso_authzero_app_secret != settings.app.sso_authzero_app_secret: changes.add('sso') settings.app.sso_authzero_app_secret = sso_authzero_app_secret if 'sso_google_key' in flask.request.json: settings_commit = True sso_google_key = flask.request.json['sso_google_key'] or None if sso_google_key != settings.app.sso_google_key: changes.add('sso') settings.app.sso_google_key = sso_google_key if 'sso_google_email' in flask.request.json: settings_commit = True sso_google_email = flask.request.json['sso_google_email'] or None if sso_google_email != settings.app.sso_google_email: changes.add('sso') settings.app.sso_google_email = sso_google_email if 'sso_duo_token' in flask.request.json: settings_commit = True sso_duo_token = flask.request.json['sso_duo_token'] or None if sso_duo_token != settings.app.sso_duo_token: changes.add('sso') settings.app.sso_duo_token = sso_duo_token if 'sso_duo_secret' in flask.request.json: settings_commit = True sso_duo_secret = flask.request.json['sso_duo_secret'] or None if sso_duo_secret != settings.app.sso_duo_secret: changes.add('sso') settings.app.sso_duo_secret = sso_duo_secret if 'sso_duo_host' in flask.request.json: settings_commit = True sso_duo_host = flask.request.json['sso_duo_host'] or None if sso_duo_host != settings.app.sso_duo_host: changes.add('sso') settings.app.sso_duo_host = sso_duo_host if 'sso_duo_mode' in flask.request.json: settings_commit = True sso_duo_mode = flask.request.json['sso_duo_mode'] or None if sso_duo_mode != settings.app.sso_duo_mode: changes.add('sso') settings.app.sso_duo_mode = sso_duo_mode if 'sso_radius_secret' in flask.request.json: settings_commit = True sso_radius_secret = flask.request.json['sso_radius_secret'] or None if sso_radius_secret != settings.app.sso_radius_secret: changes.add('sso') settings.app.sso_radius_secret = sso_radius_secret if 'sso_radius_host' in flask.request.json: settings_commit = True sso_radius_host = flask.request.json['sso_radius_host'] or None if sso_radius_host != settings.app.sso_radius_host: changes.add('sso') settings.app.sso_radius_host = sso_radius_host if 'sso_org' in flask.request.json: settings_commit = True sso_org = flask.request.json['sso_org'] or None if sso_org: sso_org = utils.ObjectId(sso_org) else: sso_org = None if sso_org != settings.app.sso_org: changes.add('sso') if settings.app.sso and not sso_org: return utils.jsonify( { 'error': SSO_ORG_NULL, 'error_msg': SSO_ORG_NULL_MSG, }, 400) settings.app.sso_org = sso_org if 'sso_saml_url' in flask.request.json: settings_commit = True sso_saml_url = flask.request.json['sso_saml_url'] or None if sso_saml_url != settings.app.sso_saml_url: changes.add('sso') settings.app.sso_saml_url = sso_saml_url if 'sso_saml_issuer_url' in flask.request.json: settings_commit = True sso_saml_issuer_url = flask.request.json['sso_saml_issuer_url'] or \ None if sso_saml_issuer_url != settings.app.sso_saml_issuer_url: changes.add('sso') settings.app.sso_saml_issuer_url = sso_saml_issuer_url if 'sso_saml_cert' in flask.request.json: settings_commit = True sso_saml_cert = flask.request.json['sso_saml_cert'] or None if sso_saml_cert != settings.app.sso_saml_cert: changes.add('sso') settings.app.sso_saml_cert = sso_saml_cert if 'sso_okta_app_id' in flask.request.json: settings_commit = True sso_okta_app_id = flask.request.json['sso_okta_app_id'] or None if sso_okta_app_id != settings.app.sso_okta_app_id: changes.add('sso') settings.app.sso_okta_app_id = sso_okta_app_id if 'sso_okta_token' in flask.request.json: settings_commit = True sso_okta_token = flask.request.json['sso_okta_token'] or None if sso_okta_token != settings.app.sso_okta_token: changes.add('sso') settings.app.sso_okta_token = sso_okta_token if 'sso_okta_mode' in flask.request.json: sso_mode = settings.app.sso if sso_mode and sso_mode == SAML_OKTA_AUTH: settings_commit = True sso_okta_mode = flask.request.json['sso_okta_mode'] settings.app.sso_okta_mode = sso_okta_mode if 'sso_onelogin_app_id' in flask.request.json: settings_commit = True sso_onelogin_app_id = flask.request.json['sso_onelogin_app_id'] or \ None if sso_onelogin_app_id != settings.app.sso_onelogin_app_id: changes.add('sso') settings.app.sso_onelogin_app_id = sso_onelogin_app_id if 'sso_onelogin_id' in flask.request.json: settings_commit = True sso_onelogin_id = flask.request.json['sso_onelogin_id'] or None if sso_onelogin_id != settings.app.sso_onelogin_id: changes.add('sso') settings.app.sso_onelogin_id = sso_onelogin_id if 'sso_onelogin_secret' in flask.request.json: settings_commit = True sso_onelogin_secret = \ flask.request.json['sso_onelogin_secret'] or None if sso_onelogin_secret != settings.app.sso_onelogin_secret: changes.add('sso') settings.app.sso_onelogin_secret = sso_onelogin_secret if 'sso_onelogin_mode' in flask.request.json: sso_mode = settings.app.sso if sso_mode and sso_mode == SAML_ONELOGIN_AUTH: settings_commit = True sso_onelogin_mode = flask.request.json['sso_onelogin_mode'] settings.app.sso_onelogin_mode = sso_onelogin_mode if 'sso_cache' in flask.request.json: settings_commit = True sso_cache = True if \ flask.request.json['sso_cache'] else False if sso_cache != settings.app.sso_cache: changes.add('sso') settings.app.sso_cache = sso_cache if 'sso_client_cache' in flask.request.json: settings_commit = True sso_client_cache = True if \ flask.request.json['sso_client_cache'] else False if sso_client_cache != settings.app.sso_client_cache: changes.add('sso') settings.app.sso_client_cache = sso_client_cache if 'restrict_import' in flask.request.json: settings_commit = True restrict_import = True if \ flask.request.json['restrict_import'] else False if restrict_import != settings.user.restrict_import: changes.add('restrict_import') settings.user.restrict_import = restrict_import if 'client_reconnect' in flask.request.json: settings_commit = True client_reconnect = True if \ flask.request.json['client_reconnect'] else False settings.user.reconnect = client_reconnect if 'sso_yubico_client' in flask.request.json: settings_commit = True sso_yubico_client = \ flask.request.json['sso_yubico_client'] or None if sso_yubico_client != settings.app.sso_yubico_client: changes.add('sso') settings.app.sso_yubico_client = sso_yubico_client if 'sso_yubico_secret' in flask.request.json: settings_commit = True sso_yubico_secret = \ flask.request.json['sso_yubico_secret'] or None if sso_yubico_secret != settings.app.sso_yubico_secret: changes.add('sso') settings.app.sso_yubico_secret = sso_yubico_secret if flask.request.json.get('theme'): settings_commit = True theme = 'light' if flask.request.json['theme'] == 'light' else 'dark' if theme != settings.app.theme: if theme == 'dark': event.Event(type=THEME_DARK) else: event.Event(type=THEME_LIGHT) settings.app.theme = theme if 'public_address' in flask.request.json: public_address = flask.request.json['public_address'] or None if public_address != settings.local.host.public_addr: settings.local.host.public_address = public_address settings.local.host.commit('public_address') if 'public_address6' in flask.request.json: public_address6 = flask.request.json['public_address6'] or None if public_address6 != settings.local.host.public_addr6: settings.local.host.public_address6 = public_address6 settings.local.host.commit('public_address6') if 'routed_subnet6' in flask.request.json: routed_subnet6 = flask.request.json['routed_subnet6'] if routed_subnet6: try: routed_subnet6 = ipaddress.IPv6Network( flask.request.json['routed_subnet6']) except (ipaddress.AddressValueError, ValueError): return utils.jsonify( { 'error': IPV6_SUBNET_INVALID, 'error_msg': IPV6_SUBNET_INVALID_MSG, }, 400) if routed_subnet6.prefixlen > 64: return utils.jsonify( { 'error': IPV6_SUBNET_SIZE_INVALID, 'error_msg': IPV6_SUBNET_SIZE_INVALID_MSG, }, 400) routed_subnet6 = str(routed_subnet6) else: routed_subnet6 = None if settings.local.host.routed_subnet6 != routed_subnet6: if server.get_online_ipv6_count(): return utils.jsonify( { 'error': IPV6_SUBNET_ONLINE, 'error_msg': IPV6_SUBNET_ONLINE_MSG, }, 400) settings.local.host.routed_subnet6 = routed_subnet6 settings.local.host.commit('routed_subnet6') if 'reverse_proxy' in flask.request.json: settings_commit = True reverse_proxy = flask.request.json['reverse_proxy'] settings.app.reverse_proxy = True if reverse_proxy else False if 'cloud_provider' in flask.request.json: settings_commit = True cloud_provider = flask.request.json['cloud_provider'] or None settings.app.cloud_provider = cloud_provider if 'route53_region' in flask.request.json: settings_commit = True settings.app.route53_region = utils.filter_str( flask.request.json['route53_region']) or None if 'route53_zone' in flask.request.json: settings_commit = True settings.app.route53_zone = utils.filter_str( flask.request.json['route53_zone']) or None if settings.app.cloud_provider == 'oracle': if 'oracle_user_ocid' in flask.request.json: settings_commit = True settings.app.oracle_user_ocid = utils.filter_str( flask.request.json['oracle_user_ocid']) or None elif settings.app.oracle_user_ocid: settings_commit = True settings.app.oracle_user_ocid = None if 'oracle_public_key' in flask.request.json: if flask.request.json['oracle_public_key'] == 'reset': settings_commit = True private_key, public_key = utils.generate_rsa_key() settings.app.oracle_private_key = private_key settings.app.oracle_public_key = public_key for aws_key in ( 'us_east_1_access_key', 'us_east_1_secret_key', 'us_east_2_access_key', 'us_east_2_secret_key', 'us_west_1_access_key', 'us_west_1_secret_key', 'us_west_2_access_key', 'us_west_2_secret_key', 'us_gov_east_1_access_key', 'us_gov_east_1_secret_key', 'us_gov_west_1_access_key', 'us_gov_west_1_secret_key', 'eu_north_1_access_key', 'eu_north_1_secret_key', 'eu_west_1_access_key', 'eu_west_1_secret_key', 'eu_west_2_access_key', 'eu_west_2_secret_key', 'eu_west_3_access_key', 'eu_west_3_secret_key', 'eu_central_1_access_key', 'eu_central_1_secret_key', 'ca_central_1_access_key', 'ca_central_1_secret_key', 'cn_north_1_access_key', 'cn_north_1_secret_key', 'cn_northwest_1_access_key', 'cn_northwest_1_secret_key', 'ap_northeast_1_access_key', 'ap_northeast_1_secret_key', 'ap_northeast_2_access_key', 'ap_northeast_2_secret_key', 'ap_southeast_1_access_key', 'ap_southeast_1_secret_key', 'ap_southeast_2_access_key', 'ap_southeast_2_secret_key', 'ap_east_1_access_key', 'ap_east_1_secret_key', 'ap_south_1_access_key', 'ap_south_1_secret_key', 'sa_east_1_access_key', 'sa_east_1_secret_key', ): if settings.app.cloud_provider != 'aws': settings_commit = True setattr(settings.app, aws_key, None) elif aws_key in flask.request.json: settings_commit = True aws_value = flask.request.json[aws_key] if aws_value: setattr(settings.app, aws_key, utils.filter_str(aws_value)) else: setattr(settings.app, aws_key, None) if not settings.app.sso: settings.app.sso_match = None settings.app.sso_azure_directory_id = None settings.app.sso_azure_app_id = None settings.app.sso_azure_app_secret = None settings.app.sso_authzero_directory_id = None settings.app.sso_authzero_app_id = None settings.app.sso_authzero_app_secret = None settings.app.sso_google_key = None settings.app.sso_google_email = None settings.app.sso_duo_token = None settings.app.sso_duo_secret = None settings.app.sso_duo_host = None settings.app.sso_org = None settings.app.sso_saml_url = None settings.app.sso_saml_issuer_url = None settings.app.sso_saml_cert = None settings.app.sso_okta_app_id = None settings.app.sso_okta_token = None settings.app.sso_onelogin_key = None settings.app.sso_onelogin_app_id = None settings.app.sso_onelogin_id = None settings.app.sso_onelogin_secret = None settings.app.sso_radius_secret = None settings.app.sso_radius_host = None else: if RADIUS_AUTH in settings.app.sso and \ settings.app.sso_duo_mode == 'passcode': return utils.jsonify( { 'error': RADIUS_DUO_PASSCODE, 'error_msg': RADIUS_DUO_PASSCODE_MSG, }, 400) if settings.app.sso == DUO_AUTH and \ settings.app.sso_duo_mode == 'passcode': return utils.jsonify( { 'error': DUO_PASSCODE, 'error_msg': DUO_PASSCODE_MSG, }, 400) for change in changes: remote_addr = utils.get_remote_addr() flask.g.administrator.audit_event( 'admin_settings', _changes_audit_text[change], remote_addr=remote_addr, ) journal.entry( journal.SETTINGS_UPDATE, remote_address=remote_addr, event_long='Settings updated', changed=_changes_audit_text[change], ) if settings_commit: settings.commit() admin.commit(admin.changed) if admin_event: event.Event(type=ADMINS_UPDATED) if org_event: for org in organization.iter_orgs(fields=('_id')): event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SETTINGS_UPDATED) if update_acme: try: acme.update_acme_cert() app.update_server(0.5) except: logger.exception( 'Failed to get LetsEncrypt cert', 'handler', acme_domain=settings.app.acme_domain, ) settings.app.acme_domain = None settings.app.acme_key = None settings.app.acme_timestamp = None settings.commit() return utils.jsonify( { 'error': ACME_ERROR, 'error_msg': ACME_ERROR_MSG, }, 400) elif update_cert: logger.info('Regenerating server certificate...', 'handler') utils.create_server_cert() app.update_server(0.5) elif update_server: app.update_server(0.5) response = flask.g.administrator.dict() response.update(_dict()) return utils.jsonify(response)
def _run_server(restart): global app_server try: context = subprocess.check_output( ['id', '-Z'], stderr=subprocess.PIPE, ).strip() except: context = 'none' journal.entry( journal.WEB_SERVER_START, selinux_context=context, ) logger.info('Starting server', 'app', selinux_context=context, ) app_server = cheroot.wsgi.Server( ('localhost', settings.app.server_internal_port), app, request_queue_size=settings.app.request_queue_size, accepted_queue_size=settings.app.request_accepted_queue_size, numthreads=settings.app.request_thread_count, max=settings.app.request_max_thread_count, shutdown_timeout=3, ) app_server.server_name = '' server_cert_path = None server_key_path = None redirect_server = 'true' if settings.app.redirect_server else 'false' internal_addr = 'localhost:%s' % settings.app.server_internal_port app.config.update( SESSION_COOKIE_HTTPONLY=True, SESSION_COOKIE_SECURE=True, ) if settings.app.server_ssl: setup_server_cert() server_cert_path, server_key_path = utils.write_server_cert( settings.app.server_cert, settings.app.server_key, settings.app.acme_domain, ) if not restart: settings.local.server_ready.set() settings.local.server_start.wait() process_state = True process = subprocess.Popen( ['pritunl-web'], env=dict(os.environ, **{ 'REVERSE_PROXY_HEADER': settings.app.reverse_proxy_header if \ settings.app.reverse_proxy else '', 'REVERSE_PROXY_PROTO_HEADER': \ settings.app.reverse_proxy_proto_header if \ settings.app.reverse_proxy else '', 'REDIRECT_SERVER': redirect_server, 'BIND_HOST': settings.conf.bind_addr, 'BIND_PORT': str(settings.app.server_port), 'INTERNAL_ADDRESS': internal_addr, 'CERT_PATH': server_cert_path or '', 'KEY_PATH': server_key_path or '', }), ) def poll_thread(): time.sleep(0.5) if process.wait() and process_state: time.sleep(0.25) if not check_global_interrupt(): stdout, stderr = process._communicate(None) logger.error('Web server process exited unexpectedly', 'app', stdout=stdout, stderr=stderr, ) time.sleep(1) restart_server(1) thread = threading.Thread(target=poll_thread) thread.daemon = True thread.start() _watch_event.set() try: app_server.start() except (KeyboardInterrupt, SystemExit): return except ServerRestart: raise except: logger.exception('Server error occurred', 'app') raise finally: process_state = False try: process.kill() except: pass
def key_wg_post(org_id, user_id, server_id): org_id = org_id user_id = user_id server_id = server_id remote_addr = utils.get_remote_addr() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Missing auth header', ) return flask.abort(406) auth_token = auth_token[:256] auth_timestamp = auth_timestamp[:64] auth_nonce = auth_nonce[:32] auth_signature = auth_signature[:512] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Expired auth timestamp', ) return flask.abort(408) except ValueError: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Invalid auth timestamp', ) return flask.abort(405) org = organization.get_by_id(org_id) if not org: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='Organization not found', ) return flask.abort(404) usr = org.get_user(id=user_id) if not usr: journal.entry( journal.USER_WG_FAILURE, remote_address=remote_addr, event_long='User not found', ) return flask.abort(404) elif not usr.sync_secret: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User missing sync secret', ) return flask.abort(410) if auth_token != usr.sync_token: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync token mismatch', ) return flask.abort(411) if usr.disabled: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) cipher_data64 = flask.request.json.get('data') box_nonce64 = flask.request.json.get('nonce') public_key64 = flask.request.json.get('public_key') signature64 = flask.request.json.get('signature') auth_string = '&'.join([ usr.sync_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path, cipher_data64, box_nonce64, public_key64, signature64 ]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Auth string len limit exceeded', ) return flask.abort(414) auth_test_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), auth_string, hashlib.sha512).digest()) if not utils.const_compare(auth_signature, auth_test_signature): journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Auth signature mismatch', ) return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate nonce', ) return flask.abort(409) data_hash = hashlib.sha512('&'.join( [cipher_data64, box_nonce64, public_key64])).digest() try: usr.verify_sig( data_hash, base64.b64decode(signature64), ) except InvalidSignature: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Invalid rsa signature', ) return flask.abort(412) svr = usr.get_server(server_id) sender_pub_key = nacl.public.PublicKey(base64.b64decode(public_key64)) box_nonce = base64.b64decode(box_nonce64) priv_key = nacl.public.PrivateKey( base64.b64decode(svr.auth_box_private_key)) cipher_data = base64.b64decode(cipher_data64) nacl_box = nacl.public.Box(priv_key, sender_pub_key) plaintext = nacl_box.decrypt(cipher_data, box_nonce).decode('utf-8') try: nonces_collection.insert({ 'token': auth_token, 'nonce': box_nonce64, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate secondary nonce', ) return flask.abort(415) key_data = json.loads(plaintext) client_platform = utils.filter_str_uni(key_data['platform']) client_device_id = utils.filter_str_uni(key_data['device_id']) client_device_name = utils.filter_str_uni(key_data['device_name']) client_mac_addr = utils.filter_str_uni(key_data['mac_addr']) client_mac_addrs = key_data['mac_addrs'] if client_mac_addrs: client_mac_addrs = [utils.filter_str_uni(x) for x in client_mac_addrs] else: client_mac_addrs = None client_auth_token = key_data['token'].decode('utf-8') client_auth_nonce = utils.filter_str_uni(key_data['nonce']) client_auth_password = key_data['password'].decode('utf-8') client_auth_timestamp = int(key_data['timestamp']) client_wg_public_key = key_data['wg_public_key'].decode('utf-8') if len(client_wg_public_key) < 32: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Public key too short', ) return flask.abort(416) try: client_wg_public_key = base64.b64decode(client_wg_public_key) if len(client_wg_public_key) != 32: raise ValueError('Invalid length') client_wg_public_key = base64.b64encode(client_wg_public_key) except: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Public key invalid', ) return flask.abort(417) instance = server.get_instance(server_id) if not instance or instance.state != 'running': return flask.abort(429) if not instance.server.wg: return flask.abort(429) wg_keys_collection = mongo.get_collection('wg_keys') try: wg_keys_collection.insert({ '_id': client_wg_public_key, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_WG_FAILURE, usr.journal_data, remote_address=remote_addr, wg_public_key=client_wg_public_key, event_long='Duplicate wg public key', ) return flask.abort(413) clients = instance.instance_com.clients event = threading.Event() send_data = { 'allow': None, 'configuration': None, 'reason': None, } def callback(allow, data): send_data['allow'] = allow if allow: send_data['configuration'] = data else: send_data['reason'] = data event.set() clients.connect_wg( user=usr, org=org, wg_public_key=client_wg_public_key, auth_password=client_auth_password, auth_token=client_auth_token, auth_nonce=client_auth_nonce, auth_timestamp=client_auth_timestamp, platform=client_platform, device_id=client_device_id, device_name=client_device_name, mac_addr=client_mac_addr, mac_addrs=client_mac_addrs, remote_ip=remote_addr, connect_callback=callback, ) event.wait() send_nonce = nacl.utils.random(nacl.public.Box.NONCE_SIZE) nacl_box = nacl.public.Box(priv_key, sender_pub_key) send_cipher_data = nacl_box.encrypt(json.dumps(send_data), send_nonce) send_cipher_data = send_cipher_data[nacl.public.Box.NONCE_SIZE:] send_nonce64 = base64.b64encode(send_nonce) send_cipher_data64 = base64.b64encode(send_cipher_data) usr.audit_event( 'user_profile', 'User retrieved wg public key from pritunl client', remote_addr=remote_addr, ) journal.entry( journal.USER_WG_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User retrieved wg public key from pritunl client', ) sync_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), send_cipher_data64 + '&' + send_nonce64, hashlib.sha512).digest()) return utils.jsonify({ 'data': send_cipher_data64, 'nonce': send_nonce64, 'signature': sync_signature, })
def key_sync_get(org_id, user_id, server_id, key_hash): remote_addr = utils.get_remote_addr() if not settings.user.conf_sync: return utils.jsonify({}) if not settings.local.sub_active: return utils.jsonify({}, status_code=480) utils.rand_sleep() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Missing auth header', ) return flask.abort(406) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Expired auth timestamp', ) return flask.abort(408) except ValueError: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Invalid auth timestamp', ) return flask.abort(405) org = organization.get_by_id(org_id) if not org: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Organization not found', ) return flask.abort(404) usr = org.get_user(id=user_id) if not usr: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='User not found', ) return flask.abort(404) elif not usr.sync_secret: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User missing sync secret', ) return flask.abort(410) if auth_token != usr.sync_token: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync token mismatch', ) return flask.abort(410) if usr.disabled: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) auth_string = '&'.join([ usr.sync_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Auth string len limit exceeded', ) return flask.abort(413) auth_test_signature = base64.b64encode(hmac.new( usr.sync_secret.encode(), auth_string, hashlib.sha512).digest()) if not utils.const_compare(auth_signature, auth_test_signature): journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync signature mismatch', ) return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate key', ) return flask.abort(409) key_conf = usr.sync_conf(server_id, key_hash) if key_conf: usr.audit_event('user_profile', 'User profile synced from pritunl client', remote_addr=remote_addr, ) journal.entry( journal.USER_SYNC_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User profile synced from pritunl client', ) sync_signature = base64.b64encode(hmac.new( usr.sync_secret.encode(), key_conf['conf'], hashlib.sha512).digest()) return utils.jsonify({ 'signature': sync_signature, 'conf': key_conf['conf'], }) return utils.jsonify({})
def _auth_plugin(username, password, remote_addr): if not settings.local.sub_plan or \ 'enterprise' not in settings.local.sub_plan: journal.entry( journal.ADMIN_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.ADMIN_AUTH_REASON_INVALID_USERNAME, reason_long='Invalid username', ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) has_plugin, valid, org_id, groups = sso.plugin_login_authenticate( user_name=username, password=password, remote_ip=remote_addr, ) if not has_plugin: journal.entry( journal.ADMIN_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.ADMIN_AUTH_REASON_INVALID_USERNAME, reason_long='Invalid username', ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if not valid: journal.entry( journal.SSO_AUTH_REASON_PLUGIN_FAILED, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Plugin authentication failed', ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if not org_id: logger.error( 'Login plugin did not return valid organization name', 'auth', org_name=org_id, user_name=username, ) return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'auth', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, type=CERT_CLIENT, auth_type=PLUGIN_AUTH, groups=list(groups) if groups else None) usr.audit_event( 'user_created', 'User created with plugin authentication', remote_addr=utils.get_remote_addr(), ) journal.entry( journal.USER_CREATE, usr.journal_data, event_long='User created with plugin authentication', 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 usr.disabled: return utils.jsonify( { 'error': AUTH_DISABLED, 'error_msg': AUTH_DISABLED_MSG, }, 403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != PLUGIN_AUTH: usr.auth_type = PLUGIN_AUTH usr.set_pin(None) usr.commit(('auth_type', 'pin')) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from plugin authentication', remote_addr=utils.get_remote_addr(), ) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, event_long='User profile viewed from plugin authentication', remote_address=remote_addr, ) return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 202)
def user_linked_key_page_get(short_code): remote_addr = utils.get_remote_addr() doc = _find_doc({ 'short_id': short_code, }, one_time=True, one_time_new=True) if not doc: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='Key ID not found', ) return flask.abort(404) org = organization.get_by_id(doc['org_id']) usr = org.get_user(id=doc['user_id']) if usr.disabled: journal.entry( journal.USER_PROFILE_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User temporary profile link viewed', ) usr.audit_event('user_profile', 'User temporary profile link viewed', remote_addr=remote_addr, ) if settings.local.sub_active and settings.app.theme == 'dark': view_name = KEY_VIEW_DARK_NAME else: view_name = KEY_VIEW_NAME if RADIUS_AUTH in usr.auth_type or \ settings.user.pin_mode == PIN_DISABLED: header_class = 'pin-disabled' else: header_class = '' key_page = static.StaticFile(settings.conf.www_path, view_name, cache=False, gzip=False).data uri_url = (utils.get_url_root() + '/ku/' + doc['short_id']).encode() if uri_url.startswith('https'): uri_url = uri_url.replace('https', 'pritunl', 1) else: uri_url = uri_url.replace('http', 'pritunl', 1) key_page = key_page.replace('<%= uri_url %>', uri_url) key_page = key_page.replace('<%= user_name %>', '%s - %s' % ( org.name, usr.name)) key_page = key_page.replace('<%= user_key_tar_url %>', '/key/%s.tar' % ( doc['key_id'])) key_page = key_page.replace('<%= user_key_zip_url %>', '/key/%s.zip' % ( doc['key_id'])) if org.otp_auth and not usr.has_duo_passcode and not usr.has_yubikey: key_page = key_page.replace('<%= user_otp_key %>', usr.otp_secret) key_page = key_page.replace('<%= user_otp_url %>', 'otpauth://totp/%s@%s?secret=%s' % ( usr.name, org.name, usr.otp_secret)) else: key_page = key_page.replace('<%= user_otp_key %>', '') key_page = key_page.replace('<%= user_otp_url %>', '') if usr.pin: key_page = key_page.replace('<%= cur_pin_display %>', 'block') else: key_page = key_page.replace('<%= cur_pin_display %>', 'none') key_page = key_page.replace('<%= key_id %>', doc['key_id']) key_page = key_page.replace('<%= short_id %>', doc['short_id']) conf_links = '' if settings.local.sub_active: conf_links += '<a class="btn btn-success download-chrome" ' + \ 'title="Download Chrome OS Profile" ' + \ 'href="/key_onc/%s.onc">Download Chrome OS Profile</a>\n' % ( doc['key_id']) has_servers = False for server in usr.iter_servers(): has_servers = True conf_links += '<a class="btn btn-sm download-profile" ' + \ 'title="Download Profile" ' + \ 'href="/key/%s/%s.key">Download Profile (%s)</a>\n' % ( doc['key_id'], server.id, server.name) key_page = key_page.replace('<%= conf_links %>', conf_links) if not has_servers: header_class += 'no-servers' key_page = key_page.replace('<%= header_class %>', header_class) return key_page
def _create_user(users, org, user_data, remote_addr, pool): name = utils.filter_str(user_data['name']) email = utils.filter_str(user_data.get('email')) auth_type = utils.filter_str(user_data.get('auth_type')) pin = utils.filter_str(user_data.get('pin')) or None disabled = True if user_data.get('disabled') else False network_links = user_data.get('network_links') or None bypass_secondary = True if user_data.get('bypass_secondary') else False client_to_client = True if user_data.get('client_to_client') else False dns_servers = user_data.get('dns_servers') or None dns_suffix = utils.filter_str(user_data.get('dns_suffix')) or None port_forwarding_in = user_data.get('port_forwarding') port_forwarding = [] if auth_type not in AUTH_TYPES: auth_type = LOCAL_AUTH if auth_type == YUBICO_AUTH: yubico_id = user_data.get('yubico_id') yubico_id = yubico_id[:12] if yubico_id else None else: yubico_id = None groups = user_data.get('groups') or [] for i, group in enumerate(groups): groups[i] = utils.filter_str(group) groups = list(set(groups)) if pin: if settings.user.pin_digits_only and not pin.isdigit(): return utils.jsonify( { 'error': PIN_NOT_DIGITS, 'error_msg': PIN_NOT_DIGITS_MSG, }, 400) if len(pin) < settings.user.pin_min_length: return utils.jsonify( { 'error': PIN_TOO_SHORT, 'error_msg': PIN_TOO_SHORT_MSG, }, 400) pin = auth.generate_hash_pin_v2(pin) if bypass_secondary: if pin: return utils.jsonify( { 'error': PIN_BYPASS_SECONDARY, 'error_msg': PIN_BYPASS_SECONDARY_MSG, }, 400) if yubico_id: return utils.jsonify( { 'error': YUBIKEY_BYPASS_SECONDARY, 'error_msg': YUBIKEY_BYPASS_SECONDARY_MSG, }, 400) if port_forwarding_in: for data in port_forwarding_in: port_forwarding.append({ 'protocol': utils.filter_str(data.get('protocol')), 'port': utils.filter_str(data.get('port')), 'dport': utils.filter_str(data.get('dport')), }) user = org.new_user(type=CERT_CLIENT, pool=pool, name=name, email=email, auth_type=auth_type, yubico_id=yubico_id, groups=groups, pin=pin, disabled=disabled, bypass_secondary=bypass_secondary, client_to_client=client_to_client, dns_servers=dns_servers, dns_suffix=dns_suffix, port_forwarding=port_forwarding) user.audit_event( 'user_created', 'User created from web console', remote_addr=remote_addr, ) journal.entry( journal.USER_CREATE, user.journal_data, event_long='User created from web console', remote_address=remote_addr, ) if network_links: for network_link in network_links: try: user.add_network_link(network_link) except (ipaddress.AddressValueError, ValueError): return _network_link_invalid() except ServerOnlineError: return utils.jsonify( { 'error': NETWORK_LINK_NOT_OFFLINE, 'error_msg': NETWORK_LINK_NOT_OFFLINE_MSG, }, 400) users.append(user.dict())
def sso_authenticate_post(): if settings.app.sso != DUO_AUTH or \ settings.app.sso_duo_mode == 'passcode': return flask.abort(405) remote_addr = utils.get_remote_addr() username = utils.json_filter_str('username').lower() 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=remote_addr, auth_type='Key', ) valid = duo_auth.authenticate() break except InvalidUser: if i == len(usernames) - 1: logger.warning( '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=remote_addr, ) if not valid: logger.warning( 'Duo plugin authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Duo plugin authentication failed', ) return flask.abort(401) groups = set(groups or []) else: logger.warning( 'Duo authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_DUO_FAILED, reason_long='Duo authentication failed', ) return flask.abort(401) if not org_id: org_id = settings.app.sso_org return _validate_user(username, email, DUO_AUTH, org_id, groups, remote_addr)
def user_key_pin_put(key_id): if settings.app.demo_mode: return utils.demo_blocked() remote_addr = utils.get_remote_addr() doc = _find_doc({ 'key_id': key_id, }) if not doc: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='Key ID not found', ) return flask.abort(404) if settings.user.pin_mode == PIN_DISABLED: return utils.jsonify({ 'error': PIN_IS_DISABLED, 'error_msg': PIN_IS_DISABLED_MSG, }, 400) org = organization.get_by_id(doc['org_id']) usr = org.get_user(doc['user_id']) if usr.disabled: return flask.abort(403) if RADIUS_AUTH in usr.auth_type: return utils.jsonify({ 'error': PIN_RADIUS, 'error_msg': PIN_RADIUS_MSG, }, 400) current_pin = utils.filter_str( flask.request.json.get('current_pin')) or None pin = utils.filter_str(flask.request.json.get('pin')) or None if pin: if settings.user.pin_digits_only and not pin.isdigit(): return utils.jsonify({ 'error': PIN_NOT_DIGITS, 'error_msg': PIN_NOT_DIGITS_MSG, }, 400) if len(pin) < settings.user.pin_min_length: return utils.jsonify({ 'error': PIN_TOO_SHORT, 'error_msg': PIN_TOO_SHORT_MSG, }, 400) if usr.pin: if not limiter.auth_check(usr.id): return utils.jsonify({ 'error': AUTH_TOO_MANY, 'error_msg': AUTH_TOO_MANY_MSG, }, 400) if not usr.check_pin(current_pin): return utils.jsonify({ 'error': PIN_INVALID, 'error_msg': PIN_INVALID_MSG, }, 400) if usr.set_pin(pin): journal.entry( journal.USER_PIN_UPDATE, usr.journal_data, remote_address=remote_addr, event_long='User pin changed with temporary profile link', ) usr.audit_event('user_updated', 'User pin changed with temporary profile link', remote_addr=remote_addr, ) usr.commit() event.Event(type=USERS_UPDATED, resource_id=org.id) return utils.jsonify({})
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 _check_auth_data(self): if not self.auth_token and not self.auth_nonce and \ not self.auth_timestamp: return if not self.auth_nonce: self.user.audit_event( 'user_connection', 'User connection to "%s" denied. Auth data missing nonce' % \ self.server.name, remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Auth data missing nonce', ) raise AuthError('Auth data missing nonce') if not self.auth_timestamp: self.user.audit_event( 'user_connection', ('User connection to "%s" denied. ' + 'Auth data missing timestamp') % \ self.server.name, remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Auth data missing timestamp', ) raise AuthError('Auth data missing timestamp') if abs(int(self.auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: self.user.audit_event( 'user_connection', 'User connection to "%s" denied. Auth timestamp expired' % \ self.server.name, remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Auth timestamp expired', ) raise AuthError('Auth timestamp expired') if self.auth_token: auth_token_hash = hashlib.sha512() auth_token_hash.update(self.auth_token) auth_token = base64.b64encode(auth_token_hash.digest()) else: auth_token = None try: self.nonces_collection.insert({ 'token': self.user.id, 'nonce': self.auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: self.user.audit_event( 'user_connection', 'User connection to "%s" denied. Duplicate nonce' % \ self.server.name, remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='Duplicate nonce', ) raise AuthError('Duplicate nonce') self.password = self.auth_password self.server_auth_token = auth_token
def user_linked_key_conf_get(key_id, server_id): remote_addr = utils.get_remote_addr() doc = _find_doc({ 'key_id': key_id, }) if not doc: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='Key ID not found', ) return flask.abort(404) org = organization.get_by_id(doc['org_id']) if not org: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='Organization not found', ) return flask.abort(404) usr = org.get_user(id=doc['user_id']) if not usr: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='User not found', ) return flask.abort(404) if usr.disabled: journal.entry( journal.USER_PROFILE_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) key_conf = usr.build_key_conf(server_id) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User profile downloaded with temporary profile link', ) usr.audit_event('user_profile', 'User profile downloaded with temporary profile link', remote_addr=remote_addr, ) response = flask.Response(response=key_conf['conf'], mimetype='application/ovpn') response.headers.add('Content-Disposition', 'attachment; filename="%s"' % key_conf['name']) return response
def user_linked_key_page_get(short_code): remote_addr = utils.get_remote_addr() doc = _find_doc({ 'short_id': short_code, }, one_time=True, one_time_new=True) if not doc: journal.entry( journal.USER_PROFILE_FAILURE, remote_address=remote_addr, event_long='Key ID not found', ) return flask.abort(404) org = organization.get_by_id(doc['org_id']) usr = org.get_user(id=doc['user_id']) if usr.disabled: journal.entry( journal.USER_PROFILE_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User temporary profile link viewed', ) usr.audit_event( 'user_profile', 'User temporary profile link viewed', remote_addr=remote_addr, ) if settings.local.sub_active and settings.app.theme == 'dark': view_name = KEY_VIEW_DARK_NAME else: view_name = KEY_VIEW_NAME if RADIUS_AUTH in usr.auth_type or \ settings.user.pin_mode == PIN_DISABLED: header_class = 'pin-disabled' else: header_class = '' if settings.user.restrict_import: header_class += ' restrict-import' key_page = static.StaticFile(settings.conf.www_path, view_name, cache=False, gzip=False).data uri_url = (utils.get_url_root() + '/ku/' + doc['short_id']).encode() if uri_url.startswith('https'): uri_url = uri_url.replace('https', 'pritunl', 1) else: uri_url = uri_url.replace('http', 'pritunl', 1) key_page = key_page.replace('<%= uri_url %>', uri_url) key_page = key_page.replace('<%= user_name %>', '%s - %s' % (org.name, usr.name)) key_page = key_page.replace('<%= user_key_tar_url %>', '/key/%s.tar' % (doc['key_id'])) key_page = key_page.replace('<%= user_key_zip_url %>', '/key/%s.zip' % (doc['key_id'])) if org.otp_auth and not usr.has_duo_passcode and not usr.has_yubikey: key_page = key_page.replace('<%= user_otp_key %>', usr.otp_secret) key_page = key_page.replace( '<%= user_otp_url %>', 'otpauth://totp/%s@%s?secret=%s' % (usr.name, org.name, usr.otp_secret)) else: key_page = key_page.replace('<%= user_otp_key %>', '') key_page = key_page.replace('<%= user_otp_url %>', '') if usr.pin: key_page = key_page.replace('<%= cur_pin_display %>', 'block') else: key_page = key_page.replace('<%= cur_pin_display %>', 'none') key_page = key_page.replace('<%= key_id %>', doc['key_id']) key_page = key_page.replace('<%= short_id %>', doc['short_id']) conf_links = '' if settings.local.sub_active: conf_links += '<a class="btn btn-success download-chrome" ' + \ 'title="Download Chrome OS Profile" ' + \ 'href="/key_onc/%s.onc">Download Chrome OS Profile</a>\n' % ( doc['key_id']) has_servers = False for server in usr.iter_servers(): has_servers = True conf_links += '<a class="btn btn-sm download-profile" ' + \ 'title="Download Profile" ' + \ 'href="/key/%s/%s.key">Download Profile (%s)</a>\n' % ( doc['key_id'], server.id, server.name) key_page = key_page.replace('<%= conf_links %>', conf_links) if not has_servers: header_class += ' no-servers' key_page = key_page.replace('<%= header_class %>', header_class) return key_page
def _auth_push_thread(self): info = { 'Server': self.server.name, } platform_name = None if self.platform == 'linux': platform_name = 'Linux' elif self.platform == 'mac': platform_name = 'macOS' elif self.platform == 'ios': platform_name = 'iOS' elif self.platform == 'win': platform_name = 'Windows' elif self.platform == 'chrome': platform_name = 'Chrome OS' if self.device_name: info['Device'] = '%s (%s)' % (self.device_name, platform_name) onelogin_mode = utils.get_onelogin_mode() okta_mode = utils.get_okta_mode() if self.push_type == DUO_AUTH: duo_auth = sso.Duo( username=self.user.name, factor=settings.app.sso_duo_mode, remote_ip=self.remote_ip, auth_type='Connection', info=info, ) allow = duo_auth.authenticate() elif self.push_type == SAML_ONELOGIN_AUTH: allow = sso.auth_onelogin_secondary( username=self.user.name, passcode=None, remote_ip=self.remote_ip, onelogin_mode=onelogin_mode, ) elif self.push_type == SAML_OKTA_AUTH: allow = sso.auth_okta_secondary( username=self.user.name, passcode=None, remote_ip=self.remote_ip, okta_mode=okta_mode, ) else: raise ValueError('Unkown push auth type') if not allow: self.user.audit_event('user_connection', ('User connection to "%s" denied. ' + 'Push authentication failed') % ( self.server.name), remote_addr=self.remote_ip, ) journal.entry( journal.USER_CONNECT_FAILURE, self.journal_data, self.user.journal_data, self.server.journal_data, event_long='User failed push authentication', ) raise AuthError('User failed push authentication') if settings.app.sso_cache and not self.server_auth_token: self.sso_push_cache_collection.update({ 'user_id': self.user.id, 'server_id': self.server.id, 'mac_addr': self.mac_addr, 'device_id': self.device_id, 'device_name': self.device_name, }, { 'user_id': self.user.id, 'server_id': self.server.id, 'remote_ip': self.remote_ip, 'mac_addr': self.mac_addr, 'platform': self.platform, 'device_id': self.device_id, 'device_name': self.device_name, 'timestamp': utils.now(), }, upsert=True)
def sso_authenticate_post(): if settings.app.sso != DUO_AUTH or \ settings.app.sso_duo_mode == 'passcode': return flask.abort(405) remote_addr = utils.get_remote_addr() 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=remote_addr, auth_type='Key', ) valid = duo_auth.authenticate() break except InvalidUser: if i == len(usernames) - 1: logger.warning('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=remote_addr, ) if not valid: logger.warning('Duo plugin authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_PLUGIN_FAILED, reason_long='Duo plugin authentication failed', ) return flask.abort(401) groups = set(groups or []) else: logger.warning('Duo authentication not valid', 'sso', username=username, ) journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_DUO_FAILED, reason_long='Duo authentication failed', ) return flask.abort(401) if not org_id: org_id = settings.app.sso_org return _validate_user(username, email, DUO_AUTH, org_id, groups, remote_addr)
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 key_sync_get(org_id, user_id, server_id, key_hash): remote_addr = utils.get_remote_addr() if not settings.user.conf_sync: return utils.jsonify({}) if not settings.local.sub_active: return utils.jsonify({}, status_code=480) utils.rand_sleep() auth_token = flask.request.headers.get('Auth-Token', None) auth_timestamp = flask.request.headers.get('Auth-Timestamp', None) auth_nonce = flask.request.headers.get('Auth-Nonce', None) auth_signature = flask.request.headers.get('Auth-Signature', None) if not auth_token or not auth_timestamp or not auth_nonce or \ not auth_signature: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Missing auth header', ) return flask.abort(406) auth_nonce = auth_nonce[:32] try: if abs(int(auth_timestamp) - int(utils.time_now())) > \ settings.app.auth_time_window: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Expired auth timestamp', ) return flask.abort(408) except ValueError: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Invalid auth timestamp', ) return flask.abort(405) org = organization.get_by_id(org_id) if not org: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='Organization not found', ) return flask.abort(404) usr = org.get_user(id=user_id) if not usr: journal.entry( journal.USER_SYNC_FAILURE, remote_address=remote_addr, event_long='User not found', ) return flask.abort(404) elif not usr.sync_secret: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User missing sync secret', ) return flask.abort(410) if auth_token != usr.sync_token: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync token mismatch', ) return flask.abort(410) if usr.disabled: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='User disabled', ) return flask.abort(403) auth_string = '&'.join([ usr.sync_token, auth_timestamp, auth_nonce, flask.request.method, flask.request.path ]) if len(auth_string) > AUTH_SIG_STRING_MAX_LEN: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Auth string len limit exceeded', ) return flask.abort(413) auth_test_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), auth_string, hashlib.sha512).digest()) if not utils.const_compare(auth_signature, auth_test_signature): journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Sync signature mismatch', ) return flask.abort(401) nonces_collection = mongo.get_collection('auth_nonces') try: nonces_collection.insert({ 'token': auth_token, 'nonce': auth_nonce, 'timestamp': utils.now(), }) except pymongo.errors.DuplicateKeyError: journal.entry( journal.USER_SYNC_FAILURE, usr.journal_data, remote_address=remote_addr, event_long='Duplicate key', ) return flask.abort(409) key_conf = usr.sync_conf(server_id, key_hash) if key_conf: usr.audit_event( 'user_profile', 'User profile synced from pritunl client', remote_addr=remote_addr, ) journal.entry( journal.USER_SYNC_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User profile synced from pritunl client', ) sync_signature = base64.b64encode( hmac.new(usr.sync_secret.encode(), key_conf['conf'], hashlib.sha512).digest()) return utils.jsonify({ 'signature': sync_signature, 'conf': key_conf['conf'], }) return utils.jsonify({})
def settings_put(): if settings.app.demo_mode: return utils.demo_blocked() org_event = False admin_event = False admin = flask.g.administrator changes = set() settings_commit = False update_server = False update_acme = False update_cert = False if 'username' in flask.request.json and flask.request.json['username']: username = utils.filter_str( flask.request.json['username']).lower() if username != admin.username: changes.add('username') admin.username = username if 'password' in flask.request.json and flask.request.json['password']: password = flask.request.json['password'] changes.add('password') admin.password = password if 'server_cert' in flask.request.json: settings_commit = True server_cert = flask.request.json['server_cert'] if server_cert: server_cert = server_cert.strip() else: server_cert = None if server_cert != settings.app.server_cert: update_server = True settings.app.server_cert = server_cert if 'server_key' in flask.request.json: settings_commit = True server_key = flask.request.json['server_key'] if server_key: server_key = server_key.strip() else: server_key = None if server_key != settings.app.server_key: update_server = True settings.app.server_key = server_key if 'server_port' in flask.request.json: settings_commit = True server_port = flask.request.json['server_port'] if not server_port: server_port = 443 try: server_port = int(server_port) if server_port < 1 or server_port > 65535: raise ValueError('Port invalid') except ValueError: return utils.jsonify({ 'error': PORT_INVALID, 'error_msg': PORT_INVALID_MSG, }, 400) if server_port != settings.app.server_port: update_server = True settings.app.server_port = server_port if 'acme_domain' in flask.request.json: settings_commit = True acme_domain = utils.filter_str( flask.request.json['acme_domain'] or None) if acme_domain: acme_domain = acme_domain.replace('https://', '') acme_domain = acme_domain.replace('http://', '') acme_domain = acme_domain.replace('/', '') if acme_domain != settings.app.acme_domain: if not acme_domain: settings.app.acme_key = None settings.app.acme_timestamp = None settings.app.server_key = None settings.app.server_cert = None update_server = True update_cert = True else: update_acme = True settings.app.acme_domain = acme_domain if 'auditing' in flask.request.json: settings_commit = True auditing = flask.request.json['auditing'] or None if settings.app.auditing == ALL and auditing != ALL: return utils.jsonify({ 'error': CANNOT_DISABLE_AUTIDING, 'error_msg': CANNOT_DISABLE_AUTIDING_MSG, }, 400) if settings.app.auditing != auditing: if not flask.g.administrator.super_user: return utils.jsonify({ 'error': REQUIRES_SUPER_USER, 'error_msg': REQUIRES_SUPER_USER_MSG, }, 400) admin_event = True org_event = True settings.app.auditing = auditing if 'monitoring' in flask.request.json: settings_commit = True monitoring = flask.request.json['monitoring'] or None settings.app.monitoring = monitoring if 'influxdb_uri' in flask.request.json: settings_commit = True influxdb_uri = flask.request.json['influxdb_uri'] or None settings.app.influxdb_uri = influxdb_uri if 'email_from' in flask.request.json: settings_commit = True email_from = flask.request.json['email_from'] or None if email_from != settings.app.email_from: changes.add('smtp') settings.app.email_from = email_from if 'email_server' in flask.request.json: settings_commit = True email_server = flask.request.json['email_server'] or None if email_server != settings.app.email_server: changes.add('smtp') settings.app.email_server = email_server if 'email_username' in flask.request.json: settings_commit = True email_username = flask.request.json['email_username'] or None if email_username != settings.app.email_username: changes.add('smtp') settings.app.email_username = email_username if 'email_password' in flask.request.json: settings_commit = True email_password = flask.request.json['email_password'] or None if email_password != settings.app.email_password: changes.add('smtp') settings.app.email_password = email_password if 'pin_mode' in flask.request.json: settings_commit = True pin_mode = flask.request.json['pin_mode'] or None if pin_mode != settings.user.pin_mode: changes.add('pin_mode') settings.user.pin_mode = pin_mode if 'sso' in flask.request.json: org_event = True settings_commit = True sso = flask.request.json['sso'] or None if sso != settings.app.sso: changes.add('sso') settings.app.sso = sso if 'sso_match' in flask.request.json: settings_commit = True sso_match = flask.request.json['sso_match'] or None if sso_match != settings.app.sso_match: changes.add('sso') if isinstance(sso_match, list): settings.app.sso_match = sso_match else: settings.app.sso_match = None if 'sso_azure_directory_id' in flask.request.json: settings_commit = True sso_azure_directory_id = flask.request.json[ 'sso_azure_directory_id'] or None if sso_azure_directory_id != settings.app.sso_azure_directory_id: changes.add('sso') settings.app.sso_azure_directory_id = sso_azure_directory_id if 'sso_azure_app_id' in flask.request.json: settings_commit = True sso_azure_app_id = flask.request.json['sso_azure_app_id'] or None if sso_azure_app_id != settings.app.sso_azure_app_id: changes.add('sso') settings.app.sso_azure_app_id = sso_azure_app_id if 'sso_azure_app_secret' in flask.request.json: settings_commit = True sso_azure_app_secret = flask.request.json[ 'sso_azure_app_secret'] or None if sso_azure_app_secret != settings.app.sso_azure_app_secret: changes.add('sso') settings.app.sso_azure_app_secret = sso_azure_app_secret if 'sso_authzero_domain' in flask.request.json: settings_commit = True sso_authzero_domain = flask.request.json[ 'sso_authzero_domain'] or None if sso_authzero_domain != settings.app.sso_authzero_domain: changes.add('sso') settings.app.sso_authzero_domain = sso_authzero_domain if 'sso_authzero_app_id' in flask.request.json: settings_commit = True sso_authzero_app_id = flask.request.json[ 'sso_authzero_app_id'] or None if sso_authzero_app_id != settings.app.sso_authzero_app_id: changes.add('sso') settings.app.sso_authzero_app_id = sso_authzero_app_id if 'sso_authzero_app_secret' in flask.request.json: settings_commit = True sso_authzero_app_secret = flask.request.json[ 'sso_authzero_app_secret'] or None if sso_authzero_app_secret != settings.app.sso_authzero_app_secret: changes.add('sso') settings.app.sso_authzero_app_secret = sso_authzero_app_secret if 'sso_google_key' in flask.request.json: settings_commit = True sso_google_key = flask.request.json['sso_google_key'] or None if sso_google_key != settings.app.sso_google_key: changes.add('sso') settings.app.sso_google_key = sso_google_key if 'sso_google_email' in flask.request.json: settings_commit = True sso_google_email = flask.request.json['sso_google_email'] or None if sso_google_email != settings.app.sso_google_email: changes.add('sso') settings.app.sso_google_email = sso_google_email if 'sso_duo_token' in flask.request.json: settings_commit = True sso_duo_token = flask.request.json['sso_duo_token'] or None if sso_duo_token != settings.app.sso_duo_token: changes.add('sso') settings.app.sso_duo_token = sso_duo_token if 'sso_duo_secret' in flask.request.json: settings_commit = True sso_duo_secret = flask.request.json['sso_duo_secret'] or None if sso_duo_secret != settings.app.sso_duo_secret: changes.add('sso') settings.app.sso_duo_secret = sso_duo_secret if 'sso_duo_host' in flask.request.json: settings_commit = True sso_duo_host = flask.request.json['sso_duo_host'] or None if sso_duo_host != settings.app.sso_duo_host: changes.add('sso') settings.app.sso_duo_host = sso_duo_host if 'sso_duo_mode' in flask.request.json: settings_commit = True sso_duo_mode = flask.request.json['sso_duo_mode'] or None if sso_duo_mode != settings.app.sso_duo_mode: changes.add('sso') settings.app.sso_duo_mode = sso_duo_mode if 'sso_radius_secret' in flask.request.json: settings_commit = True sso_radius_secret = flask.request.json['sso_radius_secret'] or None if sso_radius_secret != settings.app.sso_radius_secret: changes.add('sso') settings.app.sso_radius_secret = sso_radius_secret if 'sso_radius_host' in flask.request.json: settings_commit = True sso_radius_host = flask.request.json['sso_radius_host'] or None if sso_radius_host != settings.app.sso_radius_host: changes.add('sso') settings.app.sso_radius_host = sso_radius_host if 'sso_org' in flask.request.json: settings_commit = True sso_org = flask.request.json['sso_org'] or None if sso_org: sso_org = utils.ObjectId(sso_org) else: sso_org = None if sso_org != settings.app.sso_org: changes.add('sso') if settings.app.sso and not sso_org: return utils.jsonify({ 'error': SSO_ORG_NULL, 'error_msg': SSO_ORG_NULL_MSG, }, 400) settings.app.sso_org = sso_org if 'sso_saml_url' in flask.request.json: settings_commit = True sso_saml_url = flask.request.json['sso_saml_url'] or None if sso_saml_url != settings.app.sso_saml_url: changes.add('sso') settings.app.sso_saml_url = sso_saml_url if 'sso_saml_issuer_url' in flask.request.json: settings_commit = True sso_saml_issuer_url = flask.request.json['sso_saml_issuer_url'] or \ None if sso_saml_issuer_url != settings.app.sso_saml_issuer_url: changes.add('sso') settings.app.sso_saml_issuer_url = sso_saml_issuer_url if 'sso_saml_cert' in flask.request.json: settings_commit = True sso_saml_cert = flask.request.json['sso_saml_cert'] or None if sso_saml_cert != settings.app.sso_saml_cert: changes.add('sso') settings.app.sso_saml_cert = sso_saml_cert if 'sso_okta_app_id' in flask.request.json: settings_commit = True sso_okta_app_id = flask.request.json['sso_okta_app_id'] or None if sso_okta_app_id != settings.app.sso_okta_app_id: changes.add('sso') settings.app.sso_okta_app_id = sso_okta_app_id if 'sso_okta_token' in flask.request.json: settings_commit = True sso_okta_token = flask.request.json['sso_okta_token'] or None if sso_okta_token != settings.app.sso_okta_token: changes.add('sso') settings.app.sso_okta_token = sso_okta_token if 'sso_okta_mode' in flask.request.json: sso_mode = settings.app.sso if sso_mode and sso_mode == SAML_OKTA_AUTH: settings_commit = True sso_okta_mode = flask.request.json['sso_okta_mode'] settings.app.sso_okta_mode = sso_okta_mode if 'sso_onelogin_app_id' in flask.request.json: settings_commit = True sso_onelogin_app_id = flask.request.json['sso_onelogin_app_id'] or \ None if sso_onelogin_app_id != settings.app.sso_onelogin_app_id: changes.add('sso') settings.app.sso_onelogin_app_id = sso_onelogin_app_id if 'sso_onelogin_id' in flask.request.json: settings_commit = True sso_onelogin_id = flask.request.json['sso_onelogin_id'] or None if sso_onelogin_id != settings.app.sso_onelogin_id: changes.add('sso') settings.app.sso_onelogin_id = sso_onelogin_id if 'sso_onelogin_secret' in flask.request.json: settings_commit = True sso_onelogin_secret = \ flask.request.json['sso_onelogin_secret'] or None if sso_onelogin_secret != settings.app.sso_onelogin_secret: changes.add('sso') settings.app.sso_onelogin_secret = sso_onelogin_secret if 'sso_onelogin_mode' in flask.request.json: sso_mode = settings.app.sso if sso_mode and sso_mode == SAML_ONELOGIN_AUTH: settings_commit = True sso_onelogin_mode = flask.request.json['sso_onelogin_mode'] settings.app.sso_onelogin_mode = sso_onelogin_mode if 'sso_cache' in flask.request.json: settings_commit = True sso_cache = True if \ flask.request.json['sso_cache'] else False if sso_cache != settings.app.sso_cache: changes.add('sso') settings.app.sso_cache = sso_cache if 'sso_client_cache' in flask.request.json: settings_commit = True sso_client_cache = True if \ flask.request.json['sso_client_cache'] else False if sso_client_cache != settings.app.sso_client_cache: changes.add('sso') settings.app.sso_client_cache = sso_client_cache if 'client_reconnect' in flask.request.json: settings_commit = True client_reconnect = True if \ flask.request.json['client_reconnect'] else False settings.user.reconnect = client_reconnect if 'sso_yubico_client' in flask.request.json: settings_commit = True sso_yubico_client = \ flask.request.json['sso_yubico_client'] or None if sso_yubico_client != settings.app.sso_yubico_client: changes.add('sso') settings.app.sso_yubico_client = sso_yubico_client if 'sso_yubico_secret' in flask.request.json: settings_commit = True sso_yubico_secret = \ flask.request.json['sso_yubico_secret'] or None if sso_yubico_secret != settings.app.sso_yubico_secret: changes.add('sso') settings.app.sso_yubico_secret = sso_yubico_secret if flask.request.json.get('theme'): settings_commit = True theme = 'light' if flask.request.json['theme'] == 'light' else 'dark' if theme != settings.app.theme: if theme == 'dark': event.Event(type=THEME_DARK) else: event.Event(type=THEME_LIGHT) settings.app.theme = theme if 'public_address' in flask.request.json: public_address = flask.request.json['public_address'] or None if public_address != settings.local.host.public_addr: settings.local.host.public_address = public_address settings.local.host.commit('public_address') if 'public_address6' in flask.request.json: public_address6 = flask.request.json['public_address6'] or None if public_address6 != settings.local.host.public_addr6: settings.local.host.public_address6 = public_address6 settings.local.host.commit('public_address6') if 'routed_subnet6' in flask.request.json: routed_subnet6 = flask.request.json['routed_subnet6'] if routed_subnet6: try: routed_subnet6 = ipaddress.IPv6Network( flask.request.json['routed_subnet6']) except (ipaddress.AddressValueError, ValueError): return utils.jsonify({ 'error': IPV6_SUBNET_INVALID, 'error_msg': IPV6_SUBNET_INVALID_MSG, }, 400) if routed_subnet6.prefixlen > 64: return utils.jsonify({ 'error': IPV6_SUBNET_SIZE_INVALID, 'error_msg': IPV6_SUBNET_SIZE_INVALID_MSG, }, 400) routed_subnet6 = str(routed_subnet6) else: routed_subnet6 = None if settings.local.host.routed_subnet6 != routed_subnet6: if server.get_online_ipv6_count(): return utils.jsonify({ 'error': IPV6_SUBNET_ONLINE, 'error_msg': IPV6_SUBNET_ONLINE_MSG, }, 400) settings.local.host.routed_subnet6 = routed_subnet6 settings.local.host.commit('routed_subnet6') if 'reverse_proxy' in flask.request.json: settings_commit = True reverse_proxy = flask.request.json['reverse_proxy'] settings.app.reverse_proxy = True if reverse_proxy else False if 'cloud_provider' in flask.request.json: settings_commit = True cloud_provider = flask.request.json['cloud_provider'] or None settings.app.cloud_provider = cloud_provider if 'route53_region' in flask.request.json: settings_commit = True settings.app.route53_region = utils.filter_str( flask.request.json['route53_region']) or None if 'route53_zone' in flask.request.json: settings_commit = True settings.app.route53_zone = utils.filter_str( flask.request.json['route53_zone']) or None if settings.app.cloud_provider == 'oracle': if 'oracle_user_ocid' in flask.request.json: settings_commit = True settings.app.oracle_user_ocid = utils.filter_str( flask.request.json['oracle_user_ocid']) or None elif settings.app.oracle_user_ocid: settings_commit = True settings.app.oracle_user_ocid = None if 'oracle_public_key' in flask.request.json: if flask.request.json['oracle_public_key'] == 'reset': settings_commit = True private_key, public_key = utils.generate_rsa_key() settings.app.oracle_private_key = private_key settings.app.oracle_public_key = public_key for aws_key in ( 'us_east_1_access_key', 'us_east_1_secret_key', 'us_east_2_access_key', 'us_east_2_secret_key', 'us_west_1_access_key', 'us_west_1_secret_key', 'us_west_2_access_key', 'us_west_2_secret_key', 'us_gov_west_1_access_key', 'us_gov_west_1_secret_key', 'eu_west_1_access_key', 'eu_west_1_secret_key', 'eu_west_2_access_key', 'eu_west_2_secret_key', 'eu_central_1_access_key', 'eu_central_1_secret_key', 'ca_central_1_access_key', 'ca_central_1_secret_key', 'ap_northeast_1_access_key', 'ap_northeast_1_secret_key', 'ap_northeast_2_access_key', 'ap_northeast_2_secret_key', 'ap_southeast_1_access_key', 'ap_southeast_1_secret_key', 'ap_southeast_2_access_key', 'ap_southeast_2_secret_key', 'ap_south_1_access_key', 'ap_south_1_secret_key', 'sa_east_1_access_key', 'sa_east_1_secret_key', ): if settings.app.cloud_provider != 'aws': settings_commit = True setattr(settings.app, aws_key, None) elif aws_key in flask.request.json: settings_commit = True aws_value = flask.request.json[aws_key] if aws_value: setattr(settings.app, aws_key, utils.filter_str(aws_value)) else: setattr(settings.app, aws_key, None) if not settings.app.sso: settings.app.sso_match = None settings.app.sso_azure_directory_id = None settings.app.sso_azure_app_id = None settings.app.sso_azure_app_secret = None settings.app.sso_authzero_directory_id = None settings.app.sso_authzero_app_id = None settings.app.sso_authzero_app_secret = None settings.app.sso_google_key = None settings.app.sso_google_email = None settings.app.sso_duo_token = None settings.app.sso_duo_secret = None settings.app.sso_duo_host = None settings.app.sso_org = None settings.app.sso_saml_url = None settings.app.sso_saml_issuer_url = None settings.app.sso_saml_cert = None settings.app.sso_okta_app_id = None settings.app.sso_okta_token = None settings.app.sso_onelogin_key = None settings.app.sso_onelogin_app_id = None settings.app.sso_onelogin_id = None settings.app.sso_onelogin_secret = None settings.app.sso_radius_secret = None settings.app.sso_radius_host = None else: if RADIUS_AUTH in settings.app.sso and \ settings.app.sso_duo_mode == 'passcode': return utils.jsonify({ 'error': RADIUS_DUO_PASSCODE, 'error_msg': RADIUS_DUO_PASSCODE_MSG, }, 400) if settings.app.sso == DUO_AUTH and \ settings.app.sso_duo_mode == 'passcode': return utils.jsonify({ 'error': DUO_PASSCODE, 'error_msg': DUO_PASSCODE_MSG, }, 400) for change in changes: remote_addr = utils.get_remote_addr() flask.g.administrator.audit_event( 'admin_settings', _changes_audit_text[change], remote_addr=remote_addr, ) journal.entry( journal.SETTINGS_UPDATE, remote_address=remote_addr, event_long='Settings updated', changed=_changes_audit_text[change], ) if settings_commit: settings.commit() admin.commit(admin.changed) if admin_event: event.Event(type=ADMINS_UPDATED) if org_event: for org in organization.iter_orgs(fields=('_id')): event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SETTINGS_UPDATED) if update_acme: try: acme.update_acme_cert() app.update_server(0.5) except: logger.exception('Failed to get LetsEncrypt cert', 'handler', acme_domain=settings.app.acme_domain, ) settings.app.acme_domain = None settings.app.acme_key = None settings.app.acme_timestamp = None settings.commit() return utils.jsonify({ 'error': ACME_ERROR, 'error_msg': ACME_ERROR_MSG, }, 400) elif update_cert: logger.info('Regenerating server certificate...', 'handler') utils.create_server_cert() app.update_server(0.5) elif update_server: app.update_server(0.5) response = flask.g.administrator.dict() response.update(_dict()) return utils.jsonify(response)