-
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 all 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
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,25 @@ | ||
| :orphan: | ||
|
|
||
| **New Features** | ||
|
|
||
| - API/CLI: Add support for access tokens. Add the ability create and administer access tokens for | ||
| users to authenticate in automated workflows. Users can define the lifespan of these tokens, | ||
| making it easier to securely authenticate and run processes. This feature enhances automation | ||
| while maintaining strong security protocols by allowing tighter control over token usage and | ||
| expiration. | ||
|
|
||
| - CLI: | ||
|
|
||
| - ``det token create``: Create a new access token. | ||
| - ``det token login``: Sign in with an access token. | ||
| - ``det token edit``: Update an access token's description. | ||
| - ``det token list``: List all active access tokens, with options for displaying revoked | ||
| tokens. | ||
| - ``det token describe``: Show details of specific access tokens. | ||
| - ``det token revoke``: Revoke an access token. | ||
|
|
||
| - API: | ||
|
|
||
| - ``POST /api/v1/tokens``: Create a new access token. | ||
| - ``GET /api/v1/tokens``: Retrieve a list of access tokens. | ||
| - ``PATCH /api/v1/tokens/{token_id}``: Edit an existing access token. |
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,6 @@ | ||
| :orphan: | ||
|
|
||
| **New Features** | ||
|
|
||
| - New RBAC role: Add a ``TokenCreator`` RBAC role, which allows users to create, view, and revoke | ||
| their own access tokens. This role can only be assigned globally. |
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,200 @@ | ||
| 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) | ||
| try: | ||
| resp = bindings.get_GetAccessTokens(session=sess, tokenIds=args.token_id) | ||
| 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) | ||
| try: | ||
| username = args.username if args.username else None | ||
| show_inactive = True if args.show_inactive else False | ||
| resp = bindings.get_GetAccessTokens(sess, username=username, showInactive=show_inactive) | ||
| 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)) | ||
| print(f"Successfully revoked token {args.token_id}.") | ||
| except api.errors.NotFoundException: | ||
| raise errors.CliError("Token not found") | ||
|
|
||
|
|
||
| def create_token(args: argparse.Namespace) -> None: | ||
| sess = cli.setup_session(args) | ||
| try: | ||
| username = args.username or sess.username | ||
| user = bindings.get_GetUserByUsername(session=sess, username=username).user | ||
|
|
||
| if user is None or user.id is None: | ||
| raise errors.CliError(f"User '{username}' not found or does not have an ID") | ||
|
|
||
| # convert days into hours Go duration format | ||
| if args.expiration_days: | ||
| expiration_in_hours = str(24 * args.expiration_days) + "h" | ||
|
|
||
| request = bindings.v1PostAccessTokenRequest( | ||
| userId=user.id, lifespan=expiration_in_hours, 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"]}' | ||
|
|
||
| print(output_string) | ||
| except api.errors.APIException as e: | ||
| raise errors.CliError(f"Caught APIException: {str(e)}") | ||
| except api.errors.NotFoundException as e: | ||
| raise errors.CliError(f"Caught NotFoundException: {str(e)}") | ||
|
|
||
|
|
||
| def edit_token(args: argparse.Namespace) -> None: | ||
| sess = cli.setup_session(args) | ||
| try: | ||
| if args.token_id: | ||
| request = bindings.v1PatchAccessTokenRequest( | ||
| tokenId=args.token_id, | ||
| description=args.description if args.description else None, | ||
| setRevoked=False, | ||
| ) | ||
| resp = bindings.patch_PatchAccessToken(sess, body=request, tokenId=args.token_id) | ||
| print(json.dumps(resp.to_json(), indent=2)) | ||
| print(f"Successfully updated token with ID: {args.token_id}.") | ||
| except api.errors.APIException as e: | ||
| raise errors.CliError(f"Caught APIException: {str(e)}") | ||
| except api.errors.NotFoundException: | ||
| raise errors.CliError("Token not found") | ||
|
|
||
|
|
||
| def login_with_token(args: argparse.Namespace) -> None: | ||
ShreyaLnuHpe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| try: | ||
| 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() | ||
| username = user_data.get("user").get("username") | ||
ShreyaLnuHpe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| token_store = authentication.TokenStore(args.master) | ||
| token_store.set_token(username, args.token) | ||
| token_store.set_active(username) | ||
| print(f"Authenticated as {username}.") | ||
| except api.errors.APIException as e: | ||
| raise errors.CliError(f"Caught APIException: {str(e)}") | ||
| except api.errors.UnauthenticatedException as e: | ||
| raise errors.CliError(f"Caught UnauthenticatedException: {str(e)}") | ||
| except api.errors.NotFoundException as e: | ||
| raise errors.CliError(f"Caught NotFoundException: {str(e)}") | ||
|
|
||
|
|
||
| # 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 accessible to users", [ | ||
| cli.Arg("username", type=str, nargs=argparse.OPTIONAL, | ||
| help="list access tokens for the given username", default=None), | ||
| cli.Arg("--show-inactive", action="store_true", default=None, | ||
| help="list all access tokens accessible to the current user"), | ||
| 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-days", "-e", type=int, | ||
| help="specify the token expiration in days. '-e 2' sets it to 2 days."), | ||
| 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.