csspin-python 2.0.0__py3-none-any.whl → 2.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- csspin_python/python.py +93 -14
- csspin_python/python_schema.yaml +28 -0
- {csspin_python-2.0.0.dist-info → csspin_python-2.1.0.dist-info}/METADATA +14 -14
- {csspin_python-2.0.0.dist-info → csspin_python-2.1.0.dist-info}/RECORD +7 -9
- csspin_python/aws_auth.py +0 -195
- csspin_python/aws_auth_schema.yaml +0 -38
- {csspin_python-2.0.0.dist-info → csspin_python-2.1.0.dist-info}/WHEEL +0 -0
- {csspin_python-2.0.0.dist-info → csspin_python-2.1.0.dist-info}/licenses/LICENSE +0 -0
- {csspin_python-2.0.0.dist-info → csspin_python-2.1.0.dist-info}/top_level.txt +0 -0
csspin_python/python.py
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
# See the License for the specific language governing permissions and
|
|
16
16
|
# limitations under the License.
|
|
17
17
|
#
|
|
18
|
-
# pylint: disable=too-few-public-methods,missing-class-docstring
|
|
18
|
+
# pylint: disable=too-few-public-methods,missing-class-docstring,too-many-lines
|
|
19
19
|
|
|
20
20
|
"""``python``
|
|
21
21
|
==========
|
|
@@ -148,6 +148,13 @@ defaults = config(
|
|
|
148
148
|
install=True,
|
|
149
149
|
extras=[],
|
|
150
150
|
),
|
|
151
|
+
aws_auth=config(
|
|
152
|
+
enabled=False,
|
|
153
|
+
memo="{spin.spin_dir}/aws_auth.memo",
|
|
154
|
+
key_duration=3600 * 10, # 10 hours
|
|
155
|
+
static_oidc=False,
|
|
156
|
+
index="16.0/simple",
|
|
157
|
+
),
|
|
151
158
|
index_url="https://pypi.org/simple",
|
|
152
159
|
requires=config(
|
|
153
160
|
python=["build", "wheel"],
|
|
@@ -337,6 +344,9 @@ def configure(cfg):
|
|
|
337
344
|
if exists(cfg.python.python):
|
|
338
345
|
cfg.python.site_packages = get_site_packages(interpreter=cfg.python.python)
|
|
339
346
|
|
|
347
|
+
if cfg.python.aws_auth.enabled:
|
|
348
|
+
_check_aws_token_validity(cfg)
|
|
349
|
+
|
|
340
350
|
|
|
341
351
|
def init(cfg):
|
|
342
352
|
"""Initialize the python plugin"""
|
|
@@ -888,13 +898,7 @@ def venv_provision(cfg): # pylint: disable=too-many-branches,missing-function-d
|
|
|
888
898
|
# This sets PATH to the venv
|
|
889
899
|
init(cfg)
|
|
890
900
|
|
|
891
|
-
|
|
892
|
-
if sys.platform == "win32":
|
|
893
|
-
pipconf = cfg.python.venv / "pip.ini"
|
|
894
|
-
else:
|
|
895
|
-
pipconf = cfg.python.venv / "pip.conf"
|
|
896
|
-
|
|
897
|
-
_create_pipconf(cfg, pipconf)
|
|
901
|
+
_configure_pipconf(cfg)
|
|
898
902
|
|
|
899
903
|
# Establish the prerequisites
|
|
900
904
|
if fresh_env:
|
|
@@ -945,7 +949,9 @@ def cleanup(cfg: ConfigTree) -> None:
|
|
|
945
949
|
f"'{provisioner.__class__.__name__}' failed: {err}"
|
|
946
950
|
)
|
|
947
951
|
memo.clear()
|
|
952
|
+
|
|
948
953
|
rmtree(cfg.python.provisioner_memo)
|
|
954
|
+
rmtree(cfg.python.aws_auth.memo)
|
|
949
955
|
for path in cfg.python.build_wheels:
|
|
950
956
|
current_path = Path(interpolate1(path))
|
|
951
957
|
rmtree(current_path / "build")
|
|
@@ -955,16 +961,89 @@ def cleanup(cfg: ConfigTree) -> None:
|
|
|
955
961
|
rmtree(current_path / filename)
|
|
956
962
|
|
|
957
963
|
|
|
958
|
-
def
|
|
959
|
-
|
|
960
|
-
|
|
964
|
+
def _get_pipconf(cfg):
|
|
965
|
+
"""Retrieve the pipconf configuration file path."""
|
|
966
|
+
if sys.platform == "win32":
|
|
967
|
+
pipconf = interpolate1(Path(cfg.python.venv)) / "pip.ini"
|
|
968
|
+
else:
|
|
969
|
+
pipconf = interpolate1(Path(cfg.python.venv)) / "pip.conf"
|
|
970
|
+
|
|
971
|
+
return pipconf
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def _configure_pipconf(cfg, update=False):
|
|
975
|
+
"""Configure the pip configuration file"""
|
|
961
976
|
config_parser = configparser.ConfigParser()
|
|
962
977
|
config_parser.read_string(cfg.python.pipconf)
|
|
963
978
|
if not config_parser.has_section("global"):
|
|
964
979
|
config_parser.add_section("global")
|
|
965
|
-
if not (
|
|
980
|
+
if update or not (
|
|
966
981
|
"index_url" in config_parser["global"] or "index-url" in config_parser["global"]
|
|
967
982
|
):
|
|
968
|
-
config_parser["global"]["index_url"] = cfg.python.index_url
|
|
969
|
-
with open(
|
|
983
|
+
config_parser["global"]["index_url"] = interpolate1(cfg.python.index_url)
|
|
984
|
+
with open(_get_pipconf(cfg), mode="w", encoding="utf-8") as fd:
|
|
970
985
|
config_parser.write(fd)
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
def _obfuscate_index_url(index_url):
|
|
989
|
+
"""Add the CodeArtifact token to the secrets."""
|
|
990
|
+
|
|
991
|
+
from csspin import secrets
|
|
992
|
+
|
|
993
|
+
secrets.add(index_url.split(":")[2].split("@")[0]) # Codeartifact token
|
|
994
|
+
|
|
995
|
+
|
|
996
|
+
def _check_aws_token_validity(cfg):
|
|
997
|
+
"""
|
|
998
|
+
If csspin-python[aws_auth] is installed, we can use csaccess to get the
|
|
999
|
+
CodeArtifact authentication token.
|
|
1000
|
+
"""
|
|
1001
|
+
|
|
1002
|
+
try:
|
|
1003
|
+
from csaccess import get_ca_pypi_url_programmatic
|
|
1004
|
+
except ImportError:
|
|
1005
|
+
die(
|
|
1006
|
+
"The 'aws_auth' feature requires the 'aws_auth' extra being"
|
|
1007
|
+
" installed (e.g. via csspin-python[aws_auth] in spinfile.yaml)."
|
|
1008
|
+
)
|
|
1009
|
+
|
|
1010
|
+
import time
|
|
1011
|
+
|
|
1012
|
+
current_time = int(time.time())
|
|
1013
|
+
timestamp_key = "aws_auth_timestamp"
|
|
1014
|
+
|
|
1015
|
+
with memoizer(cfg.python.aws_auth.memo) as memo:
|
|
1016
|
+
for item in memo.items():
|
|
1017
|
+
if isinstance(item, str) and item.startswith(f"{timestamp_key}:"):
|
|
1018
|
+
last_time = int(item.split(":", 1)[1])
|
|
1019
|
+
if current_time - last_time < cfg.python.aws_auth.key_duration:
|
|
1020
|
+
pipconf = _get_pipconf(cfg)
|
|
1021
|
+
config_parser = configparser.ConfigParser()
|
|
1022
|
+
config_parser.read(pipconf)
|
|
1023
|
+
info(f"Using existing index URL from {pipconf}.")
|
|
1024
|
+
|
|
1025
|
+
if index_url := (
|
|
1026
|
+
config_parser["global"].get("index_url")
|
|
1027
|
+
or config_parser["global"].get("index-url")
|
|
1028
|
+
):
|
|
1029
|
+
cfg.python.index_url = index_url
|
|
1030
|
+
_obfuscate_index_url(index_url)
|
|
1031
|
+
break
|
|
1032
|
+
memo.items().remove(item)
|
|
1033
|
+
else:
|
|
1034
|
+
info("Updating Codeartifact token.")
|
|
1035
|
+
from urllib.parse import urljoin
|
|
1036
|
+
|
|
1037
|
+
index_url = urljoin(
|
|
1038
|
+
get_ca_pypi_url_programmatic(
|
|
1039
|
+
static_oidc=cfg.python.aws_auth.static_oidc
|
|
1040
|
+
)
|
|
1041
|
+
+ "/",
|
|
1042
|
+
cfg.python.aws_auth.index,
|
|
1043
|
+
)
|
|
1044
|
+
cfg.python.index_url = index_url
|
|
1045
|
+
_obfuscate_index_url(index_url)
|
|
1046
|
+
|
|
1047
|
+
if exists(cfg.python.venv):
|
|
1048
|
+
_configure_pipconf(cfg, update=True)
|
|
1049
|
+
memo.add(f"{timestamp_key}:{current_time}")
|
csspin_python/python_schema.yaml
CHANGED
|
@@ -129,3 +129,31 @@ python:
|
|
|
129
129
|
help: |
|
|
130
130
|
A list of packages that will be built along with the current
|
|
131
131
|
project when executing python:wheel.
|
|
132
|
+
aws_auth:
|
|
133
|
+
type: object
|
|
134
|
+
help: Configuration for the 'aws_auth' extra.
|
|
135
|
+
properties:
|
|
136
|
+
enabled:
|
|
137
|
+
type: bool
|
|
138
|
+
help: |
|
|
139
|
+
Whether to enable AWS authentication for retrieving
|
|
140
|
+
packages from CodeArtifact.
|
|
141
|
+
memo:
|
|
142
|
+
type: path
|
|
143
|
+
help: |
|
|
144
|
+
Path to the memoized information regarding the aws_auth
|
|
145
|
+
extra.
|
|
146
|
+
key_duration:
|
|
147
|
+
type: int
|
|
148
|
+
help: |
|
|
149
|
+
Time in seconds defining how long the plugin should
|
|
150
|
+
consider the authentication token as valid before
|
|
151
|
+
issuing a new one.
|
|
152
|
+
static_oidc:
|
|
153
|
+
type: bool
|
|
154
|
+
help: |
|
|
155
|
+
Whether to static OIDC when authenticating with AWS
|
|
156
|
+
CodeArtifact.
|
|
157
|
+
index:
|
|
158
|
+
type: str
|
|
159
|
+
help: The Codeartifact repository index (e.g. "16.0/simple").
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
|
-
Name:
|
|
3
|
-
Version: 2.
|
|
2
|
+
Name: csspin-python
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Plugin-package for csspin providing Python related plugins
|
|
5
5
|
Author-email: CONTACT Software GmbH <info@contact-software.com>
|
|
6
6
|
Maintainer-email: Waleri Enns <waleri.enns@contact-software.com>, Benjamin Thomas Schwertfeger <benjaminthomas.schwertfeger@contact-software.com>, Fabian Hafer <fabian.hafer@contact-software.com>
|
|
@@ -22,19 +22,16 @@ Description-Content-Type: text/x-rst
|
|
|
22
22
|
License-File: LICENSE
|
|
23
23
|
Requires-Dist: virtualenv
|
|
24
24
|
Provides-Extra: aws-auth
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
Requires-Dist: requests; extra == "aws-auth"
|
|
25
|
+
Requires-Dist: csaccess>=0.1.0; extra == "aws-auth"
|
|
27
26
|
Dynamic: license-file
|
|
28
27
|
|
|
29
28
|
|Latest Version| |Python| |License|
|
|
30
29
|
|
|
31
|
-
`
|
|
30
|
+
`csspin-python` is maintained and published by `CONTACT Software GmbH`_ and
|
|
32
31
|
serves Python-based plugins for the `csspin`_ task runner.
|
|
33
32
|
|
|
34
33
|
The following plugins are available:
|
|
35
34
|
|
|
36
|
-
- `csspin_python.aws_auth`: A plugin for authenticating to CONTACT Elements
|
|
37
|
-
instances hosted on AWS.
|
|
38
35
|
- `csspin_python.behave`: A plugin for running tests using Behave.
|
|
39
36
|
- `csspin_python.debugpy`: A plugin for debugging Python code using `debugpy`_.
|
|
40
37
|
- `csspin_python.devpi`: A plugin for simplified usage of `devpi`_.
|
|
@@ -46,6 +43,9 @@ The following plugins are available:
|
|
|
46
43
|
complexity.
|
|
47
44
|
- `csspin_python.sphinx`: A plugin for building Sphinx documentation.
|
|
48
45
|
|
|
46
|
+
The package provides an ``aws_auth`` extra, that, if enabled, can
|
|
47
|
+
authenticate to `CONTACT Software GmbH`_'s AWS Codeartifact.
|
|
48
|
+
|
|
49
49
|
Prerequisites
|
|
50
50
|
-------------
|
|
51
51
|
|
|
@@ -56,10 +56,10 @@ Python package manager, e.g.:
|
|
|
56
56
|
|
|
57
57
|
python -m pip install csspin
|
|
58
58
|
|
|
59
|
-
Using
|
|
59
|
+
Using csspin-python
|
|
60
60
|
-------------------
|
|
61
61
|
|
|
62
|
-
The `
|
|
62
|
+
The `csspin-python` package and its plugins can be installed by defining those
|
|
63
63
|
within the `spinfile.yaml` configuration file of your project.
|
|
64
64
|
|
|
65
65
|
.. code-block:: yaml
|
|
@@ -70,7 +70,7 @@ within the `spinfile.yaml` configuration file of your project.
|
|
|
70
70
|
# To develop plugins comfortably, install the packages editable as
|
|
71
71
|
# follows and add the relevant plugins to the list 'plugins' below
|
|
72
72
|
plugin_packages:
|
|
73
|
-
-
|
|
73
|
+
- csspin-python
|
|
74
74
|
|
|
75
75
|
# The list of plugins to be used for this project.
|
|
76
76
|
plugins:
|
|
@@ -93,13 +93,13 @@ environment and install the required dependencies. After that, you can run
|
|
|
93
93
|
tests using ``spin pytest`` and do other great things.
|
|
94
94
|
|
|
95
95
|
.. _`CONTACT Software GmbH`: https://contact-software.com
|
|
96
|
-
.. |Python| image:: https://img.shields.io/pypi/pyversions/
|
|
97
|
-
:target: https://pypi.python.org/pypi/
|
|
96
|
+
.. |Python| image:: https://img.shields.io/pypi/pyversions/csspin-python.svg?style=flat
|
|
97
|
+
:target: https://pypi.python.org/pypi/csspin-python/
|
|
98
98
|
:alt: Supported Python Versions
|
|
99
|
-
.. |Latest Version| image:: http://img.shields.io/pypi/v/
|
|
99
|
+
.. |Latest Version| image:: http://img.shields.io/pypi/v/csspin-python.svg?style=flat
|
|
100
100
|
:target: https://pypi.python.org/pypi/csspin/
|
|
101
101
|
:alt: Latest Package Version
|
|
102
|
-
.. |License| image:: http://img.shields.io/pypi/l/
|
|
102
|
+
.. |License| image:: http://img.shields.io/pypi/l/csspin-python.svg?style=flat
|
|
103
103
|
:target: https://www.apache.org/licenses/LICENSE-2.0.txt
|
|
104
104
|
:alt: License
|
|
105
105
|
.. _`csspin`: https://pypi.org/project/csspin
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
csspin_python/aws_auth.py,sha256=cQXz2AWW-TM-KAv8ZpXOSntTRRzvRL21f2NwMpOf3PA,7232
|
|
2
|
-
csspin_python/aws_auth_schema.yaml,sha256=GgLhnwn4savhmLUcfL2eVPyNhWkiKA5tKz6y1tU1C6w,1353
|
|
3
1
|
csspin_python/behave.py,sha256=iA5t-vwMDhmWoo-FHaeZ6JyIzj6k4r2WLyYa2AjXyp4,4815
|
|
4
2
|
csspin_python/behave_schema.yaml,sha256=8qoOCK-uTmwgRRW29urgK0X_kgn0zO0X34v89bvii2w,1241
|
|
5
3
|
csspin_python/debugpy.py,sha256=v0ZZopv5TNoSaFf2kiePsw9OmhBpjfOBFh0u71jTcnQ,962
|
|
@@ -10,12 +8,12 @@ csspin_python/playwright.py,sha256=nYuPjuTfGx_6QPYsUpr-eAZARyXX_hZF8D4AxeI2u1s,4
|
|
|
10
8
|
csspin_python/playwright_schema.yaml,sha256=WFMok7dB7G6L8f8y_2_RKHjGe4ww1iUUS4tqCoUI1FE,1054
|
|
11
9
|
csspin_python/pytest.py,sha256=EwxPynyPWS72NTtIah1jUGVa7fJYY8I2_NQz0y0U7Os,2978
|
|
12
10
|
csspin_python/pytest_schema.yaml,sha256=1bF8hNsJfV-LHUwGBBJ3GnQOZJiIQkG81DCBma2MalU,809
|
|
13
|
-
csspin_python/python.py,sha256=
|
|
14
|
-
csspin_python/python_schema.yaml,sha256=
|
|
11
|
+
csspin_python/python.py,sha256=du1sFA6Uh8Wp5V6sN3_iSsQNEz-OZBAJG-FdgCyvsjM,33114
|
|
12
|
+
csspin_python/python_schema.yaml,sha256=F_PMK8D3KBvXK945b6-oRDoaxuDgxkBGqVPAJ-eFmv0,5970
|
|
15
13
|
csspin_python/radon.py,sha256=OSV0vTz7SMMHC82jUvWsBq6vtWolOjdiZ2bBdxplVJ4,1744
|
|
16
14
|
csspin_python/radon_schema.yaml,sha256=rlRzXw5z4XbjOVznRiUxWGP4E9hx1Jm-gGw1iQiYzE0,548
|
|
17
|
-
csspin_python-2.
|
|
18
|
-
csspin_python-2.
|
|
19
|
-
csspin_python-2.
|
|
20
|
-
csspin_python-2.
|
|
21
|
-
csspin_python-2.
|
|
15
|
+
csspin_python-2.1.0.dist-info/licenses/LICENSE,sha256=4MAecetnRTQw5DlHtiikDSzKWO1xVLwzM5_DsPMYlnE,10172
|
|
16
|
+
csspin_python-2.1.0.dist-info/METADATA,sha256=-cWwMdEo8x0I1_vcEAjiCO5DC79Y-ymGXro6Qig7Q4s,4174
|
|
17
|
+
csspin_python-2.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
+
csspin_python-2.1.0.dist-info/top_level.txt,sha256=QSeglMEGbFu1z4L6MCQYwo01NgL0KojWvC4rzgMQ8gU,14
|
|
19
|
+
csspin_python-2.1.0.dist-info/RECORD,,
|
csspin_python/aws_auth.py
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
# -*- mode: python; coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2025 CONTACT Software GmbH
|
|
4
|
-
# https://www.contact-software.com/
|
|
5
|
-
#
|
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
-
# you may not use this file except in compliance with the License.
|
|
8
|
-
# You may obtain a copy of the License at
|
|
9
|
-
#
|
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
-
#
|
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
-
# See the License for the specific language governing permissions and
|
|
16
|
-
# limitations under the License.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"""Module implementing the aws_auth plugin for spin"""
|
|
20
|
-
|
|
21
|
-
import configparser
|
|
22
|
-
import os
|
|
23
|
-
|
|
24
|
-
from csspin import Path, config, debug, die, exists, info, interpolate1
|
|
25
|
-
|
|
26
|
-
defaults = config(
|
|
27
|
-
aws_role_arn="arn:aws:iam::373369985286:role/cs-central1-codeartifact-ecr-read-role",
|
|
28
|
-
aws_region="eu-central-1",
|
|
29
|
-
aws_role_session_name="CodeArtifactSession",
|
|
30
|
-
aws_codeartifact_domain="contact",
|
|
31
|
-
aws_key_duration=3600,
|
|
32
|
-
keycloak_url="https://login.contact-cloud.com/realms/contact/protocol/openid-connect/token",
|
|
33
|
-
client_id="central1-auth-oidc-read",
|
|
34
|
-
requires=config(
|
|
35
|
-
spin=[
|
|
36
|
-
"csspin_python.python",
|
|
37
|
-
],
|
|
38
|
-
),
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def configure(cfg): # pylint: disable=too-many-statements
|
|
43
|
-
"""Configure the plugin and apply changes to the configuration tree"""
|
|
44
|
-
# Could be useful in CI e.g. when you want to build docs
|
|
45
|
-
# and need to include this plugin in spinfile
|
|
46
|
-
# without using it's functionality
|
|
47
|
-
if os.environ.get("AWS_AUTH_DISABLE"):
|
|
48
|
-
info("AWS_AUTH_DISABLE is set, ignoring aws_auth plugin")
|
|
49
|
-
return
|
|
50
|
-
|
|
51
|
-
from sys import platform
|
|
52
|
-
|
|
53
|
-
try:
|
|
54
|
-
import boto3
|
|
55
|
-
import requests
|
|
56
|
-
from botocore.exceptions import ClientError
|
|
57
|
-
except ImportError:
|
|
58
|
-
die(
|
|
59
|
-
"Failed to import required modules. Please install them by setting:"
|
|
60
|
-
"\n\tplugin_packages:\n\t\t- csspin_python[aws_auth]\n"
|
|
61
|
-
"in your project's spinfile.yaml"
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
cfg.aws_auth.client_secret = os.environ.get("KEYCLOAK_CLIENT_SECRET")
|
|
65
|
-
if not cfg.aws_auth.client_secret:
|
|
66
|
-
die(
|
|
67
|
-
"Neither aws_auth.client_secret config"
|
|
68
|
-
"entry nor KEYCLOAK_CLIENT_SECRET environment variable was found."
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
def get_keycloak_access_token(keycloak_url, client_id, client_secret):
|
|
72
|
-
"""
|
|
73
|
-
Obtain the Keycloak access token using client credentials.
|
|
74
|
-
"""
|
|
75
|
-
debug("Requesting Keycloak access token...")
|
|
76
|
-
payload = {
|
|
77
|
-
"grant_type": "client_credentials",
|
|
78
|
-
"client_id": client_id,
|
|
79
|
-
"client_secret": client_secret,
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
try:
|
|
83
|
-
response = requests.post(keycloak_url, data=payload, timeout=15)
|
|
84
|
-
response.raise_for_status()
|
|
85
|
-
data = response.json()
|
|
86
|
-
access_token = data.get("access_token")
|
|
87
|
-
if not access_token:
|
|
88
|
-
raise ValueError("Response doesn't contain access_token")
|
|
89
|
-
except (ValueError, requests.exceptions.RequestException) as e:
|
|
90
|
-
die(f"Failed to fetch Keycloak access token: {e}")
|
|
91
|
-
|
|
92
|
-
return access_token
|
|
93
|
-
|
|
94
|
-
def assume_aws_role_with_web_identity(
|
|
95
|
-
keycloak_access_token,
|
|
96
|
-
role_arn,
|
|
97
|
-
role_session_name,
|
|
98
|
-
region,
|
|
99
|
-
key_duration_seconds,
|
|
100
|
-
):
|
|
101
|
-
"""
|
|
102
|
-
Request AWS STS credentials using the Keycloak token as a web identity.
|
|
103
|
-
"""
|
|
104
|
-
debug("Requesting AWS STS credentials...")
|
|
105
|
-
sts_client = boto3.client("sts", region_name=region)
|
|
106
|
-
try:
|
|
107
|
-
sts_response = sts_client.assume_role_with_web_identity(
|
|
108
|
-
RoleArn=role_arn,
|
|
109
|
-
RoleSessionName=role_session_name,
|
|
110
|
-
WebIdentityToken=keycloak_access_token,
|
|
111
|
-
DurationSeconds=key_duration_seconds,
|
|
112
|
-
)
|
|
113
|
-
credentials = sts_response.get("Credentials", {})
|
|
114
|
-
if not (
|
|
115
|
-
credentials.get("AccessKeyId")
|
|
116
|
-
and credentials.get("SecretAccessKey")
|
|
117
|
-
and credentials.get("SessionToken")
|
|
118
|
-
):
|
|
119
|
-
raise ValueError("Incomplete AWS credentials received")
|
|
120
|
-
except (ValueError, ClientError) as e:
|
|
121
|
-
die(f"Failed to assume AWS role with web identity: {e}")
|
|
122
|
-
|
|
123
|
-
return credentials
|
|
124
|
-
|
|
125
|
-
def get_codeartifact_auth_token(credentials, domain, region):
|
|
126
|
-
"""
|
|
127
|
-
Retrieve the AWS CodeArtifact authentication token using temporary AWS credentials.
|
|
128
|
-
"""
|
|
129
|
-
debug("Requesting CodeArtifact authentication token...")
|
|
130
|
-
codeartifact_client = boto3.client(
|
|
131
|
-
"codeartifact",
|
|
132
|
-
region_name=region,
|
|
133
|
-
aws_access_key_id=credentials.get("AccessKeyId"),
|
|
134
|
-
aws_secret_access_key=credentials.get("SecretAccessKey"),
|
|
135
|
-
aws_session_token=credentials.get("SessionToken"),
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
try:
|
|
139
|
-
response = codeartifact_client.get_authorization_token(domain=domain)
|
|
140
|
-
auth_token = response.get("authorizationToken")
|
|
141
|
-
if not auth_token:
|
|
142
|
-
raise ValueError("Failed to retrieve CodeArtifact authentication token")
|
|
143
|
-
except (ValueError, ClientError) as e:
|
|
144
|
-
die(f"Failed to retrieve CodeArtifact authentication token: {e}")
|
|
145
|
-
|
|
146
|
-
return auth_token
|
|
147
|
-
|
|
148
|
-
keycloak_access_token = get_keycloak_access_token(
|
|
149
|
-
cfg.aws_auth.keycloak_url, cfg.aws_auth.client_id, cfg.aws_auth.client_secret
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
credentials = assume_aws_role_with_web_identity(
|
|
153
|
-
keycloak_access_token,
|
|
154
|
-
cfg.aws_auth.aws_role_arn,
|
|
155
|
-
cfg.aws_auth.aws_role_session_name,
|
|
156
|
-
cfg.aws_auth.aws_region,
|
|
157
|
-
cfg.aws_auth.aws_key_duration,
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
codeartifact_auth_token = get_codeartifact_auth_token(
|
|
161
|
-
credentials, cfg.aws_auth.aws_codeartifact_domain, cfg.aws_auth.aws_region
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
domain_owner = cfg.aws_auth.aws_role_arn.split(":")[4]
|
|
165
|
-
|
|
166
|
-
cfg.aws_auth.codeartifact_auth_token = codeartifact_auth_token
|
|
167
|
-
cfg.python.index_url = (
|
|
168
|
-
f"https://aws:{codeartifact_auth_token}@"
|
|
169
|
-
f"{cfg.aws_auth.aws_codeartifact_domain}-{domain_owner}"
|
|
170
|
-
f".d.codeartifact.{cfg.aws_auth.aws_region}.amazonaws.com/pypi/elements/simple/"
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
pipconf = interpolate1(cfg.python.venv) / Path(
|
|
174
|
-
"pip.ini" if platform == "win32" else "pip.conf"
|
|
175
|
-
)
|
|
176
|
-
if exists(pipconf):
|
|
177
|
-
# Need to update pip.conf with the new index_url
|
|
178
|
-
# for "spin run pip ..." to use the right index and
|
|
179
|
-
# not the default one
|
|
180
|
-
_update_pipconf_url(pipconf, cfg.python.index_url)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
def _update_pipconf_url(filename, url):
|
|
184
|
-
"""Upates the python.index_url in the pip.conf file with the new value"""
|
|
185
|
-
info(f"Updating python.index_url in {filename} with a fresh token...")
|
|
186
|
-
config_parser = configparser.ConfigParser()
|
|
187
|
-
config_parser.read(filename)
|
|
188
|
-
if not config_parser.has_section("global"):
|
|
189
|
-
config_parser.add_section("global")
|
|
190
|
-
option = (
|
|
191
|
-
"index-url" if config_parser.has_option("global", "index-url") else "index_url"
|
|
192
|
-
)
|
|
193
|
-
config_parser.set("global", option, url)
|
|
194
|
-
with open(filename, mode="w", encoding="utf-8") as f:
|
|
195
|
-
config_parser.write(f)
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# -*- mode: yaml; coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Schema of the aws_auth plugin for spin
|
|
4
|
-
|
|
5
|
-
aws_auth:
|
|
6
|
-
type: object
|
|
7
|
-
help: |
|
|
8
|
-
Configuration for the aws_auth plugin in spin.
|
|
9
|
-
This plugin handles authentication with AWS and retrieves
|
|
10
|
-
secret keys from AWS CodeArtifact.
|
|
11
|
-
properties:
|
|
12
|
-
aws_role_arn:
|
|
13
|
-
type: str
|
|
14
|
-
help: The ARN of the AWS IAM role to assume for authentication.
|
|
15
|
-
aws_region:
|
|
16
|
-
type: str
|
|
17
|
-
help: The AWS region where the CodeArtifact repository is located.
|
|
18
|
-
role_session_name:
|
|
19
|
-
type: str
|
|
20
|
-
help: The name for the AWS STS session when assuming a role.
|
|
21
|
-
aws_codeartifact_domain:
|
|
22
|
-
type: str
|
|
23
|
-
help: The domain name of the AWS CodeArtifact repository.
|
|
24
|
-
aws_key_duration:
|
|
25
|
-
type: int
|
|
26
|
-
help: The duration in seconds for which the temporary key will be valid.
|
|
27
|
-
keycloak_url:
|
|
28
|
-
type: str
|
|
29
|
-
help: The URL of the Keycloak authentication server.
|
|
30
|
-
client_id:
|
|
31
|
-
type: str
|
|
32
|
-
help: The client ID for authentication with Keycloak.
|
|
33
|
-
client_secret:
|
|
34
|
-
type: str
|
|
35
|
-
help: The client secret for authentication with Keycloak.
|
|
36
|
-
codeartifact_auth_token:
|
|
37
|
-
type: str
|
|
38
|
-
help: The CodeArtifact auth token.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|