ladok3 4.9__py3-none-any.whl → 4.11__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.

Potentially problematic release.


This version of ladok3 might be problematic. Click here for more details.

ladok3/cli.nw CHANGED
@@ -504,7 +504,11 @@ If all fail, the function will return [[None]] for both.
504
504
  (This is due to how we handle the [[login]] command.)
505
505
  <<functions>>=
506
506
  def load_credentials(filename="config.json"):
507
- """Load credentials from environment or file named filename"""
507
+ """
508
+ Loads credentials from environment or file named filename.
509
+ Returns the tuple (instituation, credential dictionary) that
510
+ can be passed to `LadokSession(instiution, credential dictionary)`.
511
+ """
508
512
 
509
513
  <<fetch vars from keyring>>
510
514
  <<fetch username and password from keyring>>
@@ -518,6 +522,9 @@ def load_credentials(filename="config.json"):
518
522
 
519
523
  First we try the newest format.
520
524
  We try to fetch the institution and vars from the keyring.
525
+
526
+ Note that [[keyring]] returns [[None]] if the key doesn't exist, it doesn't
527
+ raise an exception.
521
528
  <<fetch vars from keyring>>=
522
529
  try:
523
530
  institution = keyring.get_password("ladok3", "institution")
@@ -525,7 +532,9 @@ try:
525
532
 
526
533
  vars = {}
527
534
  for key in vars_keys.split(";"):
528
- vars[key] = keyring.get_password("ladok3", key)
535
+ value = keyring.get_password("ladok3", key)
536
+ if value:
537
+ vars[key] = value
529
538
 
530
539
  if institution and vars:
531
540
  return institution, vars
@@ -538,12 +547,10 @@ supported KTH.
538
547
  <<fetch username and password from keyring>>=
539
548
  try:
540
549
  institution = "KTH Royal Institute of Technology"
541
- vars = {
542
- "username": keyring.get_password("ladok3", "username"),
543
- "password": keyring.get_password("ladok3", "password")
544
- }
545
- if vars:
546
- return institution, vars
550
+ username = keyring.get_password("ladok3", "username")
551
+ password = keyring.get_password("ladok3", "password")
552
+ if username and password:
553
+ return institution, {"username": username, "password": password}
547
554
  except:
548
555
  pass
549
556
  @
@@ -577,7 +584,10 @@ try:
577
584
 
578
585
  vars = {}
579
586
  for key in vars_keys.split(":"):
580
- vars[key] = os.environ[key]
587
+ try:
588
+ vars[key] = os.environ[key]
589
+ except KeyError:
590
+ <<print warning about missing variable in [[LADOK_VARS]]>>
581
591
 
582
592
  if institution and vars:
583
593
  return institution, vars
@@ -585,6 +595,14 @@ except:
585
595
  pass
586
596
  @
587
597
 
598
+ Unlike in the other cases, we don't just ignore the exception of the key not
599
+ existing.
600
+ Since the user has explicitly specified the variable, we should warn them that
601
+ it doesn't exist.
602
+ <<print warning about missing variable in [[LADOK_VARS]]>>=
603
+ warn(f"Variable {key} not set, ignoring.")
604
+ @
605
+
588
606
  If none of the above worked, the last resort is to try to read the
589
607
  configuration file.
590
608
  We pop the institution from the configuration file (a dictionary), because then
@@ -595,7 +613,7 @@ try:
595
613
  config = json.load(conf_file)
596
614
 
597
615
  institution = config.pop("institution",
598
- "KTH Royal Institute of Technology")
616
+ "KTH Royal Institute of Technology")
599
617
  return institution, config
600
618
  except:
601
619
  pass
ladok3/cli.py CHANGED
@@ -26,92 +26,97 @@ import ladok3.student
26
26
 
27
27
  dirs = appdirs.AppDirs("ladok", "dbosk@kth.se")
28
28
 
29
+
29
30
  def err(rc, msg):
30
- print(f"{sys.argv[0]}: error: {msg}", file=sys.stderr)
31
- sys.exit(rc)
31
+ print(f"{sys.argv[0]}: error: {msg}", file=sys.stderr)
32
+ sys.exit(rc)
33
+
32
34
 
33
35
  def warn(msg):
34
- print(f"{sys.argv[0]}: {msg}", file=sys.stderr)
35
- def store_ladok_session(ls, credentials):
36
- if not os.path.isdir(dirs.user_cache_dir):
37
- os.makedirs(dirs.user_cache_dir)
36
+ print(f"{sys.argv[0]}: {msg}", file=sys.stderr)
38
37
 
39
- file_path = dirs.user_cache_dir + "/LadokSession"
40
38
 
41
- pickled_ls = pickle.dumps(ls)
42
- if not credentials or len(credentials) < 2:
43
- raise ValueError(f"Missing credentials, see `ladok login -h`.")
39
+ def store_ladok_session(ls, credentials):
40
+ if not os.path.isdir(dirs.user_cache_dir):
41
+ os.makedirs(dirs.user_cache_dir)
44
42
 
45
- if isinstance(credentials, dict):
46
- try:
47
- salt = credentials["username"]
48
- passwd = credentials["password"]
49
- except KeyError:
50
- credentials = list(credentials.values())
51
- salt = credentials[0]
52
- passwd = credentials[1]
53
- else:
54
- salt = credentials[0]
55
- passwd = credentials[1]
56
-
57
- kdf = PBKDF2HMAC(
58
- algorithm=hashes.SHA256(),
59
- length=32,
60
- salt=salt.encode("utf-8"),
61
- iterations=100000
62
- )
63
- key = base64.urlsafe_b64encode(kdf.derive(passwd.encode("utf-8")))
64
-
65
-
66
- fernet_protocol = Fernet(key)
67
- encrypted_ls = fernet_protocol.encrypt(pickled_ls)
68
-
69
-
70
- with open(file_path, "wb") as file:
71
- file.write(encrypted_ls)
72
-
73
- def restore_ladok_session(credentials):
74
- file_path = dirs.user_cache_dir + "/LadokSession"
43
+ file_path = dirs.user_cache_dir + "/LadokSession"
75
44
 
76
- if os.path.isfile(file_path):
77
- with open(file_path, "rb") as file:
78
- encrypted_ls = file.read()
79
- if not credentials or len(credentials) < 2:
45
+ pickled_ls = pickle.dumps(ls)
46
+ if not credentials or len(credentials) < 2:
80
47
  raise ValueError(f"Missing credentials, see `ladok login -h`.")
81
48
 
82
- if isinstance(credentials, dict):
49
+ if isinstance(credentials, dict):
83
50
  try:
84
- salt = credentials["username"]
85
- passwd = credentials["password"]
51
+ salt = credentials["username"]
52
+ passwd = credentials["password"]
86
53
  except KeyError:
87
- credentials = list(credentials.values())
88
- salt = credentials[0]
89
- passwd = credentials[1]
90
- else:
54
+ credentials = list(credentials.values())
55
+ salt = credentials[0]
56
+ passwd = credentials[1]
57
+ else:
91
58
  salt = credentials[0]
92
59
  passwd = credentials[1]
93
60
 
94
- kdf = PBKDF2HMAC(
61
+ kdf = PBKDF2HMAC(
95
62
  algorithm=hashes.SHA256(),
96
63
  length=32,
97
64
  salt=salt.encode("utf-8"),
98
- iterations=100000
99
- )
100
- key = base64.urlsafe_b64encode(kdf.derive(passwd.encode("utf-8")))
65
+ iterations=100000,
66
+ )
67
+ key = base64.urlsafe_b64encode(kdf.derive(passwd.encode("utf-8")))
68
+
69
+ fernet_protocol = Fernet(key)
70
+ encrypted_ls = fernet_protocol.encrypt(pickled_ls)
71
+
72
+ with open(file_path, "wb") as file:
73
+ file.write(encrypted_ls)
101
74
 
102
75
 
103
- fernet_protocol = Fernet(key)
104
- try:
105
- pickled_ls = fernet_protocol.decrypt(encrypted_ls)
106
- except Exception as err:
107
- warn(f"cache was corrupted, cannot decrypt: {err}")
108
- pickled_ls = None
109
- if pickled_ls:
110
- return pickle.loads(pickled_ls)
76
+ def restore_ladok_session(credentials):
77
+ file_path = dirs.user_cache_dir + "/LadokSession"
78
+
79
+ if os.path.isfile(file_path):
80
+ with open(file_path, "rb") as file:
81
+ encrypted_ls = file.read()
82
+ if not credentials or len(credentials) < 2:
83
+ raise ValueError(f"Missing credentials, see `ladok login -h`.")
84
+
85
+ if isinstance(credentials, dict):
86
+ try:
87
+ salt = credentials["username"]
88
+ passwd = credentials["password"]
89
+ except KeyError:
90
+ credentials = list(credentials.values())
91
+ salt = credentials[0]
92
+ passwd = credentials[1]
93
+ else:
94
+ salt = credentials[0]
95
+ passwd = credentials[1]
96
+
97
+ kdf = PBKDF2HMAC(
98
+ algorithm=hashes.SHA256(),
99
+ length=32,
100
+ salt=salt.encode("utf-8"),
101
+ iterations=100000,
102
+ )
103
+ key = base64.urlsafe_b64encode(kdf.derive(passwd.encode("utf-8")))
104
+
105
+ fernet_protocol = Fernet(key)
106
+ try:
107
+ pickled_ls = fernet_protocol.decrypt(encrypted_ls)
108
+ except Exception as err:
109
+ warn(f"cache was corrupted, cannot decrypt: {err}")
110
+ pickled_ls = None
111
+ if pickled_ls:
112
+ return pickle.loads(pickled_ls)
113
+
114
+ return None
115
+
111
116
 
112
- return None
113
117
  def update_credentials_in_keyring(ls, args):
114
- print("""
118
+ print(
119
+ """
115
120
  This login process is exactly the same as when you log in using
116
121
  the web browser. You need three things:
117
122
 
@@ -126,57 +131,66 @@ the web browser. You need three things:
126
131
 
127
132
  3) Your password at your institution.
128
133
 
129
- """)
130
- while True:
131
- institution = input("Institution: ")
132
- matches = sa.find_entity_data_by_name(institution)
134
+ """
135
+ )
136
+ while True:
137
+ institution = input("Institution: ")
138
+ matches = sa.find_entity_data_by_name(institution)
133
139
 
134
- if not matches:
135
- print("No match, try again.")
136
- continue
140
+ if not matches:
141
+ print("No match, try again.")
142
+ continue
137
143
 
138
- if len(matches) > 1:
139
- print("More than one match. Which one?")
140
- for match in matches:
141
- print(f"- {match['title']}")
142
- continue
144
+ if len(matches) > 1:
145
+ print("More than one match. Which one?")
146
+ for match in matches:
147
+ print(f"- {match['title']}")
148
+ continue
143
149
 
144
- match = matches[0]
150
+ match = matches[0]
145
151
 
146
- print(f"Matched uniquely, using {match['title']}\n"
147
- f" with domain {match['domain']} and\n"
148
- f" unique identifier {match['id']}.")
152
+ print(
153
+ f"Matched uniquely, using {match['title']}\n"
154
+ f" with domain {match['domain']} and\n"
155
+ f" unique identifier {match['id']}."
156
+ )
149
157
 
150
- institution = match['id']
151
- break
158
+ institution = match["id"]
159
+ break
152
160
 
153
- vars = {
154
- "username": input("Institution username: "),
155
- "password": getpass.getpass("Institution password: [input is hidden] ")
156
- }
157
- while True:
158
- temp_ls = ladok3.LadokSession(institution, vars=vars)
161
+ vars = {
162
+ "username": input("Institution username: "),
163
+ "password": getpass.getpass("Institution password: [input is hidden] "),
164
+ }
165
+ while True:
166
+ temp_ls = ladok3.LadokSession(institution, vars=vars)
167
+
168
+ try:
169
+ temp_ls.user_info_JSON()
170
+ except weblogin.AuthenticationError as err:
171
+ adjust_vars(vars, err.variables)
172
+ else:
173
+ break
159
174
 
160
175
  try:
161
- temp_ls.user_info_JSON()
162
- except weblogin.AuthenticationError as err:
163
- adjust_vars(vars, err.variables)
164
- else:
165
- break
166
-
167
- try:
168
- keyring.set_password("ladok3", "institution", institution)
169
- keyring.set_password("ladok3", "vars", ";".join(vars.keys()))
170
- for key, value in vars.items():
171
- keyring.set_password("ladok3", key, value)
172
- except Exception as err:
173
- globals()["err"](-1, f"You don't seem to have a working keyring. "
174
- f"Use one of the other methods, see "
175
- f"`ladok login -h`: {err}.")
176
-
177
- clear_cache(ls, args)
176
+ keyring.set_password("ladok3", "institution", institution)
177
+ keyring.set_password("ladok3", "vars", ";".join(vars.keys()))
178
+ for key, value in vars.items():
179
+ keyring.set_password("ladok3", key, value)
180
+ except Exception as err:
181
+ globals()["err"](
182
+ -1,
183
+ f"You don't seem to have a working keyring. "
184
+ f"Use one of the other methods, see "
185
+ f"`ladok login -h`: {err}.",
186
+ )
187
+
188
+ clear_cache(ls, args)
189
+
190
+
178
191
  def adjust_vars(vars, form_variables):
179
- print("""
192
+ print(
193
+ """
180
194
  Some part of the authentication went wrong. Either you typed your username or
181
195
  password incorrectly, or your institution requires some adjustments. We'll
182
196
  guide you through it.
@@ -191,103 +205,115 @@ when it should be 'dbosk@ug.kth.se' --- or something similar. Use your
191
205
  institution's login page to figure this out.
192
206
 
193
207
  Note: Your password will be visible on screen during this process.
194
- """)
195
- input("\nPress return to continue.\n")
196
-
197
- for key, value in form_variables.items():
198
- key = key.casefold()
199
- new_val = input(f"{key} = '{value}' "
200
- f"[enter new value, blank to keep] ")
201
- if new_val:
202
- vars[key] = new_val
208
+ """
209
+ )
210
+ input("\nPress return to continue.\n")
211
+
212
+ for key, value in form_variables.items():
213
+ key = key.casefold()
214
+ new_val = input(f"{key} = '{value}' " f"[enter new value, blank to keep] ")
215
+ if new_val:
216
+ vars[key] = new_val
217
+
218
+
203
219
  def load_credentials(filename="config.json"):
204
- """Load credentials from environment or file named filename"""
205
-
206
- try:
207
- institution = keyring.get_password("ladok3", "institution")
208
- vars_keys = keyring.get_password("ladok3", "vars")
209
-
210
- vars = {}
211
- for key in vars_keys.split(";"):
212
- vars[key] = keyring.get_password("ladok3", key)
213
-
214
- if institution and vars:
215
- return institution, vars
216
- except:
217
- pass
218
- try:
219
- institution = "KTH Royal Institute of Technology"
220
- vars = {
221
- "username": keyring.get_password("ladok3", "username"),
222
- "password": keyring.get_password("ladok3", "password")
223
- }
224
- if vars:
225
- return institution, vars
226
- except:
227
- pass
228
- try:
229
- institution = os.environ["LADOK_INST"]
230
- except:
231
- institution = "KTH Royal Institute of Technology"
232
-
233
- try:
234
- vars = {
235
- "username": os.environ["LADOK_USER"],
236
- "password": os.environ["LADOK_PASS"]
237
- }
238
- if institution and vars:
239
- return institution, vars
240
- except:
241
- pass
242
- try:
243
- vars_keys = os.environ["LADOK_VARS"]
244
-
245
- vars = {}
246
- for key in vars_keys.split(":"):
247
- vars[key] = os.environ[key]
248
-
249
- if institution and vars:
250
- return institution, vars
251
- except:
252
- pass
253
- try:
254
- with open(filename) as conf_file:
255
- config = json.load(conf_file)
256
-
257
- institution = config.pop("institution",
258
- "KTH Royal Institute of Technology")
259
- return institution, config
260
- except:
261
- pass
262
-
263
- return None, None
220
+ """
221
+ Loads credentials from environment or file named filename.
222
+ Returns the tuple (instituation, credential dictionary) that
223
+ can be passed to `LadokSession(instiution, credential dictionary)`.
224
+ """
225
+
226
+ try:
227
+ institution = keyring.get_password("ladok3", "institution")
228
+ vars_keys = keyring.get_password("ladok3", "vars")
229
+
230
+ vars = {}
231
+ for key in vars_keys.split(";"):
232
+ value = keyring.get_password("ladok3", key)
233
+ if value:
234
+ vars[key] = value
235
+
236
+ if institution and vars:
237
+ return institution, vars
238
+ except:
239
+ pass
240
+ try:
241
+ institution = "KTH Royal Institute of Technology"
242
+ username = keyring.get_password("ladok3", "username")
243
+ password = keyring.get_password("ladok3", "password")
244
+ if username and password:
245
+ return institution, {"username": username, "password": password}
246
+ except:
247
+ pass
248
+ try:
249
+ institution = os.environ["LADOK_INST"]
250
+ except:
251
+ institution = "KTH Royal Institute of Technology"
252
+
253
+ try:
254
+ vars = {
255
+ "username": os.environ["LADOK_USER"],
256
+ "password": os.environ["LADOK_PASS"],
257
+ }
258
+ if institution and vars:
259
+ return institution, vars
260
+ except:
261
+ pass
262
+ try:
263
+ vars_keys = os.environ["LADOK_VARS"]
264
+
265
+ vars = {}
266
+ for key in vars_keys.split(":"):
267
+ try:
268
+ vars[key] = os.environ[key]
269
+ except KeyError:
270
+ warn(f"Variable {key} not set, ignoring.")
271
+
272
+ if institution and vars:
273
+ return institution, vars
274
+ except:
275
+ pass
276
+ try:
277
+ with open(filename) as conf_file:
278
+ config = json.load(conf_file)
279
+
280
+ institution = config.pop("institution", "KTH Royal Institute of Technology")
281
+ return institution, config
282
+ except:
283
+ pass
284
+
285
+ return None, None
286
+
287
+
264
288
  def clear_cache(ls, args):
265
- try:
266
- os.remove(dirs.user_cache_dir + "/LadokSession")
267
- except FileNotFoundError as err:
268
- pass
289
+ try:
290
+ os.remove(dirs.user_cache_dir + "/LadokSession")
291
+ except FileNotFoundError as err:
292
+ pass
293
+
294
+ sys.exit(0)
295
+
269
296
 
270
- sys.exit(0)
271
297
  def main():
272
- """Run the command-line interface for the ladok command"""
273
- argp = argparse.ArgumentParser(
274
- description="This is a CLI-ification of LADOK3's web GUI.",
275
- epilog="Web: https://github.com/dbosk/ladok3"
276
- )
277
- argp.add_argument("-f", "--config-file",
278
- default=f"{dirs.user_config_dir}/config.json",
279
- help="Path to configuration file "
280
- f"(default: {dirs.user_config_dir}/config.json) "
281
- "or set LADOK_USER and LADOK_PASS environment variables.")
282
- subp = argp.add_subparsers(
283
- title="commands",
284
- dest="command",
285
- required=True
286
- )
287
- login_parser = subp.add_parser("login",
288
- help="Manage login credentials",
289
- formatter_class=argparse.RawDescriptionHelpFormatter,
290
- description=f"""
298
+ """Run the command-line interface for the ladok command"""
299
+ argp = argparse.ArgumentParser(
300
+ description="This is a CLI-ification of LADOK3's web GUI.",
301
+ epilog="Web: https://github.com/dbosk/ladok3",
302
+ )
303
+ argp.add_argument(
304
+ "-f",
305
+ "--config-file",
306
+ default=f"{dirs.user_config_dir}/config.json",
307
+ help="Path to configuration file "
308
+ f"(default: {dirs.user_config_dir}/config.json) "
309
+ "or set LADOK_USER and LADOK_PASS environment variables.",
310
+ )
311
+ subp = argp.add_subparsers(title="commands", dest="command", required=True)
312
+ login_parser = subp.add_parser(
313
+ "login",
314
+ help="Manage login credentials",
315
+ formatter_class=argparse.RawDescriptionHelpFormatter,
316
+ description=f"""
291
317
  Manages the user's LADOK login credentials. There are three ways to supply the
292
318
  login credentials, in order of priority:
293
319
 
@@ -317,42 +343,40 @@ def main():
317
343
  option). (The keys 'username' and 'password' can be renamed to correspond to
318
344
  the necessary values if the university login system uses other names.)
319
345
 
320
- """)
321
-
322
- login_parser.set_defaults(func=update_credentials_in_keyring)
323
- cache_parser = subp.add_parser("cache",
324
- help="Manage cache",
325
- description="Manages the cache of LADOK data"
326
- )
327
- cache_subp = cache_parser.add_subparsers(
328
- title="subcommands",
329
- dest="subcommand",
330
- required=True
331
- )
332
- cache_clear = cache_subp.add_parser("clear",
333
- help="Clear the cache",
334
- description="Clears everything from the cache"
335
- )
336
- cache_clear.set_defaults(func=clear_cache)
337
- ladok3.data.add_command_options(subp)
338
- ladok3.report.add_command_options(subp)
339
- ladok3.student.add_command_options(subp)
340
- argcomplete.autocomplete(argp)
341
- args = argp.parse_args()
342
- LADOK_INST, LADOK_VARS = load_credentials(args.config_file)
343
- try:
344
- ls = restore_ladok_session(LADOK_VARS)
345
- except ValueError as error:
346
- err(-1, f"Couldn't restore LADOK session: {error}")
347
- if not ls:
348
- ls = ladok3.LadokSession(LADOK_INST, vars=LADOK_VARS)
349
- if "func" in args:
350
- args.func(ls, args)
351
- store_ladok_session(ls, LADOK_VARS)
346
+ """,
347
+ )
348
+
349
+ login_parser.set_defaults(func=update_credentials_in_keyring)
350
+ cache_parser = subp.add_parser(
351
+ "cache", help="Manage cache", description="Manages the cache of LADOK data"
352
+ )
353
+ cache_subp = cache_parser.add_subparsers(
354
+ title="subcommands", dest="subcommand", required=True
355
+ )
356
+ cache_clear = cache_subp.add_parser(
357
+ "clear", help="Clear the cache", description="Clears everything from the cache"
358
+ )
359
+ cache_clear.set_defaults(func=clear_cache)
360
+ ladok3.data.add_command_options(subp)
361
+ ladok3.report.add_command_options(subp)
362
+ ladok3.student.add_command_options(subp)
363
+ argcomplete.autocomplete(argp)
364
+ args = argp.parse_args()
365
+ LADOK_INST, LADOK_VARS = load_credentials(args.config_file)
366
+ try:
367
+ ls = restore_ladok_session(LADOK_VARS)
368
+ except ValueError as error:
369
+ err(-1, f"Couldn't restore LADOK session: {error}")
370
+ if not ls:
371
+ ls = ladok3.LadokSession(LADOK_INST, vars=LADOK_VARS)
372
+ if "func" in args:
373
+ args.func(ls, args)
374
+ store_ladok_session(ls, LADOK_VARS)
375
+
352
376
 
353
377
  if __name__ == "__main__":
354
- try:
355
- main()
356
- sys.exit(0)
357
- except Exception as e:
358
- err(-1, e)
378
+ try:
379
+ main()
380
+ sys.exit(0)
381
+ except Exception as e:
382
+ err(-1, e)