Skip to content

Commit 61e2719

Browse files
author
ci bot
committed
Merge branch 'aarthy/auth-plugin' into 'enterprise'
SSO auth support + refactor Users page See merge request dkinternal/testgen/dataops-testgen!297
2 parents f968a2f + 1a55681 commit 61e2719

55 files changed

Lines changed: 461 additions & 440 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

testgen/commands/run_execute_tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def run_execution_steps_in_background(project_code, test_suite):
123123
empty_cache()
124124
background_thread = threading.Thread(
125125
target=run_execution_steps,
126-
args=(project_code, test_suite, session.username),
126+
args=(project_code, test_suite, session.auth.user_display),
127127
)
128128
background_thread.start()
129129
else:

testgen/commands/run_profiling_bridge.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ def run_profiling_in_background(table_group_id):
211211
empty_cache()
212212
background_thread = threading.Thread(
213213
target=run_profiling_queries,
214-
args=(table_group_id, session.username),
214+
args=(table_group_id, session.auth.user_display),
215215
)
216216
background_thread.start()
217217
else:

testgen/common/mixpanel_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def send_event(self, event_name, include_usage=False, **properties):
5757
properties.setdefault("instance_id", self.instance_id)
5858
properties.setdefault("edition", settings.DOCKER_HUB_REPOSITORY)
5959
properties.setdefault("version", settings.VERSION)
60-
properties.setdefault("username", session.username)
60+
properties.setdefault("username", session.auth.user_display)
6161
properties.setdefault("distinct_id", self.get_distinct_id(properties["username"]))
6262
if include_usage:
6363
properties.update(self.get_usage())

testgen/common/models/entity.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
from sqlalchemy import delete, select
88
from sqlalchemy.dialects import postgresql
99
from sqlalchemy.orm import InstrumentedAttribute
10-
from sqlalchemy.sql.elements import BinaryExpression
10+
from sqlalchemy.sql.elements import BinaryExpression, BooleanClauseList
1111

1212
from testgen.common.models import Base, get_current_session
1313
from testgen.utils import is_uuid4, make_json_safe
1414

1515
ENTITY_HASH_FUNCS = {
1616
BinaryExpression: lambda x: str(x.compile(compile_kwargs={"literal_binds": True})),
17+
BooleanClauseList: lambda x: str(x.compile(compile_kwargs={"literal_binds": True})),
1718
tuple: lambda x: [str(y) for y in x],
1819
}
1920

testgen/common/models/user.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
from datetime import UTC, datetime
12
from typing import Literal
23
from uuid import UUID, uuid4
34

4-
from sqlalchemy import Column, String, asc
5+
from sqlalchemy import Column, String, asc, func, update
56
from sqlalchemy.dialects import postgresql
67

8+
from testgen.common.models import get_current_session
79
from testgen.common.models.custom_types import NullIfEmptyString
810
from testgen.common.models.entity import Entity
911

@@ -19,5 +21,25 @@ class User(Entity):
1921
name: str = Column(NullIfEmptyString)
2022
password: str = Column(String)
2123
role: RoleType = Column(String)
24+
latest_login: datetime = Column(postgresql.TIMESTAMP)
25+
26+
_get_by = "username"
27+
_default_order_by = (asc(func.lower(username)),)
28+
29+
def save(self, update_latest_login: bool = False) -> None:
30+
if self.id and not update_latest_login:
31+
values = {
32+
column.key: getattr(self, column.key, None)
33+
for column in self.__table__.columns
34+
if column != User.latest_login
35+
}
36+
query = update(User).where(User.id == self.id).values(**values)
37+
db_session = get_current_session()
38+
db_session.execute(query)
39+
db_session.commit()
40+
User.clear_cache()
41+
else:
42+
if update_latest_login:
43+
self.latest_login = datetime.now(UTC)
44+
super().save()
2245

23-
_default_order_by = (asc(username),)

testgen/template/dbsetup/030_initialize_new_schema_structure.sql

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -622,22 +622,15 @@ CREATE TABLE functional_test_results
622622
);
623623

624624
CREATE TABLE auth_users (
625-
id UUID DEFAULT gen_random_uuid()
625+
id UUID DEFAULT gen_random_uuid()
626626
CONSTRAINT pk_au_id
627627
PRIMARY KEY,
628-
username VARCHAR(20),
629-
email VARCHAR(120),
630-
name VARCHAR(120),
631-
password VARCHAR(120),
632-
role VARCHAR(20)
633-
);
634-
635-
ALTER TABLE auth_users
636-
ADD CONSTRAINT username_check
637-
CHECK (
638-
LENGTH(username) >= 4 AND -- Minimum length of 4 characters
639-
LENGTH(username) <= 20 AND -- Maximum length of 20 characters
640-
username ~ '^[a-zA-Z0-9_]+$' -- Only alphanumeric characters and underscores allowed
628+
username VARCHAR(256),
629+
email VARCHAR(256),
630+
name VARCHAR(256),
631+
password VARCHAR(120),
632+
role VARCHAR(20),
633+
latest_login TIMESTAMP
641634
);
642635

643636
ALTER TABLE auth_users
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
SET SEARCH_PATH TO {SCHEMA_NAME};
2+
3+
ALTER TABLE auth_users
4+
ADD COLUMN latest_login TIMESTAMP,
5+
ALTER COLUMN username TYPE VARCHAR(256),
6+
ALTER COLUMN email TYPE VARCHAR(256),
7+
ALTER COLUMN name TYPE VARCHAR(256),
8+
DROP CONSTRAINT username_check;

testgen/ui/app.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from testgen.ui import bootstrap
1111
from testgen.ui.assets import get_asset_path
1212
from testgen.ui.components import widgets as testgen
13-
from testgen.ui.services import javascript_service, user_session_service
13+
from testgen.ui.services import javascript_service
1414
from testgen.ui.session import session
1515

1616

@@ -23,12 +23,14 @@ def render(log_level: int = logging.INFO):
2323
# Collapse when logging out because the sidebar takes some time to be removed from the DOM
2424
# Collapse for Catalog role since they only have access to one page
2525
initial_sidebar_state="collapsed"
26-
if session.logging_out or user_session_service.user_has_catalog_role()
26+
if session.auth and (session.auth.logging_out or (session.auth.is_logged_in and not session.auth.user_has_permission("view")))
2727
else "auto",
2828
)
2929

3030
application = get_application(log_level=log_level)
3131
application.logger.debug("Starting Streamlit re-run")
32+
if not session.auth:
33+
session.auth = application.auth_class()
3234

3335
status_ok, message = check_basic_configuration()
3436
if not status_ok:
@@ -41,20 +43,18 @@ def render(log_level: int = logging.INFO):
4143
session.page_args_pending_router and session.page_args_pending_router.get("project_code")
4244
) or st.query_params.get("project_code", session.sidebar_project)
4345

44-
if session.authentication_status is None and not session.logging_out:
45-
user_session_service.load_user_session()
46+
if not session.auth.is_logged_in and not session.auth.logging_out:
47+
session.auth.load_user_session()
4648

4749
application.logo.render()
4850

49-
if session.authentication_status and not session.logging_in:
51+
if session.auth.is_logged_in and not session.auth.logging_in:
5052
with st.sidebar:
5153
testgen.sidebar(
5254
projects=Project.select_where(),
5355
current_project=session.sidebar_project,
5456
menu=application.menu,
5557
current_page=session.current_page,
56-
username=session.username,
57-
role=session.auth_role,
5858
version=version_service.get_version(),
5959
support_email=settings.SUPPORT_EMAIL,
6060
)

testgen/ui/assets/scripts.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,6 @@ window.addEventListener('message', async function(event) {
1313
}
1414
});
1515

16-
function removeElements(selectors) {
17-
for (const selector of selectors) {
18-
const element = window.top.document.querySelector(selector);
19-
if (element) {
20-
element.remove();
21-
}
22-
}
23-
}
24-
2516
async function copyToClipboard(text) {
2617
if (navigator.clipboard && window.isSecureContext) {
2718
await navigator.clipboard.writeText(text || '');
@@ -55,4 +46,5 @@ window.testgen = {
5546
states: {},
5647
components: {},
5748
loadedStylesheets: {},
49+
changeLocation: url => window.location.href = url,
5850
};

testgen/ui/assets/style.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ img.dk-logo-img {
7979

8080
/* Sidebar */
8181
[data-testid="stSidebarContent"] [data-testid="stSidebarHeader"] {
82-
padding: 16px 20px;
82+
padding: 16px 20px 20px;
83+
margin-bottom: 0;
84+
height: auto;
8385
}
8486

8587
[data-testid="stSidebarHeader"] .stLogo {
@@ -200,6 +202,7 @@ button[title="Show password text"] {
200202
display: none;
201203
}
202204

205+
.element-container:has(iframe[height="0"][title="extra_streamlit_components.CookieManager.cookie_manager"]),
203206
.element-container:has(iframe[height="0"][title="streamlit_javascript.streamlit_javascript"]),
204207
.element-container:has(iframe[height="0"][title="testgen.ui.components.utils.component.testgen"]) {
205208
display: none !important;

0 commit comments

Comments
 (0)