-
Notifications
You must be signed in to change notification settings - Fork 369
feat: SSO Improvement - alter user_sessions table to include access token, implement CRUD ops, GET, POST, PATCH APIs and det token CLIs
#9867
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 47 commits
Commits
Show all changes
57 commits
Select commit
Hold shift + click to select a range
75fbfda
create table and crud ops
ShreyaLnuHpe fee92d7
add crud ops in go
ShreyaLnuHpe 5eaa712
integ test wip
ShreyaLnuHpe 0e78f52
integ testing
ShreyaLnuHpe a93a64a
based on comments
ShreyaLnuHpe 4a85904
API structure
ShreyaLnuHpe a8031a3
lint go corrections
ShreyaLnuHpe a8a5ea9
typo
ShreyaLnuHpe 1f689b1
lint check
ShreyaLnuHpe 33c874f
changes per swagger
ShreyaLnuHpe 99b79cf
lint proto
ShreyaLnuHpe 10e2648
add admin nonadmin user auth
ShreyaLnuHpe feb7d09
add E2E test for POST API
ShreyaLnuHpe c9ea59b
add GET CRUD ops
ShreyaLnuHpe 293a21d
add GET API structure
ShreyaLnuHpe a139398
add test-intg for GET API
ShreyaLnuHpe bc3b7d9
based on comments
ShreyaLnuHpe cd7dbeb
DELETE API and tests
ShreyaLnuHpe 2c50362
based on brainstoring and building op1
ShreyaLnuHpe 607f0e4
remove llt table
ShreyaLnuHpe d432aa0
mased on comments
ShreyaLnuHpe 736a9f5
add CLIs
ShreyaLnuHpe 6000477
chore: add revoke token permission
corban-beaird 47d87af
chore: drop WorkspaceCreator from set of roles able to revoke tokens
corban-beaird 7bae276
chore: add rbac token permissions
ShreyaLnuHpe c2427ba
chore: correct check errors & remove token filter from GetALL
ShreyaLnuHpe 6d9c3ff
refactor: clean up migration for readability
corban-beaird 825af40
Merge branch 'main' into shreya/createTable
ShreyaLnuHpe 05e05a5
fix: lint errors
ShreyaLnuHpe 152f3d3
feat: token description in CLI and authentication using token
corban-beaird 13c4a57
chore: clean up linter issues
corban-beaird 3515ffb
changes to revoke in postgres
ShreyaLnuHpe a28fbf7
feat: added API support for token description updates & unified revok…
corban-beaird 4d9f9f8
chore: clean up logging
corban-beaird 6f305b5
chore: update table, update cli, getAccessToken, getAllAccessTokens, …
ShreyaLnuHpe ef03c2a
chore: pretty print cli and unify post api and permissions
ShreyaLnuHpe c49657e
feat: describe cli to take multiple usernames
ShreyaLnuHpe c0262e6
fix: authentication of multi login, add tokenType while rendering
ShreyaLnuHpe f67cffe
fix: change name from long-lived to access token and RBAC access only…
ShreyaLnuHpe 784e69d
chore: refactor Get and Create AccessToken API, add filter option, up…
ShreyaLnuHpe 3fd0e76
chore: Revoke Access Tokens when a User is Deactivated (#10013)
ShreyaLnuHpe 7bd852b
chore: refactor access tokens CLI commands (#10012)
ShreyaLnuHpe ae46bb6
Merge branch 'main' into shreya/createTable
ShreyaLnuHpe 6d70ed4
chore: changes per merge with main
ShreyaLnuHpe 5d887c4
chore: un-nest CLIs, APIs, permissions and DB layer (#10041)
ShreyaLnuHpe bde92d2
chore: remove unwanted part from user.py
ShreyaLnuHpe dcbf014
chore: add release note, minor changes per comments
ShreyaLnuHpe 95e60f2
chore: changes per discussion with ModelDev
ShreyaLnuHpe b385f1e
remove commented lines
ShreyaLnuHpe 51e1e9d
chore: fix as per comments
ShreyaLnuHpe 72596e7
Merge branch 'main' into shreya/createTable
ShreyaLnuHpe 1b8f9e6
chore: add proto files
ShreyaLnuHpe 23b265a
chore: minor fixes
ShreyaLnuHpe 6af1292
chore: change docs and expiration days
ShreyaLnuHpe 0370c6b
chore: changes as per comments
ShreyaLnuHpe 2e464a3
fix: consider till seconds during lifespan comparison
ShreyaLnuHpe e386613
chore: create `TokenCreator` role with permissions to VIEW / CREATE /…
ShreyaLnuHpe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| :orphan: | ||
|
|
||
| **New Features** | ||
|
|
||
| - API / CLI: Adds support for creating and administrating long lived access tokens with user | ||
| defined lifespan for long running automated workflows. This update ensures that users can | ||
| securely generate and manage long-lived access tokens specifically designed for automation, | ||
| reducing operational friction while maintaining robust security protocols. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| import argparse | ||
| import json | ||
| from typing import Any, List, Sequence | ||
|
|
||
| from determined import cli | ||
| from determined.cli import errors, render | ||
| from determined.common import api, util | ||
| from determined.common.api import authentication, bindings | ||
|
|
||
| TOKEN_HEADERS = [ | ||
| "ID", | ||
| "User ID", | ||
| "Description", | ||
| "Created At", | ||
| "Expires At", | ||
| "Revoked", | ||
| "Token Type", | ||
| ] | ||
|
|
||
|
|
||
| def render_token_info(token_info: Sequence[bindings.v1TokenInfo]) -> None: | ||
| values = [ | ||
| [t.id, t.userId, t.description, t.createdAt, t.expiry, t.revoked, t.tokenType] | ||
| for t in token_info | ||
| ] | ||
| render.tabulate_or_csv(TOKEN_HEADERS, values, False) | ||
|
|
||
|
|
||
| def describe_token(args: argparse.Namespace) -> None: | ||
| sess = cli.setup_session(args) | ||
| filter_data = json.dumps({"Token_Ids": args.token_id}) | ||
ShreyaLnuHpe marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| try: | ||
| resp = bindings.get_GetAccessTokens(session=sess, filter=filter_data) | ||
| if args.json or args.yaml: | ||
| json_data = [t.to_json() for t in resp.tokenInfo] | ||
| if args.json: | ||
| render.print_json(json_data) | ||
| else: | ||
| print(util.yaml_safe_dump(json_data, default_flow_style=False)) | ||
| else: | ||
| render_token_info(resp.tokenInfo) | ||
| except api.errors.APIException as e: | ||
| raise errors.CliError(f"Caught APIException: {str(e)}") | ||
| except Exception as e: | ||
| raise errors.CliError(f"Error fetching tokens: {e}") | ||
|
|
||
|
|
||
| def list_tokens(args: argparse.Namespace) -> None: | ||
| sess = cli.setup_session(args) | ||
| filter_data = { | ||
| **({"Username": args.username} if args.username else {}), | ||
| **({"only_Active": args.only_active} if args.only_active else {}), | ||
| } | ||
| try: | ||
| filter_json = json.dumps(filter_data) | ||
| resp = bindings.get_GetAccessTokens(sess, filter=filter_json) | ||
| if args.json or args.yaml: | ||
| json_data = [t.to_json() for t in resp.tokenInfo] | ||
| if args.json: | ||
| render.print_json(json_data) | ||
| else: | ||
| print(util.yaml_safe_dump(json_data, default_flow_style=False)) | ||
| else: | ||
| render_token_info(resp.tokenInfo) | ||
| except Exception as e: | ||
| raise errors.CliError(f"Error fetching tokens: {e}") | ||
|
|
||
|
|
||
| def revoke_token(args: argparse.Namespace) -> None: | ||
| sess = cli.setup_session(args) | ||
| try: | ||
| request = bindings.v1PatchAccessTokenRequest( | ||
| tokenId=args.token_id, description=None, setRevoked=True | ||
| ) | ||
| resp = bindings.patch_PatchAccessToken(sess, body=request, tokenId=args.token_id) | ||
| print(json.dumps(resp.to_json(), indent=2)) | ||
| except api.errors.NotFoundException: | ||
| raise errors.CliError("Token not found") | ||
| print(f"Successfully revoked token {args.token_id}.") | ||
|
|
||
|
|
||
| def create_token(args: argparse.Namespace) -> None: | ||
| sess = cli.setup_session(args) | ||
| username = args.username or sess.username | ||
| user = bindings.get_GetUserByUsername(session=sess, username=username).user | ||
|
|
||
| if user is None or user.id is None: | ||
azhou-determined marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| raise errors.CliError(f"User '{username}' not found or does not have an ID") | ||
|
|
||
| request = bindings.v1PostAccessTokenRequest( | ||
| userId=user.id, lifespan=args.expiration_duration, description=args.description | ||
| ) | ||
| resp = bindings.post_PostAccessToken(sess, body=request).to_json() | ||
|
|
||
| output_string = None | ||
| if args.yaml: | ||
| output_string = util.yaml_safe_dump(resp, default_flow_style=False) | ||
| elif args.json: | ||
| output_string = json.dumps(resp, indent=2) | ||
| else: | ||
| output_string = f'TokenID: {resp["tokenId"]}\nAccess-Token: {resp["token"]}' | ||
azhou-determined marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| print(output_string) | ||
|
|
||
|
|
||
| def edit_token(args: argparse.Namespace) -> None: | ||
| sess = cli.setup_session(args) | ||
| try: | ||
| if args.description and args.token_id: | ||
| request = bindings.v1PatchAccessTokenRequest( | ||
| tokenId=args.token_id, description=args.description, setRevoked=False | ||
| ) | ||
| resp = bindings.patch_PatchAccessToken(sess, body=request, tokenId=args.token_id) | ||
| print(json.dumps(resp.to_json(), indent=2)) | ||
| except api.errors.APIException as e: | ||
| raise errors.CliError(f"Caught APIException: {str(e)}") | ||
| except api.errors.NotFoundException: | ||
| raise errors.CliError("Token not found") | ||
| print(f"Successfully updated token with ID: {args.token_id}.") | ||
|
|
||
|
|
||
| def login_with_token(args: argparse.Namespace) -> None: | ||
ShreyaLnuHpe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| unauth_session = api.UnauthSession(master=args.master, cert=cli.cert) | ||
| auth_headers = {"Authorization": f"Bearer {args.token}"} | ||
| user_data = unauth_session.get("/api/v1/me", headers=auth_headers).json() | ||
ShreyaLnuHpe marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| username = user_data.get("user").get("username") | ||
|
|
||
| token_store = authentication.TokenStore(args.master) | ||
| token_store.set_token(username, args.token) | ||
| token_store.set_active(username) | ||
| print(f"Authenticated as {username}.") | ||
|
|
||
|
|
||
| # fmt: off | ||
|
|
||
| args_description = [ | ||
| cli.Cmd("token tkn", None, "manage access tokens", [ | ||
| cli.Cmd("describe", describe_token, "describe token info", [ | ||
| cli.Arg("token_id", type=int, nargs=argparse.ONE_OR_MORE, default=None, | ||
| help="token id(s) specifying access tokens to describe"), | ||
| cli.Group( | ||
| cli.output_format_args["json"], | ||
| cli.output_format_args["yaml"], | ||
| ), | ||
| ]), | ||
| cli.Cmd("list ls", list_tokens, "list access tokens for all users", [ | ||
| cli.Arg("username", type=str, nargs=argparse.OPTIONAL, | ||
| help="list token for the given username", default=None), | ||
| cli.Arg("--only-active", action="store_true", default=None, | ||
ShreyaLnuHpe marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| help="list only the active tokens"), | ||
| cli.Group( | ||
| cli.output_format_args["json"], | ||
| cli.output_format_args["yaml"], | ||
| ), | ||
| ]), | ||
| cli.Cmd("revoke", revoke_token, "revoke token", [ | ||
| cli.Arg("token_id", help="revoke given access token id"), | ||
| ]), | ||
| cli.Cmd("create", create_token, "create token", [ | ||
| cli.Arg("username", type=str, nargs=argparse.OPTIONAL, | ||
ShreyaLnuHpe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| help="name of user to create token", default=None), | ||
| cli.Arg("--expiration-duration", "-e", type=str, | ||
ShreyaLnuHpe marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| help="give expiry duration like 2h or 5m or 10s"), | ||
| cli.Arg("--description", "-d", type=str, default=None, | ||
| help="description of new token"), | ||
| cli.Group( | ||
| cli.output_format_args["json"], | ||
| cli.output_format_args["yaml"], | ||
| ), | ||
| ]), | ||
| cli.Cmd("edit", edit_token, "edit token info", [ | ||
| cli.Arg("token_id", help="edit given access token info"), | ||
| cli.Arg("--description", "-d", type=str, default=None, | ||
| help="description of token to edit"), | ||
| cli.Group( | ||
| cli.output_format_args["json"], | ||
| cli.output_format_args["yaml"], | ||
| ), | ||
| ]), | ||
| cli.Cmd("login", login_with_token, "log in with token", [ | ||
| cli.Arg("token", help="token to use for authentication", default=None), | ||
| ]), | ||
| ]) | ||
| ] # type: List[Any] | ||
|
|
||
| # fmt: on | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.