Skip to content

Commit 16ffcb8

Browse files
committed
Rework the User table (#3251)
* Rework the User table Rework involves the following things: 1. Modifications to the User table: - Drop the password column - Drop the auth_tokens relationship (will be later replaced with API keys table) - Add a column to keep track of OIDC_IDs of the user - Add the user relationship to the Datasets and Metadata table 2. Removed the user API 3. Changes to the user_management_cli file: - Kept only list users functionality. PBENCH-1080
1 parent 06b0718 commit 16ffcb8

27 files changed

+602
-1305
lines changed

lib/pbench/cli/server/user_management.py

Lines changed: 4 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,12 @@
44
from pbench.cli import pass_cli_context
55
from pbench.cli.server import config_setup
66
from pbench.cli.server.options import common_options
7-
from pbench.server.database.models.users import Roles, User
7+
from pbench.server.database.models.users import User
88

9-
USER_LIST_ROW_FORMAT = "{0:15}\t{1:15}\t{2:15}\t{3:15}\t{4:20}"
10-
USER_LIST_HEADER_ROW = USER_LIST_ROW_FORMAT.format(
11-
"Username", "First Name", "Last Name", "Registered On", "Email"
12-
)
9+
USER_LIST_ROW_FORMAT = "{0:15}\t{1:36}"
10+
USER_LIST_HEADER_ROW = USER_LIST_ROW_FORMAT.format("Username", "OIDC ID")
1311

1412

15-
# User create CLI
1613
@click.group("user_group")
1714
@click.version_option()
1815
@pass_cli_context
@@ -22,94 +19,6 @@ def user_command_cli(context):
2219
pass
2320

2421

25-
@user_command_cli.command()
26-
@pass_cli_context
27-
@click.option(
28-
"--username",
29-
prompt=True,
30-
required=True,
31-
help="pbench server account username (will prompt if unspecified)",
32-
)
33-
@click.option(
34-
"--password",
35-
prompt=True,
36-
hide_input=True,
37-
required=True,
38-
help="pbench server account password (will prompt if unspecified)",
39-
)
40-
@click.option(
41-
"--email",
42-
prompt=True,
43-
required=True,
44-
help="pbench server account email (will prompt if unspecified)",
45-
)
46-
@click.option(
47-
"--first-name",
48-
required=False,
49-
help="pbench server account first name (will prompt if unspecified)",
50-
)
51-
@click.option(
52-
"--last-name",
53-
required=False,
54-
help="pbench server account last name (will prompt if unspecified)",
55-
)
56-
@click.option(
57-
"--role",
58-
type=click.Choice([role.name for role in Roles], case_sensitive=False),
59-
required=False,
60-
help="Optional role of the user such as Admin",
61-
)
62-
@common_options
63-
def user_create(
64-
context: object,
65-
username: str,
66-
password: str,
67-
email: str,
68-
first_name: str,
69-
last_name: str,
70-
role: str,
71-
) -> None:
72-
try:
73-
config_setup(context)
74-
user = User(
75-
username=username,
76-
password=password,
77-
first_name=first_name,
78-
last_name=last_name,
79-
email=email,
80-
role=role if role else "",
81-
)
82-
user.add()
83-
if user.is_admin():
84-
click.echo(f"Admin user {username} registered")
85-
else:
86-
click.echo(f"User {username} registered")
87-
rv = 0
88-
except Exception as exc:
89-
click.echo(exc, err=True)
90-
rv = 2 if isinstance(exc, BadConfig) else 1
91-
92-
click.get_current_context().exit(rv)
93-
94-
95-
# User delete CLI
96-
@user_command_cli.command()
97-
@common_options
98-
@click.argument("username")
99-
@pass_cli_context
100-
def user_delete(context: object, username: str) -> None:
101-
try:
102-
# Delete the the user with specified username
103-
config_setup(context)
104-
User.delete(username=username)
105-
rv = 0
106-
except Exception as exc:
107-
click.echo(exc, err=True)
108-
rv = 2 if isinstance(exc, BadConfig) else 1
109-
110-
click.get_current_context().exit(rv)
111-
112-
11322
# Users list CLI
11423
@user_command_cli.command()
11524
@common_options
@@ -126,10 +35,7 @@ def user_list(context: object) -> None:
12635
click.echo(
12736
USER_LIST_ROW_FORMAT.format(
12837
user.username,
129-
user.first_name,
130-
user.last_name,
131-
user.registered_on.strftime("%Y-%m-%d"),
132-
user.email,
38+
user.id,
13339
)
13440
)
13541

@@ -139,80 +45,3 @@ def user_list(context: object) -> None:
13945
rv = 2 if isinstance(exc, BadConfig) else 1
14046

14147
click.get_current_context().exit(rv)
142-
143-
144-
# User update CLI
145-
@user_command_cli.command()
146-
@common_options
147-
@click.argument("updateuser")
148-
@click.option(
149-
"--username",
150-
required=False,
151-
help="Specify the new username",
152-
)
153-
@click.option(
154-
"--email",
155-
required=False,
156-
help="Specify the new email",
157-
)
158-
@click.option(
159-
"--first-name",
160-
required=False,
161-
help="Specify the new first name",
162-
)
163-
@click.option(
164-
"--last-name",
165-
required=False,
166-
help="Specify the new last name",
167-
)
168-
@click.option(
169-
"--role",
170-
required=False,
171-
type=click.Choice([role.name for role in Roles], case_sensitive=False),
172-
help="Specify the new role",
173-
)
174-
@pass_cli_context
175-
def user_update(
176-
context: object,
177-
updateuser: str,
178-
username: str,
179-
first_name: str,
180-
last_name: str,
181-
email: str,
182-
role: str,
183-
) -> None:
184-
try:
185-
config_setup(context)
186-
# Query the user
187-
user = User.query(username=updateuser)
188-
189-
if user is None:
190-
click.echo(f"User {updateuser} doesn't exist")
191-
rv = 1
192-
else:
193-
dict_to_update = {}
194-
if username:
195-
dict_to_update["username"] = username
196-
197-
if first_name:
198-
dict_to_update["first_name"] = first_name
199-
200-
if last_name:
201-
dict_to_update["last_name"] = last_name
202-
203-
if email:
204-
dict_to_update["email"] = email
205-
206-
if role:
207-
dict_to_update["role"] = role
208-
209-
# Update the user
210-
user.update(**dict_to_update)
211-
212-
click.echo(f"User {updateuser} updated")
213-
rv = 0
214-
except Exception as exc:
215-
click.echo(exc, err=True)
216-
rv = 2 if isinstance(exc, BadConfig) else 1
217-
218-
click.get_current_context().exit(rv)

lib/pbench/client/__init__.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,9 @@ class API(Enum):
4949
DATASETS_SEARCH = "datasets_search"
5050
DATASETS_VALUES = "datasets_values"
5151
ENDPOINTS = "endpoints"
52-
LOGIN = "login"
53-
LOGOUT = "logout"
54-
REGISTER = "register"
5552
SERVER_AUDIT = "server_audit"
5653
SERVER_SETTINGS = "server_settings"
5754
UPLOAD = "upload"
58-
USER = "user"
5955

6056

6157
class PbenchServerClient:
@@ -205,7 +201,7 @@ def put(
205201
api: API,
206202
uri_params: Optional[JSONOBJECT] = None,
207203
*,
208-
json: Optional[dict[str, str]] = None,
204+
json: Optional[JSONOBJECT] = None,
209205
headers: Optional[dict[str, str]] = None,
210206
raise_error: bool = True,
211207
**kwargs,
@@ -240,7 +236,7 @@ def post(
240236
api: API,
241237
uri_params: Optional[JSONOBJECT] = None,
242238
*,
243-
json: Optional[dict[str, str]] = None,
239+
json: Optional[JSONOBJECT] = None,
244240
headers: Optional[dict[str, str]] = None,
245241
raise_error: bool = True,
246242
**kwargs,
@@ -443,6 +439,16 @@ def get_list(self, **kwargs) -> Iterator[Dataset]:
443439
break
444440
json = self.get(uri=next_url).json()
445441

442+
def get_user(self, username: str, add_auth_header: bool = True) -> JSONOBJECT:
443+
""" """
444+
if add_auth_header:
445+
return self.get(
446+
api=API.USER, uri_params={"target_username": username}
447+
).json()
448+
response = self.session.get(self._uri(API.USER, {"target_username": username}))
449+
response.raise_for_status()
450+
return response.json()
451+
446452
def get_metadata(self, dataset_id: str, metadata: list[str]) -> JSONOBJECT:
447453
"""Return requested metadata for a specified dataset.
448454

lib/pbench/client/oidc_admin.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,17 +102,16 @@ def user_login(self, client_id: str, username: str, password: str) -> dict:
102102
data = {
103103
"client_id": client_id,
104104
"grant_type": "password",
105-
"scope": "profile email",
105+
"scope": "openid profile email",
106106
"username": username,
107107
"password": password,
108108
}
109109
return self.post(path=url_path, data=data).json()
110110

111-
def get_user(self, username: str, token: str) -> dict:
111+
def get_user(self, token: str) -> dict:
112112
"""Get the OIDC user representation dict.
113113
114114
Args:
115-
username: username to query
116115
token: access_token string to validate
117116
118117
Returns:
@@ -132,10 +131,9 @@ def get_user(self, username: str, token: str) -> dict:
132131
}
133132
"""
134133
response = self.get(
135-
f"admin/realms/{self.OIDC_REALM}/users",
134+
f"/realms/{self.OIDC_REALM}/protocol/openid-connect/userinfo",
136135
headers={"Authorization": f"Bearer {token}"},
137-
username=username,
138136
)
139137
if response.status_code == HTTPStatus.OK:
140-
return response.json()[0]
138+
return response.json()
141139
return {}

lib/pbench/server/api/__init__.py

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
from pbench.server.api.resources.server_audit import ServerAudit
3737
from pbench.server.api.resources.server_settings import ServerSettings
3838
from pbench.server.api.resources.upload_api import Upload
39-
from pbench.server.api.resources.users_api import Login, Logout, RegisterUser, UserAPI
4039
import pbench.server.auth.auth as Auth
4140
from pbench.server.database import init_db
4241
from pbench.server.database.database import Database
@@ -131,24 +130,6 @@ def register_endpoints(api: Api, app: Flask, config: PbenchServerConfig):
131130
endpoint="endpoints",
132131
resource_class_args=(config,),
133132
)
134-
api.add_resource(
135-
Login,
136-
f"{base_uri}/login",
137-
endpoint="login",
138-
resource_class_args=(config,),
139-
)
140-
api.add_resource(
141-
Logout,
142-
f"{base_uri}/logout",
143-
endpoint="logout",
144-
resource_class_args=(config,),
145-
)
146-
api.add_resource(
147-
RegisterUser,
148-
f"{base_uri}/register",
149-
endpoint="register",
150-
resource_class_args=(config,),
151-
)
152133
api.add_resource(
153134
ServerAudit,
154135
f"{base_uri}/server/audit",
@@ -163,11 +144,6 @@ def register_endpoints(api: Api, app: Flask, config: PbenchServerConfig):
163144
endpoint="server_settings",
164145
resource_class_args=(config,),
165146
)
166-
api.add_resource(
167-
UserAPI,
168-
f"{base_uri}/user/<string:target_username>",
169-
endpoint="user",
170-
)
171147
api.add_resource(
172148
Upload,
173149
f"{base_uri}/upload/<string:filename>",

lib/pbench/server/api/resources/__init__.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ def convert_username(value: Union[str, None], _) -> Union[str, None]:
402402
if not user:
403403
raise ConversionError(value, "username", http_status=HTTPStatus.NOT_FOUND)
404404

405-
return str(user.id)
405+
return user.id
406406

407407

408408
def convert_dataset(value: str, _) -> Dataset:
@@ -1579,11 +1579,11 @@ def _get_dataset_metadata(
15791579
metadata = {}
15801580
for i in requested_items:
15811581
native_key = Metadata.get_native_key(i)
1582-
user_id = None
1582+
user: Optional[User] = None
15831583
if native_key == Metadata.USER:
1584-
user_id = Auth.get_current_user_id()
1584+
user = Auth.token_auth.current_user()
15851585
try:
1586-
metadata[i] = Metadata.getvalue(dataset=dataset, key=i, user_id=user_id)
1586+
metadata[i] = Metadata.getvalue(dataset=dataset, key=i, user=user)
15871587
except MetadataError:
15881588
metadata[i] = None
15891589

@@ -1610,11 +1610,11 @@ def _set_dataset_metadata(
16101610
fail: dict[str, str] = {}
16111611
for k, v in metadata.items():
16121612
native_key = Metadata.get_native_key(k)
1613-
user_id = None
1613+
user: Optional[User] = None
16141614
if native_key == Metadata.USER:
1615-
user_id = Auth.get_current_user_id()
1615+
user = Auth.token_auth.current_user()
16161616
try:
1617-
Metadata.setvalue(key=k, value=v, dataset=dataset, user_id=user_id)
1617+
Metadata.setvalue(key=k, value=v, dataset=dataset, user=user)
16181618
except MetadataError as e:
16191619
fail[k] = str(e)
16201620
return fail

lib/pbench/server/api/resources/datasets_metadata.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
Metadata,
2424
MetadataBadValue,
2525
MetadataError,
26+
User,
2627
)
2728

2829

0 commit comments

Comments
 (0)