libtimed 0.6.0__tar.gz → 0.6.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: libtimed
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Summary: Library to intreact with timed webapp.
5
5
  Author: Arthur Deierlein
6
6
  Author-email: arthur.deierlein@adfinis.com
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "libtimed"
3
- version = "0.6.0"
3
+ version = "0.6.2"
4
4
  description = "Library to intreact with timed webapp."
5
5
  authors = [
6
6
  "Arthur Deierlein <arthur.deierlein@adfinis.com>",
@@ -263,8 +263,8 @@ class Activities(BaseModel):
263
263
  ]
264
264
 
265
265
  attributes = [
266
- ("from-time", datetime.now().time(), transforms.Time),
267
- ("to-time", None, transforms.Time),
266
+ ("from-time", datetime.now().time(), transforms.Time(True)),
267
+ ("to-time", None, transforms.Time(True)),
268
268
  ("transferred", False, transforms.Type(bool)),
269
269
  COMMENT,
270
270
  DATE,
@@ -4,6 +4,7 @@ import base64
4
4
  import datetime
5
5
  import http.server
6
6
  import json
7
+ import time
7
8
  import webbrowser
8
9
  from urllib.parse import urlparse
9
10
 
@@ -41,18 +42,20 @@ class OIDCHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
41
42
 
42
43
 
43
44
  class OIDCClient:
44
- def __init__(self, client_id, sso_url, sso_realm, auth_path):
45
+ def __init__(self, client_id, sso_url, sso_realm, auth_path, use_device_flow=False):
45
46
  self.client_id = client_id
46
47
  self.sso_url = sso_url
47
48
  self.sso_realm = sso_realm
48
49
  self.auth_path = auth_path
50
+ self.use_device_flow = use_device_flow
49
51
 
50
52
  def autoconfig(self):
51
53
  data = requests.get(
52
- f"{self.sso_url}/auth/realms/{self.sso_realm}/.well-known/openid-configuration"
54
+ f"{self.sso_url}/realms/{self.sso_realm}/.well-known/openid-configuration"
53
55
  ).json()
54
56
  self.authorization_endpoint = data["authorization_endpoint"]
55
57
  self.token_endpoint = data["token_endpoint"]
58
+ self.device_endpoint = data["device_authorization_endpoint"]
56
59
 
57
60
  def start_browser_flow(self):
58
61
  # construct the authorization request
@@ -74,6 +77,29 @@ class OIDCClient:
74
77
  self.code = code
75
78
  return True
76
79
 
80
+ def start_device_flow(self):
81
+ # construct the authorization request
82
+ auth_data = requests.post(self.device_endpoint, data={"client_id": self.client_id}).json()
83
+ verification_uri_complete = auth_data["verification_uri_complete"]
84
+ verification_uri = auth_data["verification_uri"]
85
+ device_code = auth_data["device_code"]
86
+ user_code = auth_data["user_code"]
87
+
88
+ # open the browser to the authorization URL
89
+ webbrowser.open_new(verification_uri_complete)
90
+ # print manual instructions
91
+ print(f"Please visit {verification_uri} and enter the code {user_code}")
92
+ time.sleep(5)
93
+
94
+ resp = {}
95
+ while "access_token" not in resp:
96
+ resp = requests.post(self.token_endpoint, data={"client_id": self.client_id, "grant_type": "urn:ietf:params:oauth:grant-type:device_code", "device_code": device_code}).json()
97
+ print(resp)
98
+ time.sleep(5)
99
+ access_token = resp["access_token"]
100
+ return access_token
101
+
102
+
77
103
  def get_token(self):
78
104
  # construct the token request
79
105
  token_request = {
@@ -113,9 +139,11 @@ class OIDCClient:
113
139
  return now.timestamp() < expires_at
114
140
 
115
141
  def keyring_get(self):
142
+ return []
116
143
  return keyring.get_password("system", "libtimed_token_" + self.client_id)
117
144
 
118
145
  def keyring_set(self, token):
146
+ return True
119
147
  keyring.set_password("system", "libtimed_token_" + self.client_id, token)
120
148
 
121
149
  def authorize(self):
@@ -124,6 +152,12 @@ class OIDCClient:
124
152
  return cached_token
125
153
 
126
154
  self.autoconfig()
155
+ if self.use_device_flow:
156
+ if token:= self.start_device_flow():
157
+ self.keyring_set(token)
158
+ return token
159
+ return False
160
+
127
161
  if self.start_browser_flow():
128
162
  token = self.get_token()
129
163
  if not token:
@@ -153,8 +153,17 @@ class Time(BaseTransform):
153
153
 
154
154
  """Transform for times."""
155
155
 
156
- @staticmethod
157
- def serialize(value: time | str, **_) -> str | None:
156
+ def __init__(self, return_datetime: bool = False):
157
+ # return_datetime will be removed at some point
158
+ # it defaults to True to maintain backwards compatibility
159
+ self.return_datetime = return_datetime
160
+
161
+ def _return_value(self, value: datetime):
162
+ if self.return_datetime:
163
+ return value
164
+ return value.time()
165
+
166
+ def serialize(self, value: time | str, **_) -> str | None:
158
167
  if isinstance(value, str):
159
168
  try:
160
169
  value = datetime.strptime(value, TIME_FORMAT).time()
@@ -165,9 +174,10 @@ class Time(BaseTransform):
165
174
 
166
175
  return value.strftime(TIME_FORMAT) if value else None
167
176
 
168
- @staticmethod
169
- def deserialize(value) -> date | None:
170
- return datetime.strptime(value, "%H:%M:%S").time() if value else None
177
+ def deserialize(self, value) -> time | datetime | None:
178
+ return (
179
+ self._return_value(datetime.strptime(value, "%H:%M:%S")) if value else None
180
+ )
171
181
 
172
182
 
173
183
  class Enum(BaseTransform):
File without changes
File without changes