Skip to content

Commit c33fd90

Browse files
Paul Hewletteccles
authored andcommitted
Application Registrations
Solution: Added application registrations and regeneration endpoint. Added appidp endpoint to get token. Selectively run individual unit or functional tests. Remove cert keyword from archivist. Authtoken is now positional (not keyword) arg to Archivist. Signed-off-by: Paul Hewlett <phewlett76@gmail.com>
1 parent dff1872 commit c33fd90

57 files changed

Lines changed: 1224 additions & 249 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ One can then use the examples code to create assets (see examples directory):
3535
# Initialize connection to Archivist - the URL for production will be different to this URL
3636
arch = Archivist(
3737
"https://app.rkvst.io",
38-
auth=authtoken,
38+
authtoken,
3939
)
4040
4141
# Create a new asset

archivist/access_policies.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# Initialize connection to Archivist
1616
arch = Archivist(
1717
"https://app.rkvst.io",
18-
auth=authtoken,
18+
authtoken,
1919
)
2020
access_policy = arch.access_policies.create(...)
2121

archivist/appidp.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""Appidp interface
2+
3+
Access to the Appidp endpoint.
4+
5+
The user is not expected to use this class directly. It is an attribute of the
6+
:class:`Archivist` class.
7+
8+
For example instantiate an Archivist instance and execute the methods of the class:
9+
10+
.. code-block:: python
11+
12+
with open(".auth_token", mode="r", encoding="utf-8") as tokenfile:
13+
authtoken = tokenfile.read().strip()
14+
15+
# Initialize connection to Archivist
16+
arch = Archivist(
17+
"https://app.rkvst.io",
18+
authtoken,
19+
)
20+
appidp = arch.appidp.token(...)
21+
22+
"""
23+
24+
import logging
25+
26+
# pylint:disable=cyclic-import # but pylint doesn't understand this feature
27+
# pylint:disable=unused-import # To prevent cyclical import errors forward referencing is used
28+
# pylint:disable=too-few-public-methods
29+
from . import archivist as type_helper
30+
31+
from .constants import (
32+
APPIDP_SUBPATH,
33+
APPIDP_LABEL,
34+
APPIDP_TOKEN,
35+
)
36+
from .dictmerge import _deepmerge
37+
38+
LOGGER = logging.getLogger(__name__)
39+
40+
41+
class AppIDP(dict):
42+
"""Appidp object"""
43+
44+
45+
class _AppIDPClient:
46+
"""AppIDP Client
47+
48+
Access to appidp entities. This class is usually
49+
accessed as an attribute of the Archivist class.
50+
51+
Args:
52+
archivist (Archivist): :class:`Archivist` instance
53+
54+
"""
55+
56+
def __init__(self, archivist: "type_helper.Archivist"):
57+
self._archivist = archivist
58+
59+
def token(self, client_id: str, client_secret: str) -> AppIDP:
60+
"""Create access token from client id and secret
61+
62+
Args:
63+
client_id (str): client id
64+
client_secret (str): client_secret
65+
66+
Returns:
67+
:class:`AppIDP` instance
68+
69+
"""
70+
return AppIDP(
71+
**self._archivist.post(
72+
f"{APPIDP_SUBPATH}/{APPIDP_LABEL}/{APPIDP_TOKEN}",
73+
{
74+
"grant_type": "client_credentials",
75+
"client_id": client_id,
76+
"client_secret": client_secret,
77+
},
78+
noheaders=True,
79+
)
80+
)

archivist/applications.py

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
"""Applications interface
2+
3+
Access to the applications endpoint.
4+
5+
The user is not expected to use this class directly. It is an attribute of the
6+
:class:`Archivist` class.
7+
8+
For example instantiate an Archivist instance and execute the methods of the class:
9+
10+
.. code-block:: python
11+
12+
with open(".auth_token", mode="r", encoding="utf-8") as tokenfile:
13+
authtoken = tokenfile.read().strip()
14+
15+
# Initialize connection to Archivist
16+
arch = Archivist(
17+
"https://app.rkvst.io",
18+
authtoken,
19+
)
20+
application = arch.applications.create(...)
21+
22+
"""
23+
24+
import logging
25+
from typing import Dict, Optional
26+
27+
# pylint:disable=unused-import # To prevent cyclical import errors forward referencing is used
28+
# pylint:disable=cyclic-import # but pylint doesn't understand this feature
29+
from . import archivist as type_helper
30+
31+
from .constants import (
32+
APPLICATIONS_SUBPATH,
33+
APPLICATIONS_LABEL,
34+
APPLICATIONS_REGENERATE,
35+
)
36+
from .dictmerge import _deepmerge
37+
38+
FIXTURE_LABEL = "applications"
39+
40+
41+
LOGGER = logging.getLogger(__name__)
42+
43+
44+
class Application(dict):
45+
"""Application object"""
46+
47+
48+
class _ApplicationsClient:
49+
"""ApplicationsClient
50+
51+
Access to applications entities using CRUD interface. This class is usually
52+
accessed as an attribute of the Archivist class.
53+
54+
Args:
55+
archivist (Archivist): :class:`Archivist` instance
56+
57+
"""
58+
59+
def __init__(self, archivist: "type_helper.Archivist"):
60+
self._archivist = archivist
61+
62+
def create(self, display_name: str, custom_claims: Dict) -> Application:
63+
"""Create application
64+
65+
Creates application with defined attributes.
66+
67+
Args:
68+
display_name (str): display name of application.
69+
custom_claims (dict): custom claims
70+
71+
Returns:
72+
:class:`Application` instance
73+
74+
"""
75+
LOGGER.debug("Create Application %s", display_name)
76+
return self.create_from_data(
77+
self.__query(
78+
display_name=display_name,
79+
custom_claims=custom_claims,
80+
),
81+
)
82+
83+
def create_from_data(self, data: Dict) -> Application:
84+
"""Create application
85+
86+
Creates application with request body from data stream.
87+
Suitable for reading data from a file using json.load or yaml.load
88+
89+
Args:
90+
data (dict): request body of application.
91+
92+
Returns:
93+
:class:`Application` instance
94+
95+
"""
96+
return Application(
97+
**self._archivist.post(
98+
f"{APPLICATIONS_SUBPATH}/{APPLICATIONS_LABEL}",
99+
data,
100+
)
101+
)
102+
103+
def read(self, identity: str) -> Application:
104+
"""Read Application
105+
106+
Reads application.
107+
108+
Args:
109+
identity (str): applications identity e.g. applications/xxxxxxxxxxxxxxxxxxxxxxx
110+
111+
Returns:
112+
:class:`Application` instance
113+
114+
"""
115+
return Application(
116+
**self._archivist.get(
117+
APPLICATIONS_SUBPATH,
118+
identity,
119+
)
120+
)
121+
122+
def update(
123+
self,
124+
identity: str,
125+
*,
126+
display_name: str = None,
127+
custom_claims: Optional[Dict] = None,
128+
) -> Application:
129+
"""Update Application
130+
131+
Update application.
132+
133+
Args:
134+
identity (str): applications identity e.g. applications/xxxxxxxxxxxxxxxxxxxxxxx
135+
display_name (str): display name of application.
136+
custom_claims (dict): custom claims
137+
138+
Returns:
139+
:class:`Application` instance
140+
141+
"""
142+
return Application(
143+
**self._archivist.patch(
144+
APPLICATIONS_SUBPATH,
145+
identity,
146+
self.__query(
147+
display_name=display_name,
148+
custom_claims=custom_claims,
149+
),
150+
)
151+
)
152+
153+
def delete(self, identity: str) -> Dict:
154+
"""Delete Application
155+
156+
Deletes application.
157+
158+
Args:
159+
identity (str): applications identity e.g. applications/xxxxxxxxxxxxxxxxxxxxxxx
160+
161+
Returns:
162+
:class:`Application` instance - empty?
163+
164+
"""
165+
return self._archivist.delete(APPLICATIONS_SUBPATH, identity)
166+
167+
def __query(
168+
self,
169+
*,
170+
display_name: Optional[str] = None,
171+
custom_claims: Optional[Dict] = None,
172+
) -> Dict:
173+
174+
query = {}
175+
176+
if display_name is not None:
177+
query["display_name"] = display_name
178+
179+
if custom_claims is not None:
180+
query["custom_claims"] = custom_claims
181+
182+
return _deepmerge(self._archivist.fixtures.get(FIXTURE_LABEL), query)
183+
184+
def list(
185+
self,
186+
*,
187+
page_size: Optional[int] = None,
188+
display_name: Optional[str] = None,
189+
):
190+
"""List applications.
191+
192+
List applications that match criteria.
193+
194+
Args:
195+
display_name (str): display name (optional)
196+
page_size (int): optional page size. (Rarely used).
197+
198+
Returns:
199+
iterable that returns :class:`Application` instances
200+
201+
"""
202+
return (
203+
Application(**a)
204+
for a in self._archivist.list(
205+
f"{APPLICATIONS_SUBPATH}/{APPLICATIONS_LABEL}",
206+
APPLICATIONS_LABEL,
207+
page_size=page_size,
208+
query=self.__query(display_name=display_name),
209+
)
210+
)
211+
212+
def regenerate(self, identity: str) -> Application:
213+
"""Regenerate secret
214+
215+
Makes an SBOM public.
216+
217+
Args:
218+
identity (str): identity of application
219+
220+
Returns:
221+
:class:`Application` instance
222+
223+
"""
224+
LOGGER.debug("Regenerate %s", identity)
225+
return Application(
226+
**self._archivist.post(
227+
f"{APPLICATIONS_SUBPATH}/{identity}",
228+
None,
229+
verb=APPLICATIONS_REGENERATE,
230+
)
231+
)

0 commit comments

Comments
 (0)