def sso_duo_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get("token")) or None passcode = utils.filter_str(flask.request.json.get("passcode")) or None if not token or not passcode: return utils.jsonify({"error": TOKEN_INVALID, "error_msg": TOKEN_INVALID_MSG}, 401) tokens_collection = mongo.get_collection("sso_tokens") doc = tokens_collection.find_one({"_id": token}) if not doc or doc["_id"] != token: return utils.jsonify({"error": TOKEN_INVALID, "error_msg": TOKEN_INVALID_MSG}, 401) username = doc["username"] email = doc["email"] org_id = doc["org_id"] groups = doc["groups"] duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type="Key", passcode=passcode, ) valid = duo_auth.authenticate() if not valid: return utils.jsonify({"error": PASSCODE_INVALID, "error_msg": PASSCODE_INVALID_MSG}, 401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user( name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None ) usr.audit_event("user_created", "User created with single sign-on", remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit("groups") if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit("auth_type") key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event("user_profile", "User profile viewed from single sign-on", remote_addr=utils.get_remote_addr()) return utils.jsonify({"redirect": utils.get_url_root() + key_link["view_url"]}, 200)
def _auth_radius(username, password): valid, org_id = sso.verify_radius(username, password) if not valid: return utils.jsonify( { 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if not org_id: org_id = settings.app.sso_org org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, type=CERT_CLIENT, auth_type=RADIUS_AUTH) usr.audit_event( 'user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr(), ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return utils.jsonify( { 'error': AUTH_DISABLED, 'error_msg': AUTH_DISABLED_MSG, }, 403) if usr.auth_type != RADIUS_AUTH: usr.auth_type = RADIUS_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 single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify( { 'redirect': utils.get_url_root()[:-1] + key_link['view_url'], }, 202)
def sso_request_get(): sso_mode = settings.app.sso if sso_mode not in (GOOGLE_AUTH, GOOGLE_DUO_AUTH, SLACK_AUTH, SLACK_DUO_AUTH, SAML_AUTH, SAML_DUO_AUTH, SAML_OKTA_AUTH, SAML_OKTA_DUO_AUTH, SAML_ONELOGIN_AUTH, SAML_ONELOGIN_DUO_AUTH): return flask.abort(405) state = utils.rand_str(64) secret = utils.rand_str(64) callback = utils.get_url_root() + 'sso/callback' if not settings.local.sub_active: logger.error('Subscription must be active for sso', 'sso') return flask.abort(405) if GOOGLE_AUTH in sso_mode: resp = requests.post(AUTH_SERVER + '/v1/request/google', headers={ 'Content-Type': 'application/json', }, json={ 'license': settings.app.license, 'callback': callback, 'state': state, 'secret': secret, } ) if resp.status_code != 200: logger.error('Google auth server error', 'sso', status_code=resp.status_code, content=resp.content, ) if resp.status_code == 401: return flask.abort(405) return flask.abort(500) tokens_collection = mongo.get_collection('sso_tokens') tokens_collection.insert({ '_id': state, 'type': GOOGLE_AUTH, 'secret': secret, 'timestamp': utils.now(), }) data = resp.json() return utils.redirect(data['url']) elif SLACK_AUTH in sso_mode: resp = requests.post(AUTH_SERVER + '/v1/request/slack', headers={ 'Content-Type': 'application/json', }, json={ 'license': settings.app.license, 'callback': callback, 'state': state, 'secret': secret, } ) if resp.status_code != 200: logger.error('Slack auth server error', 'sso', status_code=resp.status_code, content=resp.content, ) if resp.status_code == 401: return flask.abort(405) return flask.abort(500) tokens_collection = mongo.get_collection('sso_tokens') tokens_collection.insert({ '_id': state, 'type': SLACK_AUTH, 'secret': secret, 'timestamp': utils.now(), }) data = resp.json() return utils.redirect(data['url']) elif SAML_AUTH in sso_mode: resp = requests.post(AUTH_SERVER + '/v1/request/saml', headers={ 'Content-Type': 'application/json', }, json={ 'license': settings.app.license, 'callback': callback, 'state': state, 'secret': secret, 'sso_url': settings.app.sso_saml_url, 'issuer_url': settings.app.sso_saml_issuer_url, 'cert': settings.app.sso_saml_cert, }, ) if resp.status_code != 200: logger.error('Saml auth server error', 'sso', status_code=resp.status_code, content=resp.content, ) if resp.status_code == 401: return flask.abort(405) return flask.abort(500) tokens_collection = mongo.get_collection('sso_tokens') tokens_collection.insert({ '_id': state, 'type': SAML_AUTH, 'secret': secret, 'timestamp': utils.now(), }) return flask.Response( status=200, response=resp.content, content_type="text/html;charset=utf-8", )
def _auth_plugin(username, password): if settings.local.sub_plan != 'enterprise': return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) valid, org_id, groups = sso.plugin_login_authenticate( user_name=username, password=password, remote_ip=utils.get_remote_addr(), ) if not valid: 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: 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(), ) 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(), ) return utils.jsonify({ 'redirect': utils.get_url_root() + key_link['view_url'], }, 202)
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 port_forwarding_event = False if 'name' in flask.request.json: name = utils.filter_str(flask.request.json['name']) or None if name != user.name: user.audit_event( 'user_updated', 'User name changed', remote_addr=utils.get_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=utils.get_remote_addr(), ) user.email = email 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=utils.get_remote_addr(), ) user.groups = list(groups) if 'pin' in flask.request.json: pin = flask.request.json['pin'] 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 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): user.audit_event( 'user_updated', 'User pin changed', remote_addr=utils.get_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']: 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=utils.get_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=utils.get_remote_addr(), ) user.port_forwarding = port_forwarding disabled = flask.request.json.get('disabled') if disabled is not None: if disabled != user.disabled: user.audit_event( 'user_updated', 'User %s' % ('disabled' if disabled else 'enabled'), remote_addr=utils.get_remote_addr(), ) user.disabled = disabled bypass_secondary = flask.request.json.get('bypass_secondary') if bypass_secondary is not None: user.bypass_secondary = True if bypass_secondary else False client_to_client = flask.request.json.get('client_to_client') if client_to_client is not None: user.client_to_client = True if client_to_client else False 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=utils.get_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=utils.get_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 or disabled: user.disconnect() if disabled: if user.type == CERT_CLIENT: logger.LogEntry(message='Disabled user "%s".' % user.name) elif disabled == False and user.type == CERT_CLIENT: logger.LogEntry(message='Enabled user "%s".' % user.name) 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=utils.get_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 user_linked_key_page_get(short_code): doc = _find_doc({ 'short_id': short_code, }, one_time=True, one_time_new=True) if not doc: return flask.abort(404) org = organization.get_by_id(doc['org_id']) user = org.get_user(id=doc['user_id']) if user.disabled: return flask.abort(403) user.audit_event('user_profile', 'User temporary profile link viewed', remote_addr=utils.get_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 user.auth_type == RADIUS_AUTH 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 key_page = key_page.replace('<%= header_class %>', header_class) uri_url = (utils.get_url_root()[:-1] + '/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, user.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: key_page = key_page.replace('<%= user_otp_key %>', user.otp_secret) key_page = key_page.replace('<%= user_otp_url %>', 'otpauth://totp/%s@%s?secret=%s' % ( user.name, org.name, user.otp_secret)) else: key_page = key_page.replace('<%= user_otp_key %>', '') key_page = key_page.replace('<%= user_otp_url %>', '') if user.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" ' + \ 'title="Download Chromebook Profiles" ' + \ 'href="/key_onc/%s.zip">Download Chromebook Profiles</a><br>\n' % ( doc['key_id']) for server in org.iter_servers(): conf_links += '<a class="btn btn-sm" title="Download Profile" ' + \ 'href="/key/%s/%s.key">Download Profile (%s)</a><br>\n' % ( doc['key_id'], server.id, server.name) key_page = key_page.replace('<%= conf_links %>', conf_links) return key_page
def sso_yubico_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None key = utils.filter_str(flask.request.json.get('key')) or None if sso_mode not in (GOOGLE_YUBICO_AUTH, SLACK_YUBICO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_YUBICO_AUTH): return flask.abort(404) if not token or not key: return utils.jsonify( { 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_one({ '_id': token, }) if not doc or doc['_id'] != token or doc['type'] != YUBICO_AUTH: return utils.jsonify( { 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = doc['groups'] valid, yubico_id = sso.auth_yubico(key) if not valid: return utils.jsonify( { 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, yubico_id=yubico_id, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if yubico_id != usr.yubico_id: return utils.jsonify( { 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def sso_callback_get(): sso_mode = settings.app.sso if sso_mode not in (GOOGLE_AUTH, GOOGLE_DUO_AUTH, GOOGLE_YUBICO_AUTH, SLACK_AUTH, SLACK_DUO_AUTH, SLACK_YUBICO_AUTH, SAML_AUTH, SAML_DUO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_AUTH, SAML_OKTA_DUO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_AUTH, SAML_ONELOGIN_DUO_AUTH, SAML_ONELOGIN_YUBICO_AUTH): return flask.abort(405) state = flask.request.args.get('state') sig = flask.request.args.get('sig') tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_and_modify(query={ '_id': state, }, remove=True) if not doc: return flask.abort(404) query = flask.request.query_string.split('&sig=')[0] test_sig = base64.urlsafe_b64encode( hmac.new(str(doc['secret']), query, hashlib.sha512).digest()) if sig != test_sig: return flask.abort(401) params = urlparse.parse_qs(query) if doc.get('type') == SAML_AUTH: username = params.get('username')[0] email = params.get('email', [None])[0] org_name = params.get('org', [None])[0] if not username: return flask.abort(406) valid, org_name = sso.verify_saml(username, email, org_name) if not valid: return flask.abort(401) org_id = settings.app.sso_org if org_name: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='saml', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), sso_org_names=[org_name], ) if valid: org_id = org_id_new or org_id else: logger.error( 'Saml plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) elif doc.get('type') == SLACK_AUTH: username = params.get('username')[0] email = None user_team = params.get('team')[0] org_names = params.get('orgs', [''])[0] org_names = org_names.split(',') valid, org_name = sso.verify_slack(username, user_team, org_names) if not valid: return flask.abort(401) if org_name: org_names = [org_name] org_id = settings.app.sso_org for org_name in org_names: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id break valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='slack', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), sso_org_names=org_names, ) if valid: org_id = org_id_new or org_id else: logger.error( 'Slack plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) else: username = params.get('username')[0] email = username valid, org_name = sso.verify_google(username) if not valid: return flask.abort(401) org_id = settings.app.sso_org if org_name: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='google', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error( 'Google plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) if DUO_AUTH in sso_mode: if settings.app.sso_duo_mode == 'passcode': token = utils.generate_secret() tokens_collection = mongo.get_collection('sso_tokens') tokens_collection.insert({ '_id': token, 'type': DUO_AUTH, 'username': username, 'email': email, 'org_id': org_id, 'groups': groups, 'timestamp': utils.now(), }) duo_page = static.StaticFile(settings.conf.www_path, 'duo.html', cache=False, gzip=False) if settings.app.theme == 'dark': duo_page.data = duo_page.data.replace('<body>', '<body class="dark">') duo_page.data = duo_page.data.replace('<%= token %>', token) return duo_page.get_response() else: duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', ) valid = duo_auth.authenticate() if valid: valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='duo', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error( 'Duo plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) groups = ((groups or set()) | (groups2 or set())) or None else: logger.error( 'Duo authentication not valid', 'sso', username=username, ) return flask.abort(401) if YUBICO_AUTH in sso_mode: token = utils.generate_secret() tokens_collection = mongo.get_collection('sso_tokens') tokens_collection.insert({ '_id': token, 'type': YUBICO_AUTH, 'username': username, 'email': email, 'org_id': org_id, 'groups': groups, 'timestamp': utils.now(), }) yubico_page = static.StaticFile(settings.conf.www_path, 'yubico.html', cache=False, gzip=False) if settings.app.theme == 'dark': yubico_page.data = yubico_page.data.replace( '<body>', '<body class="dark">') yubico_page.data = yubico_page.data.replace('<%= token %>', token) return yubico_page.get_response() org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.redirect(utils.get_url_root() + key_link['view_url'])
def _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 _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_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 'mac_addresses' in flask.request.json: mac_addresses = flask.request.json['mac_addresses'] or None if user.mac_addresses != mac_addresses: user.audit_event( 'user_updated', 'User mac addresses changed', remote_addr=remote_addr, ) journal.entry( journal.USER_UPDATE, user.journal_data, event_long='User mac addresses changed', remote_address=remote_addr, ) reset_user = True user.mac_addresses = mac_addresses 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 sso_duo_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None passcode = utils.filter_str(flask.request.json.get('passcode')) or '' if sso_mode not in (DUO_AUTH, GOOGLE_DUO_AUTH, SLACK_DUO_AUTH, SAML_DUO_AUTH, SAML_OKTA_DUO_AUTH, SAML_ONELOGIN_DUO_AUTH, RADIUS_DUO_AUTH): return flask.abort(404) if not token: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_one({ '_id': token, }) if not doc or doc['_id'] != token or doc['type'] != DUO_AUTH: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = doc['groups'] if settings.app.sso_duo_mode == 'passcode': duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', passcode=passcode, ) valid = duo_auth.authenticate() if not valid: logger.error('Duo authentication not valid', 'sso', username=username, ) return utils.jsonify({ 'error': PASSCODE_INVALID, 'error_msg': PASSCODE_INVALID_MSG, }, 401) else: duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', ) valid = duo_auth.authenticate() if not valid: logger.error('Duo authentication not valid', 'sso', username=username, ) return utils.jsonify({ 'error': DUO_FAILED, 'error_msg': DUO_FAILED_MSG, }, 401) valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='duo', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error('Duo plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) groups = ((groups or set()) | (groups2 or set())) or None usr = user.find_user_auth(name=username, auth_type=sso_mode) if not usr: org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) else: org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify({ 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def sso_duo_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None passcode = utils.filter_str(flask.request.json.get('passcode')) or '' if sso_mode not in (DUO_AUTH, GOOGLE_DUO_AUTH, SLACK_DUO_AUTH, SAML_DUO_AUTH, SAML_OKTA_DUO_AUTH, SAML_ONELOGIN_DUO_AUTH, RADIUS_DUO_AUTH): return flask.abort(404) if not token: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_one({ '_id': token, }) if not doc or doc['_id'] != token or doc['type'] != DUO_AUTH: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = doc['groups'] if settings.app.sso_duo_mode == 'passcode': duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', passcode=passcode, ) valid = duo_auth.authenticate() if not valid: logger.error('Duo authentication not valid', 'sso', username=username, ) return utils.jsonify({ 'error': PASSCODE_INVALID, 'error_msg': PASSCODE_INVALID_MSG, }, 401) else: duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', ) valid = duo_auth.authenticate() if not valid: logger.error('Duo authentication not valid', 'sso', username=username, ) return utils.jsonify({ 'error': DUO_FAILED, 'error_msg': DUO_FAILED_MSG, }, 401) valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type='duo', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error('Duo plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) groups = ((groups or set()) | (groups2 or set())) or None org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify({ 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def sso_callback_get(): sso_mode = settings.app.sso if sso_mode not in (GOOGLE_AUTH, GOOGLE_DUO_AUTH, GOOGLE_YUBICO_AUTH, SLACK_AUTH, SLACK_DUO_AUTH, SLACK_YUBICO_AUTH, SAML_AUTH, SAML_DUO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_AUTH, SAML_OKTA_DUO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_AUTH, SAML_ONELOGIN_DUO_AUTH, SAML_ONELOGIN_YUBICO_AUTH): return flask.abort(405) state = flask.request.args.get('state') sig = flask.request.args.get('sig') tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_and_modify(query={ '_id': state, }, remove=True) if not doc: return flask.abort(404) query = flask.request.query_string.split('&sig=')[0] test_sig = base64.urlsafe_b64encode(hmac.new(str(doc['secret']), query, hashlib.sha512).digest()) if not utils.const_compare(sig, test_sig): return flask.abort(401) params = urlparse.parse_qs(query) if doc.get('type') == SAML_AUTH: username = params.get('username')[0] email = params.get('email', [None])[0] org_name = params.get('org', [None])[0] if not username: return flask.abort(406) org_id = settings.app.sso_org if org_name: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='saml', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), sso_org_names=[org_name], ) if valid: org_id = org_id_new or org_id else: logger.error('Saml plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) elif doc.get('type') == SLACK_AUTH: username = params.get('username')[0] email = None user_team = params.get('team')[0] org_names = params.get('orgs', [''])[0] org_names = org_names.split(',') valid = sso.verify_slack(username, user_team) if not valid: return flask.abort(401) org_id = settings.app.sso_org for org_name in org_names: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id break valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='slack', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), sso_org_names=org_names, ) if valid: org_id = org_id_new or org_id else: logger.error('Slack plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) else: username = params.get('username')[0] email = username valid = sso.verify_google(username) if not valid: return flask.abort(401) org_id = settings.app.sso_org valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type='google', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if valid: org_id = org_id_new or org_id else: logger.error('Google plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) if DUO_AUTH in sso_mode: token = utils.generate_secret() tokens_collection = mongo.get_collection('sso_tokens') tokens_collection.insert({ '_id': token, 'type': DUO_AUTH, 'username': username, 'email': email, 'org_id': org_id, 'groups': groups, 'timestamp': utils.now(), }) duo_page = static.StaticFile(settings.conf.www_path, 'duo.html', cache=False, gzip=False) sso_duo_mode = settings.app.sso_duo_mode if sso_duo_mode == 'passcode': duo_mode = 'passcode' elif sso_duo_mode == 'phone': duo_mode = 'phone' else: duo_mode = 'push' body_class = duo_mode if settings.app.theme == 'dark': body_class += ' dark' duo_page.data = duo_page.data.replace('<%= body_class %>', body_class) duo_page.data = duo_page.data.replace('<%= token %>', token) duo_page.data = duo_page.data.replace('<%= duo_mode %>', duo_mode) return duo_page.get_response() if YUBICO_AUTH in sso_mode: token = utils.generate_secret() tokens_collection = mongo.get_collection('sso_tokens') tokens_collection.insert({ '_id': token, 'type': YUBICO_AUTH, 'username': username, 'email': email, 'org_id': org_id, 'groups': groups, 'timestamp': utils.now(), }) yubico_page = static.StaticFile(settings.conf.www_path, 'yubico.html', cache=False, gzip=False) if settings.app.theme == 'dark': yubico_page.data = yubico_page.data.replace( '<body>', '<body class="dark">') yubico_page.data = yubico_page.data.replace('<%= token %>', token) return yubico_page.get_response() org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.redirect(utils.get_url_root() + key_link['view_url'])
def sso_yubico_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None key = utils.filter_str(flask.request.json.get('key')) or None if sso_mode not in (GOOGLE_YUBICO_AUTH, SLACK_YUBICO_AUTH, SAML_YUBICO_AUTH, SAML_OKTA_YUBICO_AUTH, SAML_ONELOGIN_YUBICO_AUTH): return flask.abort(404) if not token or not key: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_one({ '_id': token, }) if not doc or doc['_id'] != token or doc['type'] != YUBICO_AUTH: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = doc['groups'] valid, yubico_id = sso.auth_yubico(key) if not valid: return utils.jsonify({ 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, yubico_id=yubico_id, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if yubico_id != usr.yubico_id: return utils.jsonify({ 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify({ 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def _auth_radius(username, password): valid, org_id = sso.verify_radius(username, password) if not valid: return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) if not org_id: org_id = settings.app.sso_org valid, org_id_new = 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: logger.error('Radius plugin authentication not valid', 'sso', username=username, ) return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, type=CERT_CLIENT, auth_type=RADIUS_AUTH) usr.audit_event( 'user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr(), ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return utils.jsonify({ 'error': AUTH_DISABLED, 'error_msg': AUTH_DISABLED_MSG, }, 403) if usr.auth_type != RADIUS_AUTH: usr.auth_type = RADIUS_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 single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify({ 'redirect': utils.get_url_root() + key_link['view_url'], }, 202)
def _validate_user(username, email, sso_mode, org_id, groups, remote_addr, http_redirect=False, yubico_id=None): usr = user.find_user_auth(name=username, auth_type=sso_mode) if not usr: org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) else: if usr.org_id != org_id: logger.info( 'User organization changed, moving user', 'sso', user_name=username, user_email=email, remote_ip=remote_addr, cur_org_id=usr.org_id, new_org_id=org_id, ) org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr.remove() old_org_id = usr.org_id new_usr = org.new_user( name=usr.name, email=usr.email, pin=usr.pin, type=usr.type, groups=usr.groups, auth_type=usr.auth_type, yubico_id=usr.yubico_id, disabled=usr.disabled, resource_id=usr.resource_id, bypass_secondary=usr.bypass_secondary, client_to_client=usr.client_to_client, mac_addresses=usr.mac_addresses, dns_servers=usr.dns_servers, dns_suffix=usr.dns_suffix, port_forwarding=usr.port_forwarding, ) new_usr.otp_secret = usr.otp_secret usr = new_usr usr.commit() event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=old_org_id) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, yubico_id=yubico_id, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=remote_addr) journal.entry( journal.USER_CREATE, usr.journal_data, event_long='User created with single sign-on', remote_address=remote_addr, ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if yubico_id and usr.yubico_id and yubico_id != usr.yubico_id: journal.entry( journal.SSO_AUTH_FAILURE, user_name=username, remote_address=remote_addr, reason=journal.SSO_AUTH_REASON_INVALID_YUBIKEY, reason_long='Invalid username', ) return utils.jsonify( { 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) if usr.disabled: return flask.abort(403) changed = False if yubico_id and not usr.yubico_id: changed = True usr.yubico_id = yubico_id usr.commit('yubico_id') if groups and groups - set(usr.groups or []): changed = True usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: changed = True usr.auth_type = sso_mode usr.commit('auth_type') usr.clear_auth_cache() usr.disconnect() if changed: event.Event(type=USERS_UPDATED, resource_id=org.id) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=remote_addr, ) journal.entry( journal.SSO_AUTH_SUCCESS, usr.journal_data, key_id_hash=hashlib.md5(key_link['id'].encode()).hexdigest(), remote_address=remote_addr, ) journal.entry( journal.USER_PROFILE_SUCCESS, usr.journal_data, remote_address=remote_addr, event_long='User profile viewed from single sign-on', ) if http_redirect: return utils.redirect(utils.get_url_root() + key_link['view_url']) else: return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def _auth_radius(username, password): sso_mode = settings.app.sso valid, org_names, groups = sso.verify_radius(username, password) if not valid: return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) org_id = settings.app.sso_org if org_names: for org_name in org_names: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id break valid, org_id_new, 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: 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, ) 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: 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, ) 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: 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=utils.get_remote_addr(), ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return 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) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify({ 'redirect': utils.get_url_root() + key_link['view_url'], }, 202)
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 port_forwarding_event = False if 'name' in flask.request.json: name = utils.filter_str(flask.request.json['name']) or None if name != user.name: user.audit_event('user_updated', 'User name changed', remote_addr=utils.get_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=utils.get_remote_addr(), ) user.email = email if 'pin' in flask.request.json: pin = flask.request.json['pin'] if pin != 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 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): user.audit_event('user_updated', 'User pin changed', remote_addr=utils.get_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']: 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=utils.get_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=utils.get_remote_addr(), ) user.port_forwarding = port_forwarding disabled = flask.request.json.get('disabled') if disabled is not None: if disabled != user.disabled: user.audit_event('user_updated', 'User %s' % ('disabled' if disabled else 'enabled'), remote_addr=utils.get_remote_addr(), ) user.disabled = disabled bypass_secondary = flask.request.json.get('bypass_secondary') if bypass_secondary is not None: user.bypass_secondary = True if bypass_secondary else False client_to_client = flask.request.json.get('client_to_client') if client_to_client is not None: user.client_to_client = True if client_to_client else False 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=utils.get_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=utils.get_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 or disabled: user.disconnect() if disabled: if user.type == CERT_CLIENT: logger.LogEntry(message='Disabled user "%s".' % user.name) elif disabled == False and user.type == CERT_CLIENT: logger.LogEntry(message='Enabled user "%s".' % user.name) 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=utils.get_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_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): doc = _find_doc({"short_id": short_code}, one_time=True, one_time_new=True) if not doc: return flask.abort(404) org = organization.get_by_id(doc["org_id"]) user = org.get_user(id=doc["user_id"]) if user.disabled: return flask.abort(403) user.audit_event("user_profile", "User temporary profile link viewed", remote_addr=utils.get_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 user.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 key_page = key_page.replace("<%= header_class %>", header_class) 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, user.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 user.has_duo_passcode: key_page = key_page.replace("<%= user_otp_key %>", user.otp_secret) key_page = key_page.replace( "<%= user_otp_url %>", "otpauth://totp/%s@%s?secret=%s" % (user.name, org.name, user.otp_secret) ) else: key_page = key_page.replace("<%= user_otp_key %>", "") key_page = key_page.replace("<%= user_otp_url %>", "") if user.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" ' + 'title="Download Chromebook Profiles" ' + 'href="/key_onc/%s.zip">Download Chromebook Profiles</a><br>\n' % (doc["key_id"]) ) for server in user.iter_servers(): conf_links += ( '<a class="btn btn-sm" title="Download Profile" ' + 'href="/key/%s/%s.key">Download Profile (%s)</a><br>\n' % (doc["key_id"], server.id, server.name) ) key_page = key_page.replace("<%= conf_links %>", conf_links) return key_page
def sso_authenticate_post(): if settings.app.sso != DUO_AUTH or \ settings.app.sso_duo_mode == 'passcode': return flask.abort(405) username = utils.json_filter_str('username') usernames = [username] email = None if '@' in username: email = username usernames.append(username.split('@')[0]) valid = False for i, username in enumerate(usernames): try: duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', ) valid = duo_auth.authenticate() break except InvalidUser: if i == len(usernames) - 1: logger.error( 'Invalid duo username', 'sso', username=username, ) if valid: valid, org_id, groups = sso.plugin_sso_authenticate( sso_type='duo', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), ) if not valid: logger.error( 'Duo plugin authentication not valid', 'sso', username=username, ) return flask.abort(401) else: logger.error( 'Duo authentication not valid', 'sso', username=username, ) return flask.abort(401) if not org_id: org_id = settings.app.sso_org org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for Duo sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=DUO_AUTH, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != DUO_AUTH: usr.auth_type = DUO_AUTH usr.commit('auth_type') event.Event(type=USERS_UPDATED, resource_id=org.id) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.get_url_root() + key_link['view_url']
def sso_authenticate_post(): if settings.app.sso != DUO_AUTH or settings.app.sso_duo_mode == "passcode": return flask.abort(405) username = utils.json_filter_str("username") usernames = [username] email = None if "@" in username: email = username usernames.append(username.split("@")[0]) valid = False for i, username in enumerate(usernames): try: duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type="Key" ) valid = duo_auth.authenticate() break except InvalidUser: if i == len(usernames) - 1: logger.error("Invalid duo username", "sso", username=username) if valid: valid, org_id, groups = sso.plugin_sso_authenticate( sso_type="duo", user_name=username, user_email=email, remote_ip=utils.get_remote_addr() ) if not valid: logger.error("Duo plugin authentication not valid", "sso", username=username) return flask.abort(401) else: logger.error("Duo authentication not valid", "sso", username=username) return flask.abort(401) if not org_id: org_id = settings.app.sso_org org = organization.get_by_id(org_id) if not org: logger.error("Organization for Duo sso does not exist", "sso", org_id=org_id) return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user( name=username, email=email, type=CERT_CLIENT, auth_type=DUO_AUTH, groups=list(groups) if groups else None ) usr.audit_event("user_created", "User created with single sign-on", remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit("groups") if usr.auth_type != DUO_AUTH: usr.auth_type = DUO_AUTH usr.commit("auth_type") event.Event(type=USERS_UPDATED, resource_id=org.id) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event("user_profile", "User profile viewed from single sign-on", remote_addr=utils.get_remote_addr()) return utils.get_url_root() + key_link["view_url"]
def _validate_user(username, email, sso_mode, org_id, groups, http_redirect=False, yubico_id=None): usr = user.find_user_auth(name=username, auth_type=sso_mode) if not usr: org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) else: if usr.org_id != org_id: logger.info( 'User organization changed, moving user', 'sso', user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), cur_org_id=usr.org_id, new_org_id=org_id, ) org = organization.get_by_id(org_id) if not org: logger.error( 'Organization for sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr.remove() old_org_id = usr.org_id usr = org.new_user( name=usr.name, email=usr.email, type=usr.type, groups=usr.groups, auth_type=usr.auth_type, yubico_id=usr.yubico_id, disabled=usr.disabled, bypass_secondary=usr.bypass_secondary, client_to_client=usr.client_to_client, dns_servers=usr.dns_servers, dns_suffix=usr.dns_suffix, port_forwarding=usr.port_forwarding, ) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=old_org_id) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) org = usr.org if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, yubico_id=yubico_id, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if yubico_id and usr.yubico_id and yubico_id != usr.yubico_id: return utils.jsonify( { 'error': YUBIKEY_INVALID, 'error_msg': YUBIKEY_INVALID_MSG, }, 401) if usr.disabled: return flask.abort(403) changed = False if yubico_id and not usr.yubico_id: changed = True usr.yubico_id = yubico_id usr.commit('yubico_id') if groups and groups - set(usr.groups or []): changed = True usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: changed = True usr.auth_type = sso_mode usr.commit('auth_type') if changed: event.Event(type=USERS_UPDATED, resource_id=org.id) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event( 'user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) if http_redirect: return utils.redirect(utils.get_url_root() + key_link['view_url']) else: return utils.jsonify( { 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def sso_request_get(): sso_mode = settings.app.sso if sso_mode not in ( GOOGLE_AUTH, GOOGLE_DUO_AUTH, SLACK_AUTH, SLACK_DUO_AUTH, SAML_AUTH, SAML_DUO_AUTH, SAML_OKTA_AUTH, SAML_OKTA_DUO_AUTH, SAML_ONELOGIN_AUTH, SAML_ONELOGIN_DUO_AUTH, ): return flask.abort(404) state = utils.rand_str(64) secret = utils.rand_str(64) callback = utils.get_url_root() + "/sso/callback" if not settings.local.sub_active: logger.error("Subscription must be active for sso", "sso") return flask.abort(405) if GOOGLE_AUTH in sso_mode: resp = requests.post( AUTH_SERVER + "/v1/request/google", headers={"Content-Type": "application/json"}, json={"license": settings.app.license, "callback": callback, "state": state, "secret": secret}, ) if resp.status_code != 200: logger.error("Google auth server error", "sso", status_code=resp.status_code, content=resp.content) if resp.status_code == 401: return flask.abort(405) return flask.abort(500) tokens_collection = mongo.get_collection("sso_tokens") tokens_collection.insert({"_id": state, "type": GOOGLE_AUTH, "secret": secret, "timestamp": utils.now()}) data = resp.json() return utils.redirect(data["url"]) elif SLACK_AUTH in sso_mode: resp = requests.post( AUTH_SERVER + "/v1/request/slack", headers={"Content-Type": "application/json"}, json={"license": settings.app.license, "callback": callback, "state": state, "secret": secret}, ) if resp.status_code != 200: logger.error("Slack auth server error", "sso", status_code=resp.status_code, content=resp.content) if resp.status_code == 401: return flask.abort(405) return flask.abort(500) tokens_collection = mongo.get_collection("sso_tokens") tokens_collection.insert({"_id": state, "type": SLACK_AUTH, "secret": secret, "timestamp": utils.now()}) data = resp.json() return utils.redirect(data["url"]) elif SAML_AUTH in sso_mode: resp = requests.post( AUTH_SERVER + "/v1/request/saml", headers={"Content-Type": "application/json"}, json={ "license": settings.app.license, "callback": callback, "state": state, "secret": secret, "sso_url": settings.app.sso_saml_url, "issuer_url": settings.app.sso_saml_issuer_url, "cert": settings.app.sso_saml_cert, }, ) if resp.status_code != 200: logger.error("Saml auth server error", "sso", status_code=resp.status_code, content=resp.content) if resp.status_code == 401: return flask.abort(405) return flask.abort(500) tokens_collection = mongo.get_collection("sso_tokens") tokens_collection.insert({"_id": state, "type": SAML_AUTH, "secret": secret, "timestamp": utils.now()}) return flask.Response(status=200, response=resp.content, content_type="text/html;charset=utf-8") else: return flask.abort(404)
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 sso_callback_get(): sso_mode = settings.app.sso if sso_mode not in ( GOOGLE_AUTH, GOOGLE_DUO_AUTH, SLACK_AUTH, SLACK_DUO_AUTH, SAML_AUTH, SAML_DUO_AUTH, SAML_OKTA_AUTH, SAML_OKTA_DUO_AUTH, SAML_ONELOGIN_AUTH, SAML_ONELOGIN_DUO_AUTH, ): return flask.abort(405) state = flask.request.args.get("state") sig = flask.request.args.get("sig") tokens_collection = mongo.get_collection("sso_tokens") doc = tokens_collection.find_and_modify(query={"_id": state}, remove=True) if not doc: return flask.abort(404) query = flask.request.query_string.split("&sig=")[0] test_sig = base64.urlsafe_b64encode(hmac.new(str(doc["secret"]), query, hashlib.sha512).digest()) if sig != test_sig: return flask.abort(401) params = urlparse.parse_qs(query) if doc.get("type") == SAML_AUTH: username = params.get("username")[0] email = params.get("email", [None])[0] org_name = params.get("org", [None])[0] if not username: return flask.abort(406) valid, org_name = sso.verify_saml(username, email, org_name) if not valid: return flask.abort(401) org_id = settings.app.sso_org if org_name: org = organization.get_by_name(org_name, fields=("_id")) if org: org_id = org.id valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type="saml", user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), sso_org_names=[org_name], ) if valid: org_id = org_id_new or org_id else: logger.error("Saml plugin authentication not valid", "sso", username=username) return flask.abort(401) elif doc.get("type") == SLACK_AUTH: username = params.get("username")[0] email = None user_team = params.get("team")[0] org_names = params.get("orgs", [""])[0] org_names = org_names.split(",") valid, org_name = sso.verify_slack(username, user_team, org_names) if not valid: return flask.abort(401) if org_name: org_names = [org_name] org_id = settings.app.sso_org for org_name in org_names: org = organization.get_by_name(org_name, fields=("_id")) if org: org_id = org.id break valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type="slack", user_name=username, user_email=email, remote_ip=utils.get_remote_addr(), sso_org_names=org_names, ) if valid: org_id = org_id_new or org_id else: logger.error("Slack plugin authentication not valid", "sso", username=username) return flask.abort(401) else: username = params.get("username")[0] email = username valid, org_name = sso.verify_google(username) if not valid: return flask.abort(401) org_id = settings.app.sso_org if org_name: org = organization.get_by_name(org_name, fields=("_id")) if org: org_id = org.id valid, org_id_new, groups = sso.plugin_sso_authenticate( sso_type="google", user_name=username, user_email=email, remote_ip=utils.get_remote_addr() ) if valid: org_id = org_id_new or org_id else: logger.error("Google plugin authentication not valid", "sso", username=username) return flask.abort(401) if DUO_AUTH in sso_mode: if settings.app.sso_duo_mode == "passcode": token = utils.generate_secret() tokens_collection = mongo.get_collection("sso_tokens") tokens_collection.insert( { "_id": token, "type": DUO_AUTH, "username": username, "email": email, "org_id": org_id, "groups": groups, "timestamp": utils.now(), } ) duo_page = static.StaticFile(settings.conf.www_path, "duo.html", cache=False, gzip=False) if settings.app.theme == "dark": duo_page.data = duo_page.data.replace("<body>", '<body class="dark">') duo_page.data = duo_page.data.replace("<%= token %>", token) return duo_page.get_response() else: duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type="Key" ) valid = duo_auth.authenticate() if valid: valid, org_id_new, groups2 = sso.plugin_sso_authenticate( sso_type="duo", user_name=username, user_email=email, remote_ip=utils.get_remote_addr() ) if valid: org_id = org_id_new or org_id else: logger.error("Duo plugin authentication not valid", "sso", username=username) return flask.abort(401) groups = ((groups or set()) | (groups2 or set())) or None else: logger.error("Duo authentication not valid", "sso", username=username) return flask.abort(401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user( name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None ) usr.audit_event("user_created", "User created with single sign-on", remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit("groups") if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit("auth_type") key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event("user_profile", "User profile viewed from single sign-on", remote_addr=utils.get_remote_addr()) return utils.redirect(utils.get_url_root() + key_link["view_url"])
def sso_authenticate_post(): if settings.app.sso != DUO_AUTH: return flask.abort(405) username = flask.request.json['username'] usernames = [username] email = None if '@' in username: email = username usernames.append(username.split('@')[0]) valid = False for i, username in enumerate(usernames): try: valid, org_id = sso.auth_duo( username, strong=True, ipaddr=flask.request.remote_addr, type='Key', ) break except InvalidUser: if i == len(usernames) - 1: logger.error('Invalid duo username', 'sso', username=username, ) if not valid: logger.error('Invalid duo username', 'sso', username=username, ) return flask.abort(401) if not org_id: org_id = settings.app.sso_org org = organization.get_by_id(org_id) if not org: logger.error('Organization for Duo sso does not exist', 'sso', org_id=org_id, ) return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=DUO_AUTH) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if usr.auth_type != DUO_AUTH: usr.auth_type = DUO_AUTH usr.commit('auth_type') event.Event(type=USERS_UPDATED, resource_id=org.id) key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.get_url_root()[:-1] + key_link['view_url']
def sso_duo_post(): sso_mode = settings.app.sso token = utils.filter_str(flask.request.json.get('token')) or None passcode = utils.filter_str(flask.request.json.get('passcode')) or None if not token or not passcode: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_one({ '_id': token, }) if not doc or doc['_id'] != token: return utils.jsonify({ 'error': TOKEN_INVALID, 'error_msg': TOKEN_INVALID_MSG, }, 401) username = doc['username'] email = doc['email'] org_id = doc['org_id'] groups = doc['groups'] duo_auth = sso.Duo( username=username, factor=settings.app.sso_duo_mode, remote_ip=utils.get_remote_addr(), auth_type='Key', passcode=passcode, ) valid = duo_auth.authenticate() if not valid: return utils.jsonify({ 'error': PASSCODE_INVALID, 'error_msg': PASSCODE_INVALID_MSG, }, 401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode, groups=list(groups) if groups else None) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if groups and groups - set(usr.groups or []): usr.groups = list(set(usr.groups or []) | groups) usr.commit('groups') if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.jsonify({ 'redirect': utils.get_url_root() + key_link['view_url'], }, 200)
def sso_callback_get(): sso_mode = settings.app.sso if sso_mode not in (GOOGLE_AUTH, GOOGLE_DUO_AUTH, SLACK_AUTH, SLACK_DUO_AUTH, SAML_AUTH, SAML_DUO_AUTH, SAML_OKTA_AUTH, SAML_OKTA_DUO_AUTH, SAML_ONELOGIN_AUTH, SAML_ONELOGIN_DUO_AUTH): return flask.abort(405) state = flask.request.args.get('state') sig = flask.request.args.get('sig') tokens_collection = mongo.get_collection('sso_tokens') doc = tokens_collection.find_and_modify(query={ '_id': state, }, remove=True) if not doc: return flask.abort(404) query = flask.request.query_string.split('&sig=')[0] test_sig = base64.urlsafe_b64encode(hmac.new(str(doc['secret']), query, hashlib.sha512).digest()) if sig != test_sig: return flask.abort(401) params = urlparse.parse_qs(query) if doc.get('type') == SAML_AUTH: username = params.get('username')[0] email = params.get('email', [None])[0] org_name = params.get('org', [None])[0] if not username: return flask.abort(406) valid, org_name = sso.verify_saml(username, email, org_name) if not valid: return flask.abort(401) org_id = settings.app.sso_org if org_name: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id elif doc.get('type') == SLACK_AUTH: username = params.get('username')[0] email = None user_team = params.get('team')[0] org_names = params.get('orgs', [''])[0] org_names = org_names.split(',') valid, org_name = sso.verify_slack(username, user_team, org_names) if not valid: return flask.abort(401) if org_name: org_names = [org_name] org_id = settings.app.sso_org for org_name in org_names: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id break else: username = params.get('username')[0] email = username valid, org_name = sso.verify_google(username) if not valid: return flask.abort(401) org_id = settings.app.sso_org if org_name: org = organization.get_by_name(org_name, fields=('_id')) if org: org_id = org.id if DUO_AUTH in sso_mode: valid, _ = sso.auth_duo( username, ipaddr=flask.request.remote_addr, type='Key', ) if not valid: return flask.abort(401) org = organization.get_by_id(org_id) if not org: return flask.abort(405) usr = org.find_user(name=username) if not usr: usr = org.new_user(name=username, email=email, type=CERT_CLIENT, auth_type=sso_mode) usr.audit_event('user_created', 'User created with single sign-on', remote_addr=utils.get_remote_addr()) event.Event(type=ORGS_UPDATED) event.Event(type=USERS_UPDATED, resource_id=org.id) event.Event(type=SERVERS_UPDATED) else: if usr.disabled: return flask.abort(403) if usr.auth_type != sso_mode: usr.auth_type = sso_mode usr.commit('auth_type') key_link = org.create_user_key_link(usr.id, one_time=True) usr.audit_event('user_profile', 'User profile viewed from single sign-on', remote_addr=utils.get_remote_addr(), ) return utils.redirect(utils.get_url_root()[:-1] + key_link['view_url'])
def _auth_plugin(username, password): if not settings.local.sub_plan or \ 'enterprise' not in settings.local.sub_plan: return utils.jsonify({ 'error': AUTH_INVALID, 'error_msg': AUTH_INVALID_MSG, }, 401) valid, org_id, groups = sso.plugin_login_authenticate( user_name=username, password=password, remote_ip=utils.get_remote_addr(), ) if not valid: 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: 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(), ) 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(), ) 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