diff --git a/api/chalicelib/blueprints/bp_core_dynamic.py b/api/chalicelib/blueprints/bp_core_dynamic.py index 14fa6de86..af674f762 100644 --- a/api/chalicelib/blueprints/bp_core_dynamic.py +++ b/api/chalicelib/blueprints/bp_core_dynamic.py @@ -22,13 +22,6 @@ app = Blueprint(__name__) _overrides.chalice_app(app) -@app.route('/signedups', methods=['GET'], authorizer=None) -def signed_ups(): - return { - 'data': tenants.get_tenants() - } - - @app.route('/login', methods=['POST'], authorizer=None) def login(): data = app.current_request.json_body @@ -52,7 +45,7 @@ def login(): c = tenants.get_by_tenant_id(tenant_id) c.pop("createdAt") c["projects"] = projects.get_projects(tenant_id=tenant_id, recording_state=True, recorded=True, - stack_integrations=True) + stack_integrations=True, version=True) c["smtp"] = helper.has_smtp() return { 'jwt': r.pop('jwt'), @@ -83,7 +76,7 @@ def get_account(context): @app.route('/projects', methods=['GET']) def get_projects(context): return {"data": projects.get_projects(tenant_id=context["tenantId"], recording_state=True, gdpr=True, recorded=True, - stack_integrations=True)} + stack_integrations=True, version=True)} @app.route('/projects', methods=['POST', 'PUT']) @@ -127,7 +120,7 @@ def get_client(context): if r is not None: r.pop("createdAt") r["projects"] = projects.get_projects(tenant_id=context['tenantId'], recording_state=True, recorded=True, - stack_integrations=True) + stack_integrations=True, version=True) return { 'data': r } @@ -148,7 +141,7 @@ def put_client(context): @app.route('/signup', methods=['GET'], authorizer=None) def get_all_signup(): - return {"data": signup.get_signed_ups()} + return {"data": tenants.tenants_exists()} @app.route('/signup', methods=['POST', 'PUT'], authorizer=None) diff --git a/api/chalicelib/core/projects.py b/api/chalicelib/core/projects.py index 01c87468b..a9e1cdf92 100644 --- a/api/chalicelib/core/projects.py +++ b/api/chalicelib/core/projects.py @@ -41,7 +41,7 @@ def __create(tenant_id, name): @dev.timed -def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, stack_integrations=False): +def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, stack_integrations=False,version=False): with pg_client.PostgresClient() as cur: cur.execute(f"""\ SELECT @@ -49,6 +49,7 @@ def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, st {',s.gdpr' if gdpr else ''} {',COALESCE((SELECT TRUE FROM public.sessions WHERE sessions.project_id = s.project_id LIMIT 1), FALSE) AS recorded' if recorded else ''} {',stack_integrations.count>0 AS stack_integrations' if stack_integrations else ''} + {',(SELECT tracker_version FROM public.sessions WHERE sessions.project_id = s.project_id ORDER BY start_ts DESC LIMIT 1) AS tracker_version' if version else ''} FROM public.projects AS s {'LEFT JOIN LATERAL (SELECT COUNT(*) AS count FROM public.integrations WHERE s.project_id = integrations.project_id LIMIT 1) AS stack_integrations ON TRUE' if stack_integrations else ''} where s.deleted_at IS NULL diff --git a/api/chalicelib/core/signup.py b/api/chalicelib/core/signup.py index a92289140..50fc6e41a 100644 --- a/api/chalicelib/core/signup.py +++ b/api/chalicelib/core/signup.py @@ -1,39 +1,31 @@ from chalicelib.utils import helper from chalicelib.utils import pg_client -from chalicelib.core import users, telemetry +from chalicelib.core import users, telemetry, tenants from chalicelib.utils import captcha import json from chalicelib.utils.TimeUTC import TimeUTC from chalicelib.utils.helper import environ -def get_signed_ups(): - with pg_client.PostgresClient() as cur: - cur.execute("SELECT tenant_id, name FROM public.tenants;") - rows = cur.fetchall() - return helper.list_to_camel_case(rows) - - def create_step1(data): print(f"===================== SIGNUP STEP 1 AT {TimeUTC.to_human_readable(TimeUTC.now())} UTC") errors = [] + if tenants.tenants_exists(): + return {"errors": ["tenants already registered"]} email = data.get("email") print(f"=====================> {email}") password = data.get("password") print("Verifying email validity") - email_exists = False if email is None or len(email) < 5 or not helper.is_valid_email(email): errors.append("Invalid email address.") else: print("Verifying email existance") if users.email_exists(email): - # errors.append("Email address already in use.") - email_exists = True + errors.append("Email address already in use.") if users.get_deleted_user_by_email(email) is not None: - # errors.append("Email address previously deleted.") - email_exists = True + errors.append("Email address previously deleted.") print("Verifying captcha") if helper.allow_captcha() and not captcha.is_valid(data["g-recaptcha-response"]): @@ -57,12 +49,6 @@ def create_step1(data): project_name = data.get("projectName") if project_name is None or len(project_name) < 1: project_name = "my first project" - signed_ups = get_signed_ups() - if len(signed_ups) > 0 and data.get("tenantId") is None: - errors.append("Tenant already exists, please select it from dropdown") - elif len(signed_ups) == 0 and data.get("tenantId") is not None \ - or len(signed_ups) > 0 and data.get("tenantId") not in [t['tenantId'] for t in signed_ups]: - errors.append("Tenant not found") if len(errors) > 0: print("==> error") @@ -77,41 +63,7 @@ def create_step1(data): "organizationName": company_name, "versionNumber": environ["version_number"] } - if data.get("tenantId") is not None: - update_user = """ - u AS ( - UPDATE public.users - SET name = %(fullname)s, deleted_at=NULL - WHERE email=%(email)s - RETURNING user_id,email, role, name - ) - UPDATE public.basic_authentication - SET password= crypt(%(password)s, gen_salt('bf', 12)) - WHERE user_id = (SELECT user_id FROM u)""" - insert_user = """ - a AS ( - UPDATE public.users - SET role='admin' - WHERE role ='owner' - ), - u AS ( - INSERT INTO public.users (email, role, name, data) - VALUES (%(email)s, 'owner', %(fullname)s,%(data)s) - RETURNING user_id,email,role,name - ) - INSERT INTO public.basic_authentication (user_id, password, generated_password) - VALUES ((SELECT user_id FROM u), crypt(%(password)s, gen_salt('bf', 12)), FALSE)""" - query = f"""\ - WITH t AS ( - UPDATE public.tenants - SET name = %(organizationName)s, - version_number = %(versionNumber)s - RETURNING api_key - ), - {update_user if email_exists else insert_user} - RETURNING (SELECT api_key FROM t) AS api_key,(SELECT project_id FROM projects LIMIT 1) AS project_id;""" - else: - query = f"""\ + query = f"""\ WITH t AS ( INSERT INTO public.tenants (name, version_number, edition) VALUES (%(organizationName)s, %(versionNumber)s, 'fos') diff --git a/api/chalicelib/core/tenants.py b/api/chalicelib/core/tenants.py index 4b439cfef..054b3f5d5 100644 --- a/api/chalicelib/core/tenants.py +++ b/api/chalicelib/core/tenants.py @@ -77,7 +77,7 @@ def update(tenant_id, user_id, data): return edit_client(tenant_id=tenant_id, changes=changes) -def get_tenants(): +def tenants_exists(): with pg_client.PostgresClient() as cur: - cur.execute(f"SELECT name FROM public.tenants") - return helper.list_to_camel_case(cur.fetchall()) + cur.execute(f"SELECT EXISTS(SELECT 1 FROM public.tenants)") + return cur.fetchone()["exists"] diff --git a/ee/api/chalicelib/blueprints/bp_core_dynamic.py b/ee/api/chalicelib/blueprints/bp_core_dynamic.py index 3eeff250e..9abd4cab8 100644 --- a/ee/api/chalicelib/blueprints/bp_core_dynamic.py +++ b/ee/api/chalicelib/blueprints/bp_core_dynamic.py @@ -22,13 +22,6 @@ app = Blueprint(__name__) _overrides.chalice_app(app) -@app.route('/signedups', methods=['GET'], authorizer=None) -def signed_ups(): - return { - 'data': tenants.get_tenants() - } - - @app.route('/login', methods=['POST'], authorizer=None) def login(): data = app.current_request.json_body @@ -54,7 +47,7 @@ def login(): c = tenants.get_by_tenant_id(tenant_id) c.pop("createdAt") c["projects"] = projects.get_projects(tenant_id=tenant_id, recording_state=True, recorded=True, - stack_integrations=True) + stack_integrations=True, version=True) return { 'jwt': r.pop('jwt'), 'data': { @@ -85,7 +78,7 @@ def get_account(context): @app.route('/projects', methods=['GET']) def get_projects(context): return {"data": projects.get_projects(tenant_id=context["tenantId"], recording_state=True, gdpr=True, recorded=True, - stack_integrations=True)} + stack_integrations=True, version=True)} @app.route('/projects', methods=['POST', 'PUT']) @@ -129,7 +122,7 @@ def get_client(context): if r is not None: r.pop("createdAt") r["projects"] = projects.get_projects(tenant_id=context['tenantId'], recording_state=True, recorded=True, - stack_integrations=True) + stack_integrations=True, version=True) return { 'data': r } @@ -148,10 +141,9 @@ def put_client(context): return tenants.update(tenant_id=context["tenantId"], user_id=context["userId"], data=data) -# TODO: delete this for production; it is used for dev only @app.route('/signup', methods=['GET'], authorizer=None) def get_all_signup(): - return {"data": signup.get_signed_ups()} + return {"data": tenants.tenants_exists()} @app.route('/signup', methods=['POST', 'PUT'], authorizer=None) @@ -391,7 +383,8 @@ def change_password_by_invitation(): if user["expiredChange"]: return {"errors": ["expired change, please re-use the invitation link"]} - return users.set_password_invitation(new_password=data["password"], user_id=user["userId"]) + return users.set_password_invitation(new_password=data["password"], user_id=user["userId"], + tenant_id=user["tenantId"]) @app.route('/client/members/{memberId}', methods=['PUT', 'POST']) diff --git a/ee/api/chalicelib/core/projects.py b/ee/api/chalicelib/core/projects.py index dc9cbfd23..cb1e7b1de 100644 --- a/ee/api/chalicelib/core/projects.py +++ b/ee/api/chalicelib/core/projects.py @@ -40,7 +40,7 @@ def __create(tenant_id, name): return get_project(tenant_id=tenant_id, project_id=project_id, include_gdpr=True) -def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, stack_integrations=False): +def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, stack_integrations=False, version=False): with pg_client.PostgresClient() as cur: cur.execute( cur.mogrify(f"""\ @@ -49,6 +49,7 @@ def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, st {',s.gdpr' if gdpr else ''} {',COALESCE((SELECT TRUE FROM public.sessions WHERE sessions.project_id = s.project_id LIMIT 1), FALSE) AS recorded' if recorded else ''} {',stack_integrations.count>0 AS stack_integrations' if stack_integrations else ''} + {',(SELECT tracker_version FROM public.sessions WHERE sessions.project_id = s.project_id ORDER BY start_ts DESC LIMIT 1) AS tracker_version' if version else ''} FROM public.projects AS s {'LEFT JOIN LATERAL (SELECT COUNT(*) AS count FROM public.integrations WHERE s.project_id = integrations.project_id LIMIT 1) AS stack_integrations ON TRUE' if stack_integrations else ''} where s.tenant_id =%(tenant_id)s diff --git a/ee/api/chalicelib/core/signup.py b/ee/api/chalicelib/core/signup.py index fbfd4f831..652867c25 100644 --- a/ee/api/chalicelib/core/signup.py +++ b/ee/api/chalicelib/core/signup.py @@ -1,22 +1,17 @@ from chalicelib.utils import helper from chalicelib.utils import pg_client -from chalicelib.core import users, telemetry +from chalicelib.core import users, telemetry, tenants from chalicelib.utils import captcha import json from chalicelib.utils.TimeUTC import TimeUTC from chalicelib.utils.helper import environ -def get_signed_ups(): - with pg_client.PostgresClient() as cur: - cur.execute("SELECT tenant_id, name FROM public.tenants;") - rows = cur.fetchall() - return helper.list_to_camel_case(rows) - - def create_step1(data): print(f"===================== SIGNUP STEP 1 AT {TimeUTC.to_human_readable(TimeUTC.now())} UTC") errors = [] + if tenants.tenants_exists(): + return {"errors":["tenants already registered"]} email = data.get("email") print(f"=====================> {email}") @@ -54,64 +49,37 @@ def create_step1(data): project_name = data.get("projectName") if project_name is None or len(project_name) < 1: project_name = "my first project" - signed_ups = get_signed_ups() - if len(signed_ups) == 0 and data.get("tenantId") is not None \ - or len(signed_ups) > 0 and data.get("tenantId") is not None\ - and data.get("tenantId") not in [t['tenantId'] for t in signed_ups]: - errors.append("Tenant not found") if len(errors) > 0: print("==> error") print(errors) return {"errors": errors} print("No errors detected") print("Decomposed infos") - tenant_id = data.get("tenantId") params = {"email": email, "password": password, "fullname": fullname, "companyName": company_name, "projectName": project_name, "versionNumber": environ["version_number"], "data": json.dumps({"lastAnnouncementView": TimeUTC.now()})} - if tenant_id is not None: - query = """\ - WITH t AS ( - UPDATE public.tenants - SET name = %(companyName)s, - version_number = %(versionNumber)s - WHERE tenant_id=%(tenant_id)s + query = """\ + WITH t AS ( + INSERT INTO public.tenants (name, version_number, edition) + VALUES (%(companyName)s, %(versionNumber)s, 'ee') RETURNING tenant_id, api_key - ), - u AS ( - UPDATE public.users - SET email = %(email)s, - name = %(fullname)s, - WHERE role ='owner' AND tenant_id=%(tenant_id)s - RETURNING user_id,email, role, name - ) - UPDATE public.basic_authentication - SET password= crypt(%(password)s, gen_salt('bf', 12)) - WHERE user_id = (SELECT user_id FROM u) - RETURNING %(tenant_id)s AS tenant_id""" - else: - query = """\ - WITH t AS ( - INSERT INTO public.tenants (name, version_number, edition) - VALUES (%(companyName)s, %(versionNumber)s, 'ee') - RETURNING tenant_id, api_key - ), - u AS ( - INSERT INTO public.users (tenant_id, email, role, name, data) - VALUES ((SELECT tenant_id FROM t), %(email)s, 'owner', %(fullname)s,%(data)s) - RETURNING user_id,email,role,name - ), - au AS ( - INSERT INTO public.basic_authentication (user_id, password, generated_password) - VALUES ((SELECT user_id FROM u), crypt(%(password)s, gen_salt('bf', 12)), FALSE) - ) - INSERT INTO public.projects (tenant_id, name, active) - VALUES ((SELECT t.tenant_id FROM t), %(projectName)s, TRUE) - RETURNING tenant_id,project_id, (SELECT api_key FROM t) AS api_key;""" + ), + u AS ( + INSERT INTO public.users (tenant_id, email, role, name, data) + VALUES ((SELECT tenant_id FROM t), %(email)s, 'owner', %(fullname)s,%(data)s) + RETURNING user_id,email,role,name + ), + au AS ( + INSERT INTO public.basic_authentication (user_id, password, generated_password) + VALUES ((SELECT user_id FROM u), crypt(%(password)s, gen_salt('bf', 12)), FALSE) + ) + INSERT INTO public.projects (tenant_id, name, active) + VALUES ((SELECT t.tenant_id FROM t), %(projectName)s, TRUE) + RETURNING tenant_id,project_id, (SELECT api_key FROM t) AS api_key;""" with pg_client.PostgresClient() as cur: cur.execute(cur.mogrify(query, params)) diff --git a/ee/api/chalicelib/core/tenants.py b/ee/api/chalicelib/core/tenants.py index 7855e2e81..eb827d827 100644 --- a/ee/api/chalicelib/core/tenants.py +++ b/ee/api/chalicelib/core/tenants.py @@ -101,7 +101,7 @@ def update(tenant_id, user_id, data): return edit_client(tenant_id=tenant_id, changes=changes) -def get_tenants(): +def tenants_exists(): with pg_client.PostgresClient() as cur: - cur.execute(f"SELECT name FROM public.tenants") - return helper.list_to_camel_case(cur.fetchall()) + cur.execute(f"SELECT EXISTS(SELECT 1 FROM public.tenants)") + return cur.fetchone()["exists"] diff --git a/ee/api/chalicelib/core/users.py b/ee/api/chalicelib/core/users.py index 4c6032fc2..034a9549d 100644 --- a/ee/api/chalicelib/core/users.py +++ b/ee/api/chalicelib/core/users.py @@ -440,11 +440,11 @@ def change_password(tenant_id, user_id, email, old_password, new_password): "jwt": authenticate(email, new_password)["jwt"]} -def set_password_invitation(user_id, new_password): +def set_password_invitation(tenant_id, user_id, new_password): changes = {"password": new_password, "generatedPassword": False, "invitationToken": None, "invitedAt": None, "changePwdExpireAt": None, "changePwdToken": None} - user = update(tenant_id=-1, user_id=user_id, changes=changes) + user = update(tenant_id=tenant_id, user_id=user_id, changes=changes) r = authenticate(user['email'], new_password) tenant_id = r.pop("tenantId")