feat(db): changed users structure for SSO

feat(api): changed user's origin
feat(api): support SSO idp_name
feat(api): SSO ignore relay state
feat(api): allow invite users if SSO is enabled
This commit is contained in:
Taha Yassine Kraiem 2021-12-02 17:36:47 +01:00
parent 5c55bd10ed
commit 7a4977931d
6 changed files with 45 additions and 20 deletions

View file

@ -354,8 +354,8 @@ def get_members(context):
@app.route('/client/members', methods=['PUT', 'POST'])
def add_member(context):
if SAML2_helper.is_saml2_available():
return {"errors": ["please use your SSO server to add teammates"]}
# if SAML2_helper.is_saml2_available():
# return {"errors": ["please use your SSO server to add teammates"]}
data = app.current_request.json_body
return users.create_member(tenant_id=context['tenantId'], user_id=context['userId'], data=data)

View file

@ -1,6 +1,7 @@
from chalice import Blueprint
from chalicelib import _overrides
from chalicelib.utils import SAML2_helper
from chalicelib.utils.SAML2_helper import prepare_request, init_saml_auth
app = Blueprint(__name__)
@ -9,7 +10,6 @@ _overrides.chalice_app(app)
from chalicelib.utils.helper import environ
from onelogin.saml2.auth import OneLogin_Saml2_Logout_Request
from onelogin.saml2.utils import OneLogin_Saml2_Utils
from chalice import Response
from chalicelib.core import users, tenants
@ -54,22 +54,27 @@ def process_sso_assertion():
# session['samlSessionExpiration'] = auth.get_session_expiration()
# print('>>>>')
# print(session)
self_url = OneLogin_Saml2_Utils.get_self_url(req)
if 'RelayState' in request.form and self_url != request.form['RelayState']:
print("====>redirect")
return Response(
status_code=307,
body='',
headers={'Location': auth.redirect_to(request.form['RelayState']), 'Content-Type': 'text/plain'})
# ---- ignore relay-state
# self_url = OneLogin_Saml2_Utils.get_self_url(req)
# if 'RelayState' in request.form and self_url != request.form['RelayState']:
# print("====>redirect to")
# print("====>redirect to")
# return Response(
# status_code=307,
# body='',
# headers={'Location': auth.redirect_to(request.form['RelayState']), 'Content-Type': 'text/plain'})
elif auth.get_settings().is_debug_active():
error_reason = auth.get_last_error_reason()
return {"errors": [error_reason]}
email = auth.get_nameid()
print("received nameId:")
print(email)
existing = users.get_by_email_only(auth.get_nameid())
internal_id = next(iter(user_data.get("internalId", [])), None)
if len(existing) == 0 or existing[0].get("origin") != 'saml':
if len(existing) == 0 or existing[0].get("origin") is None:
tenant_key = user_data.get("tenantKey", [])
if len(tenant_key) == 0:
print("tenantKey not present in assertion")
@ -86,15 +91,16 @@ def process_sso_assertion():
headers={'Location': auth.redirect_to(request.form['RelayState']), 'Content-Type': 'text/plain'})
if len(existing) == 0:
print("== new user ==")
users.create_sso_user(tenant_id=t['tenantId'], email=email, admin=True, origin='saml',
users.create_sso_user(tenant_id=t['tenantId'], email=email, admin=True,
origin=SAML2_helper.get_saml2_provider(),
name=" ".join(user_data.get("firstName", []) + user_data.get("lastName", [])),
internal_id=internal_id)
else:
existing = existing[0]
if existing.get("origin") != 'saml':
print("== migrating user to SAML ==")
if existing.get("origin") is None:
print(f"== migrating user to {SAML2_helper.get_saml2_provider()} ==")
users.update(tenant_id=t['tenantId'], user_id=existing["id"],
changes={"origin": 'saml', "internal_id": internal_id})
changes={"origin": SAML2_helper.get_saml2_provider(), "internal_id": internal_id})
return users.authenticate_sso(email=email, internal_id=internal_id, exp=auth.get_session_expiration())

View file

@ -90,7 +90,7 @@ def prepare_request(request):
'https': 'on' if request.headers.get('x-forwarded-proto', 'http') == 'https' else 'off',
'http_host': request.headers['host'],
'server_port': url_data.port,
'script_name': "/api"+request.path,
'script_name': "/api" + request.path,
'get_data': request.args.copy(),
# Uncomment if using ADFS as IdP, https://github.com/onelogin/python-saml/pull/144
# 'lowercase_urlencoding': True,
@ -103,5 +103,6 @@ def prepare_request(request):
def is_saml2_available():
return idp is not None
def get_saml2_provider():
return "Okta"
return environ.get("idp_name", "saml2") if is_saml2_available() else None

View file

@ -60,5 +60,21 @@ FROM (SELECT tenant_id, role_id
WHERE users.tenant_id = r.tenant_id
AND users.role = 'member';
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_type WHERE typname = 'user_origin') THEN
CREATE TYPE user_origin AS ENUM ('saml');
END IF;
END
$$;
ALTER TABLE public.users
ADD COLUMN IF NOT EXISTS origin user_origin NULL DEFAULT NULL,
ADD COLUMN IF NOT EXISTS internal_id text NULL DEFAULT NULL;
ALTER TABLE public.users
ALTER COLUMN origin TYPE text;
DROP TYPE IF EXISTS user_origin;
COMMIT;

View file

@ -129,7 +129,7 @@ $$
);
CREATE TYPE user_role AS ENUM ('owner', 'admin', 'member');
CREATE TYPE user_origin AS ENUM ('saml');
CREATE TABLE users
(
user_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY,
@ -204,8 +204,9 @@ $$
jwt_iat timestamp without time zone NULL DEFAULT NULL,
data jsonb NOT NULL DEFAULT '{}'::jsonb,
weekly_report boolean NOT NULL DEFAULT TRUE,
origin user_origin NULL DEFAULT NULL,
role_id integer REFERENCES roles (role_id) ON DELETE SET NULL
origin text NULL DEFAULT NULL,
role_id integer REFERENCES roles (role_id) ON DELETE SET NULL,
internal_id text NULL DEFAULT NULL
);

View file

@ -63,4 +63,5 @@ env:
idp_sso_url: ''
idp_x509cert: ''
idp_sls_url: ''
idp_name: ''
assist_secret: ''