Skip to content

Commit 37b5446

Browse files
authored
Merge pull request #79 from britive/develop
v1.4.0rc1
2 parents bb4b3bf + 72552ac commit 37b5446

7 files changed

Lines changed: 106 additions & 26 deletions

File tree

CHANGELOG.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,26 @@
11
# Change Log
22

3-
All changes to the package starting with v0.3.1 will be logged here.
3+
* All changes to the package starting with v0.3.1 will be logged here.
4+
* As of v1.4.0 release candidates will be published in an effort to get new features out faster while still allowing time for full QA testing before moving the release candidate to a full release.
5+
6+
## v1.4.0rc1 [2023-05-09]
7+
#### What's New
8+
* Command `request approve`
9+
* Command `request reject`
10+
* Command `ls approvals`
11+
12+
#### Enhancements
13+
* None
14+
15+
#### Bug Fixes
16+
* Resolved issue with profile alias names which included uppercase and special characters.
17+
* Resolved an issue with `checkout --mode browser-*` that was not actually launching the browser.
18+
19+
#### Dependencies
20+
* `britive>=2.19.0`
21+
22+
#### Other
23+
* None
424

525
## v1.3.0 [2023-03-28]
626
#### What's New

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
britive>=2.18.0
1+
britive>=2.19.0
22
certifi>=2022.12.7
33
charset-normalizer==2.1.0
44
click==8.1.3

setup.cfg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = pybritive
3-
version = 1.3.0
3+
version = 1.4.0rc1
44
author = Britive Inc.
55
author_email = support@britive.com
66
description = A pure Python CLI for Britive
@@ -26,7 +26,7 @@ install_requires =
2626
toml
2727
cryptography~=39.0.1
2828
python-dateutil
29-
britive>=2.18.0
29+
britive>=2.19.0
3030
jmespath
3131
pyjwt
3232

src/pybritive/britive_cli.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,26 @@ def list_secrets(self):
205205
self.login()
206206
self.print(self.b.my_secrets.list(), ignore_silent=True)
207207

208+
def list_approvals(self):
209+
self.login()
210+
approvals = []
211+
for approval in self.b.my_access.list_approvals():
212+
approval.pop('resource', None)
213+
approval.pop('consumer', None)
214+
approval.pop('timeToApprove', None)
215+
approval.pop('validFor', None)
216+
approval.pop('action', None)
217+
approval.pop('approvers', None)
218+
approval.pop('expirationTimeApproval', None)
219+
approval.pop('updatedAt', None)
220+
approval.pop('actionBy', None)
221+
approval.pop('validForInDays', None)
222+
approvals.append(approval)
223+
224+
approvals = sorted(approvals, key=lambda x: x['createdAt'])
225+
approvals.reverse()
226+
self.print(approvals, ignore_silent=True)
227+
208228
def list_profiles(self, checked_out: bool = False):
209229
self.login()
210230
self._set_available_profiles()
@@ -415,7 +435,7 @@ def _should_check_force_renew(app, force_renew, console):
415435
return app in ['AWS', 'AWS Standalone'] and force_renew and not console
416436

417437
def _split_profile_into_parts(self, profile):
418-
profile_real = self.config.profile_aliases.get(profile, profile)
438+
profile_real = self.config.profile_aliases.get(profile.lower(), profile)
419439
parts = profile_split(profile_real)
420440
if len(parts) == 2: # handle shortcut for profiles where the app and environment name are the same
421441
parts = [parts[0], parts[0], parts[1]]
@@ -438,7 +458,7 @@ def checkout(self, alias, blocktime, console, justification, mode, maxpolltime,
438458

439459
# these 2 modes implicitly say that console access should be checked out without having to provide
440460
# the --console flag
441-
if mode in ['browser', 'console']:
461+
if mode == 'console' or mode.startswith('browser'):
442462
console = True
443463

444464
self._validate_justification(justification)
@@ -1003,4 +1023,10 @@ def aws_console(profile, duration, browser):
10031023
browser = webbrowser.get(using=browser)
10041024
browser.open(console_url)
10051025

1026+
def request_disposition(self, request_id, decision):
1027+
self.login()
10061028

1029+
if decision == 'approve':
1030+
self.b.my_access.approve_request(request_id=request_id)
1031+
if decision == 'reject':
1032+
self.b.my_access.reject_request(request_id=request_id)

src/pybritive/commands/ls.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,9 @@ def secrets(ctx, output_format, tenant, token, silent, passphrase, federation_pr
4141
ctx.obj.britive.list_secrets()
4242

4343

44-
44+
@ls.command()
45+
@build_britive
46+
@britive_options(names='format,tenant,token,silent,passphrase,federation_provider')
47+
def approvals(ctx, output_format, tenant, token, silent, passphrase, federation_provider):
48+
"""List approvals for the currently authenticated identity."""
49+
ctx.obj.britive.list_approvals()

src/pybritive/commands/request.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,34 @@ def withdraw(ctx, tenant, token, silent, passphrase, federation_provider, profil
4747
ctx.obj.britive.request_withdraw(
4848
profile=profile
4949
)
50+
51+
52+
@request.command()
53+
@build_britive
54+
@britive_options(names='tenant,token,silent,passphrase,federation_provider')
55+
@click.argument('request-id')
56+
def approve(ctx, tenant, token, silent, passphrase, federation_provider, request_id):
57+
"""Approve a request to checkout a profile.
58+
59+
This command takes 1 required argument `request-id`. Find the `request-id` via `ls approvals`.
60+
"""
61+
62+
ctx.obj.britive.request_disposition(
63+
request_id=request_id,
64+
decision='approve'
65+
)
66+
67+
@request.command()
68+
@build_britive
69+
@britive_options(names='tenant,token,silent,passphrase,federation_provider')
70+
@click.argument('request-id')
71+
def reject(ctx, tenant, token, silent, passphrase, federation_provider, request_id):
72+
"""Reject a request to checkout a profile.
73+
74+
This command takes 1 required argument `request-id`. Find the `request-id` via `ls approvals`.
75+
"""
76+
77+
ctx.obj.britive.request_disposition(
78+
request_id=request_id,
79+
decision='reject'
80+
)

src/pybritive/helpers/cloud_credential_printer.py

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,43 +31,41 @@ def __init__(self, app_type, console, mode, profile, silent, credentials, cli):
3131
self.console = console
3232
mode = mode or 'json' # set a default if nothing is provided via flag --mode/-m
3333
helper = mode.split('-')
34-
env_prefix = helper[1] if 1 < len(helper) else None
3534
self.mode = helper[0]
35+
self.mode_modifier = safe_list_get(mode.split('-', maxsplit=1), 1, None)
3636
self.credentials = credentials
37-
self.on_windows = True if platform.system().lower() == 'windows' else False
38-
if env_prefix:
39-
self.env_command = env_options[env_prefix]
40-
else:
41-
self.env_command = env_options['wincmd'] if self.on_windows else env_options['nix']
37+
if self.mode == 'env':
38+
if self.mode_modifier:
39+
self.env_command = env_options[self.mode_modifier]
40+
else:
41+
self.on_windows = True if platform.system().lower() == 'windows' else False
42+
self.env_command = env_options['wincmd'] if self.on_windows else env_options['nix']
4243

4344
def print(self):
4445
if self.console:
4546
self.print_console()
4647
return
47-
mode_prefix = self.mode.split('-')[0]
48-
if mode_prefix == 'text':
48+
if self.mode == 'text':
4949
self.print_text()
50-
if mode_prefix == 'json':
50+
if self.mode == 'json':
5151
self.print_json()
52-
if mode_prefix == 'env':
52+
if self.mode == 'env':
5353
self.print_env()
54-
if mode_prefix == 'integrate':
54+
if self.mode == 'integrate':
5555
self.print_integrate()
56-
if mode_prefix == 'azlogin':
56+
if self.mode == 'azlogin':
5757
self.print_azlogin()
58-
if mode_prefix == 'awscredentialprocess':
58+
if self.mode == 'awscredentialprocess':
5959
self.print_awscredentialprocess()
60-
if mode_prefix == 'azps':
60+
if self.mode == 'azps':
6161
self.print_azps()
62-
if mode_prefix == 'gcloudauth':
62+
if self.mode == 'gcloudauth':
6363
self.print_gcloudauth()
6464

6565
def print_console(self):
6666
url = self.credentials.get('url', self.credentials)
67-
68-
if self.mode.startswith('browser'):
69-
browser_type = safe_list_get(self.mode.split('-', maxsplit=1), 1, None)
70-
webbrowser.get(browser_type).open(url)
67+
if self.mode == 'browser':
68+
webbrowser.get(self.mode_modifier).open(url)
7169
else:
7270
self.cli.print(url, ignore_silent=True)
7371

0 commit comments

Comments
 (0)