Skip to content

Commit 48d32ef

Browse files
authored
Merge pull request #67 from britive/azuremi
azure et. al.
2 parents 37a69f4 + e939b5e commit 48d32ef

17 files changed

Lines changed: 149 additions & 26 deletions

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,24 @@
22

33
All changes to the package starting with v0.3.1 will be logged here.
44

5+
## v1.2.0 [2023-03-03]
6+
#### What's New
7+
* Support for Azure Managed Identities as federation providers.
8+
9+
#### Enhancements
10+
* Fall back to reduced functionality (no shell completion) when the python environment is using `click<8.0.0`.
11+
12+
#### Bug Fixes
13+
* If a justification for checkout/secrets viewing is provided, ensure it is <=255 characters.
14+
* Fix issue with extraction of OIDC token expiration time. Moved to `jwt` library to perform the token decode.
15+
16+
#### Dependencies
17+
* Switching `britive` dependency from a compatible version requirement to a `>=` requirement to capture minor updates.
18+
* `britive>=2.16.0` from britive~=2.15.1
19+
20+
#### Other
21+
* Modify the error handling and reporting process to not raise `click.ClickException` exceptions in the `safe_cli` method. Instead, raise the underlying exception so a better error message is provided.
22+
523
## v1.1.1 [2023-02-16]
624
#### What's New
725
* None

docs/index.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@ pip install $(curl -s https://api.github.com/repos/britive/python-cli/releases/l
3030
The end user is free to install the CLI into a virtual environment or in the global scope, so it is available
3131
everywhere.
3232

33+
If the `pybritive` executable is not found when attempting to invoke the program, you may need to add the location
34+
of the `pybritive` executable to your path. This location can differ by OS and whether you are using a virtualenv or
35+
not. The easiest way to see where executables get installed via `pip install ...` is to run the following command.
36+
37+
~~~
38+
echo `python3 -m site --user-base`/bin
39+
~~~
40+
41+
You will need to add this location to your path. The following command will do that but it is recommended to add
42+
this command into your `.bashrc, .zshrc, etc.` file, so it will always get executed on new terminal windows.
43+
44+
~~~
45+
export PATH=\"`python3 -m site --user-base`/bin:\$PATH\"
46+
~~~
47+
3348
## Tenant Configuration
3449

3550
Before `pybritive` can connect to a Britive tenant, it needs to know some details about that tenant.
@@ -129,6 +144,12 @@ At feature launch the following types of identity providers are supported for wo
129144
* Github Actions
130145
* AWS
131146
* Bitbucket
147+
* Azure System Assigned Managed Identities
148+
* Azure User Assigned Managed Identities
149+
150+
For more information on Azure Managed Identities reference the below link.
151+
152+
https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview
132153

133154
It is possible to source an identity token from a different OIDC provider and explicitly set it via the `--token\-T` flag.
134155
However, if you are using one of the above providers, a shortcut is provided to abstract away the complexity of sourcing these tokens.
@@ -154,6 +175,18 @@ pybritive checkout "profile" --federation-provider aws_expirationseconds # use
154175

155176
# bitbucket (note that no additional options are available for bitbucket)
156177
pybritive checkout "profile" --federation-provider bitbucket
178+
179+
# azure system assigned managed identities
180+
pybritive checkout "profile" --federation-provider azuresmi # use system assigned managed identities with the default OIDC audience
181+
pybritive checkout "profile" --federation-provider azuresmi-audience # use system assigned managed identities with a custom OIDC audience
182+
pybritive checkout "profile" --federation-provider azuresmi-audience_expirationseconds # use system assigned managed identities with a custom OIDC audience and set the Britive expiration (in seconds) of the generated token
183+
pybritive checkout "profile" --federation-provider azuresmi_expirationseconds # use system assigned managed identities with the default OIDC audience and set the Britive expiration (in seconds) of the generated token
184+
185+
# azure user assigned managed identities (note that a client id is a required field)
186+
pybritive checkout "profile" --federation-provider azuresmi-clientid # use user assigned managed identities with the default OIDC audience
187+
pybritive checkout "profile" --federation-provider azuresmi-clientid|audience # use user assigned managed identities with a custom OIDC audience
188+
pybritive checkout "profile" --federation-provider azuresmi-clientid|audience_expirationseconds # use user assigned managed identities with a custom OIDC audience and set the Britive expiration (in seconds) of the generated token
189+
pybritive checkout "profile" --federation-provider azuresmi-clientid_expirationseconds # use user assigned managed identities with the default OIDC audience and set the Britive expiration (in seconds) of the generated token
157190
~~~
158191

159192
In general the field format for `--federation-provider` is `provider-[something provider specific]_[duration in seconds]`.

mkdocs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ nav:
88
theme: readthedocs
99
repo_url: https://github.com/britive/python-cli
1010
edit_uri: ''
11-
copyright: 2022 Britive, Inc.
11+
copyright: 2023 Britive, Inc.
1212
markdown_extensions:
1313
- attr_list
1414
- mkdocs-click

requirements.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
britive~=2.15.0
1+
britive>=2.16.0
22
certifi>=2022.12.7
33
charset-normalizer==2.1.0
44
click==8.1.3
@@ -17,4 +17,5 @@ mkdocs-click==0.8.0
1717
twine~=4.0.1
1818
python-dateutil~=2.8.2
1919
boto3
20-
jmespath
20+
jmespath
21+
pyjwt

setup.cfg

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = pybritive
3-
version = 1.1.1
3+
version = 1.2.0
44
author = Britive Inc.
55
author_email = support@britive.com
66
description = A pure Python CLI for Britive
@@ -26,8 +26,9 @@ install_requires =
2626
toml
2727
cryptography~=39.0.1
2828
python-dateutil
29-
britive~=2.15.0
29+
britive>=2.16.0
3030
jmespath
31+
pyjwt
3132

3233
[options.packages.find]
3334
where = src

src/pybritive/britive_cli.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,8 @@ def checkout(self, alias, blocktime, console, justification, mode, maxpolltime,
420420
response = None
421421
self.verbose_checkout = verbose
422422

423+
self._validate_justification(justification)
424+
423425
if mode == 'awscredentialprocess':
424426
self.silent = True # the aws credential process CANNOT output anything other than the expected JSON
425427
# we need to check the credential process cache for the credentials first
@@ -524,7 +526,8 @@ def configure_global(self, default_tenant_name, output_format, backend):
524526
backend=backend
525527
)
526528

527-
def viewsecret(self, path, blocktime, justification,maxpolltime):
529+
def viewsecret(self, path, blocktime, justification, maxpolltime):
530+
self._validate_justification(justification)
528531
self.login()
529532

530533
try:
@@ -554,7 +557,8 @@ def viewsecret(self, path, blocktime, justification,maxpolltime):
554557
# and finally print the secret data
555558
self.print(value)
556559

557-
def downloadsecret(self, path, blocktime, justification,maxpolltime, file):
560+
def downloadsecret(self, path, blocktime, justification, maxpolltime, file):
561+
self._validate_justification(justification)
558562
self.login()
559563

560564
try:
@@ -617,6 +621,7 @@ def configure_update(self, section, field, value):
617621
self.config.update(section=section, field=field, value=value)
618622

619623
def request_submit(self, profile, justification):
624+
self._validate_justification(justification)
620625
self.login()
621626
parts = self._split_profile_into_parts(profile)
622627

@@ -762,6 +767,11 @@ def _convert_names_to_ids(self, profile_name: str, environment_name: str, applic
762767
'environment_id': possible_environments[0]
763768
}
764769

770+
@staticmethod
771+
def _validate_justification(justification: str):
772+
if justification and len(justification) > 255:
773+
raise ValueError('justification cannot be longer than 255 characters.')
774+
765775

766776

767777

src/pybritive/cli_interface.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ def safe_cli():
2424
cli()
2525
except Exception as e:
2626
if debug:
27-
raise click.ClickException(str(e))
27+
raise e
2828
else:
29-
raise click.ClickException(str(e)) from None
29+
raise e from None
3030

3131

3232
# this is the "main" app - it really does nothing but print the overview/help section

src/pybritive/commands/checkin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import click
22
from ..helpers.build_britive import build_britive
33
from ..options.britive_options import britive_options
4-
from ..completers.profile import profile_completer
4+
from ..helpers.profile_argument_dectorator import click_smart_profile_argument
55

66

77
@click.command()
88
@build_britive
99
@britive_options(names='tenant,token,passphrase,federation_provider')
10-
@click.argument('profile', shell_complete=profile_completer)
10+
@click_smart_profile_argument
1111
def checkin(ctx, tenant, token, passphrase, federation_provider, profile):
1212
"""Checkin a profile.
1313

src/pybritive/commands/checkout.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import click
22
from ..helpers.build_britive import build_britive
33
from ..options.britive_options import britive_options
4-
from ..completers.profile import profile_completer
4+
from ..helpers.profile_argument_dectorator import click_smart_profile_argument
55

66

77
@click.command()
88
@build_britive
99
@britive_options(names='alias,blocktime,console,justification,mode,maxpolltime,silent,force_renew,aws_credentials_file,'
1010
'gcloud_key_file,verbose,tenant,token,passphrase,federation_provider')
11-
@click.argument('profile', shell_complete=profile_completer)
11+
@click_smart_profile_argument
1212
def checkout(ctx, alias, blocktime, console, justification, mode, maxpolltime, silent, force_renew,
1313
aws_credentials_file, gcloud_key_file, verbose, tenant, token, passphrase, federation_provider, profile):
1414
"""Checkout a profile.

src/pybritive/commands/request.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import click
22
from ..helpers.build_britive import build_britive
33
from ..options.britive_options import britive_options
4-
from ..completers.profile import profile_completer
4+
from ..helpers.profile_argument_dectorator import click_smart_profile_argument
5+
56

67
@click.group()
78
def request():
@@ -12,7 +13,7 @@ def request():
1213
@request.command()
1314
@build_britive
1415
@britive_options(names='justification,tenant,token,passphrase,federation_provider')
15-
@click.argument('profile', shell_complete=profile_completer)
16+
@click_smart_profile_argument
1617
def submit(ctx, justification, tenant, token, passphrase, federation_provider, profile):
1718
"""Submit a request to checkout a profile.
1819
@@ -33,7 +34,7 @@ def submit(ctx, justification, tenant, token, passphrase, federation_provider, p
3334
@request.command()
3435
@build_britive
3536
@britive_options(names='tenant,token,passphrase,federation_provider')
36-
@click.argument('profile', shell_complete=profile_completer)
37+
@click_smart_profile_argument
3738
def withdraw(ctx, tenant, token, passphrase, federation_provider, profile):
3839
"""Withdraw a request to checkout a profile.
3940
@@ -45,4 +46,4 @@ def withdraw(ctx, tenant, token, passphrase, federation_provider, profile):
4546

4647
ctx.obj.britive.request_withdraw(
4748
profile=profile
48-
)
49+
)

0 commit comments

Comments
 (0)