cmdbox 0.5.0.8__py3-none-any.whl → 0.5.1__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 cmdbox might be problematic. Click here for more details.

Files changed (51) hide show
  1. cmdbox/app/edge.py +96 -12
  2. cmdbox/app/features/cli/cmdbox_edge_config.py +9 -3
  3. cmdbox/app/features/cli/cmdbox_gui_start.py +1 -1
  4. cmdbox/app/features/cli/cmdbox_web_start.py +2 -1
  5. cmdbox/app/features/web/cmdbox_web_bbforce_cmd.py +1 -1
  6. cmdbox/app/features/web/cmdbox_web_copyright.py +1 -1
  7. cmdbox/app/features/web/cmdbox_web_del_cmd.py +1 -1
  8. cmdbox/app/features/web/cmdbox_web_del_pipe.py +1 -1
  9. cmdbox/app/features/web/cmdbox_web_do_signin.py +88 -32
  10. cmdbox/app/features/web/cmdbox_web_exec_cmd.py +2 -2
  11. cmdbox/app/features/web/cmdbox_web_exec_pipe.py +2 -2
  12. cmdbox/app/features/web/cmdbox_web_filer download.py +2 -3
  13. cmdbox/app/features/web/cmdbox_web_filer.py +1 -1
  14. cmdbox/app/features/web/cmdbox_web_filer_upload.py +1 -1
  15. cmdbox/app/features/web/cmdbox_web_get_cmd_choices.py +1 -1
  16. cmdbox/app/features/web/cmdbox_web_get_cmds.py +2 -2
  17. cmdbox/app/features/web/cmdbox_web_get_modes.py +2 -2
  18. cmdbox/app/features/web/cmdbox_web_get_server_opt.py +1 -1
  19. cmdbox/app/features/web/cmdbox_web_gui.py +2 -2
  20. cmdbox/app/features/web/cmdbox_web_list_cmd.py +2 -2
  21. cmdbox/app/features/web/cmdbox_web_list_pipe.py +2 -2
  22. cmdbox/app/features/web/cmdbox_web_load_cmd.py +1 -1
  23. cmdbox/app/features/web/cmdbox_web_load_pin.py +3 -5
  24. cmdbox/app/features/web/cmdbox_web_load_pipe.py +1 -1
  25. cmdbox/app/features/web/cmdbox_web_raw_cmd.py +1 -1
  26. cmdbox/app/features/web/cmdbox_web_raw_pipe.py +1 -1
  27. cmdbox/app/features/web/cmdbox_web_result.py +2 -2
  28. cmdbox/app/features/web/cmdbox_web_save_cmd.py +1 -1
  29. cmdbox/app/features/web/cmdbox_web_save_pin.py +2 -2
  30. cmdbox/app/features/web/cmdbox_web_save_pipe.py +1 -1
  31. cmdbox/app/features/web/cmdbox_web_signin.py +26 -8
  32. cmdbox/app/features/web/cmdbox_web_users.py +35 -37
  33. cmdbox/app/features/web/cmdbox_web_versions_cmdbox.py +1 -1
  34. cmdbox/app/features/web/cmdbox_web_versions_used.py +1 -1
  35. cmdbox/app/options.py +8 -8
  36. cmdbox/app/web.py +76 -555
  37. cmdbox/extensions/sample_project/sample/extensions/features.yml +38 -13
  38. cmdbox/extensions/sample_project/sample/extensions/user_list.yml +82 -40
  39. cmdbox/extensions/user_list.yml +10 -0
  40. cmdbox/version.py +2 -2
  41. cmdbox/web/assets/cmdbox/list_cmd.js +50 -2
  42. cmdbox/web/assets/cmdbox/signin.js +7 -0
  43. cmdbox/web/gui.html +1 -0
  44. cmdbox/web/signin.html +7 -0
  45. {cmdbox-0.5.0.8.dist-info → cmdbox-0.5.1.dist-info}/METADATA +1 -1
  46. {cmdbox-0.5.0.8.dist-info → cmdbox-0.5.1.dist-info}/RECORD +50 -51
  47. cmdbox/app/signin.py +0 -56
  48. {cmdbox-0.5.0.8.dist-info → cmdbox-0.5.1.dist-info}/LICENSE +0 -0
  49. {cmdbox-0.5.0.8.dist-info → cmdbox-0.5.1.dist-info}/WHEEL +0 -0
  50. {cmdbox-0.5.0.8.dist-info → cmdbox-0.5.1.dist-info}/entry_points.txt +0 -0
  51. {cmdbox-0.5.0.8.dist-info → cmdbox-0.5.1.dist-info}/top_level.txt +0 -0
cmdbox/app/web.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from cmdbox.app import common, options
2
+ from cmdbox.app.auth.signin import Signin
2
3
  from cmdbox.app.commons import module
3
4
  from fastapi import FastAPI, Request, Response, HTTPException
4
5
  from fastapi.responses import RedirectResponse
@@ -95,7 +96,6 @@ class Web:
95
96
  self.users_html_data = None
96
97
  self.assets_data = None
97
98
  self.signin_html_data = None
98
- self.signin_file_data = None
99
99
  self.gui_mode = gui_mode
100
100
  self.web_features_packages = web_features_packages
101
101
  self.web_features_prefix = web_features_prefix
@@ -111,8 +111,9 @@ class Web:
111
111
  self.cb_queue = queue.Queue(1000)
112
112
  self.options = options.Options.getInstance()
113
113
  self.webcap_client = requests.Session()
114
- self.load_signin_file()
115
-
114
+ signin_file_data = Signin.load_signin_file(self.signin_file)
115
+ self.signin = Signin(self.logger, self.signin_file, signin_file_data, self.appcls, self.ver)
116
+
116
117
  if self.logger.level == logging.DEBUG:
117
118
  self.logger.debug(f"web init parameter: data={self.data} -> {self.data.absolute() if self.data is not None else None}")
118
119
  self.logger.debug(f"web init parameter: redis_host={self.redis_host}")
@@ -134,233 +135,6 @@ class Web:
134
135
  self.logger.debug(f"web init parameter: pipes_path={self.pipes_path} -> {self.pipes_path.absolute() if self.pipes_path is not None else None}")
135
136
  self.logger.debug(f"web init parameter: users_path={self.users_path} -> {self.users_path.absolute() if self.users_path is not None else None}")
136
137
 
137
- def enable_cors(self, req:Request, res:Response) -> None:
138
- """
139
- CORSを有効にする
140
-
141
- Args:
142
- req (Request): リクエスト
143
- res (Response): レスポンス
144
- """
145
- if req is None or not 'Origin' in req.headers.keys():
146
- return
147
- res.headers['Access-Control-Allow-Origin'] = res.headers['Origin']
148
-
149
- def check_apikey(self, req:Request, res:Response):
150
- """
151
- ApiKeyをチェックする
152
-
153
- Args:
154
- req (Request): リクエスト
155
- res (Response): レスポンス
156
-
157
- Returns:
158
- Response: サインインエラーの場合はリダイレクトレスポンス
159
- """
160
- self.enable_cors(req, res)
161
- if self.signin_file is None:
162
- res.headers['signin'] = 'success'
163
- return None
164
- if self.signin_file_data is None:
165
- raise ValueError(f'signin_file_data is None. ({self.signin_file})')
166
- if 'Authorization' not in req.headers:
167
- self.logger.warning(f"Authorization not found. headers={req.headers}")
168
- return RedirectResponse(url=f'/signin{req.url.path}?error=noauth')
169
- auth = req.headers['Authorization']
170
- if not auth.startswith('Bearer '):
171
- self.logger.warning(f"Bearer not found. headers={req.headers}")
172
- return RedirectResponse(url=f'/signin{req.url.path}?error=apikeyfail')
173
- bearer, apikey = auth.split(' ')
174
- apikey = common.hash_password(apikey.strip(), 'sha1')
175
- if self.logger.level == logging.DEBUG:
176
- self.logger.debug(f"hashed apikey: {apikey}")
177
- find_user = None
178
- self.load_signin_file() # サインインファイルの更新をチェック
179
- for user in self.signin_file_data['users']:
180
- if 'apikeys' not in user:
181
- continue
182
- for ak, key in user['apikeys'].items():
183
- if apikey == key:
184
- find_user = user
185
- if find_user is None:
186
- self.logger.warning(f"No matching user found for apikey.")
187
- return RedirectResponse(url=f'/signin{req.url.path}?error=apikeyfail')
188
-
189
- group_names = list(set(self.correct_group(find_user['groups'])))
190
- gids = [g['gid'] for g in self.signin_file_data['groups'] if g['name'] in group_names]
191
- req.session['signin'] = dict(uid=find_user['uid'], name=find_user['name'], password=find_user['password'],
192
- gids=gids, groups=group_names)
193
- if self.logger.level == logging.DEBUG:
194
- self.logger.debug(f"find user: name={find_user['name']}, group_names={group_names}")
195
- # パスルールチェック
196
- user_groups = find_user['groups']
197
- jadge = self.signin_file_data['pathrule']['policy']
198
- for rule in self.signin_file_data['pathrule']['rules']:
199
- if len([g for g in rule['groups'] if g in user_groups]) <= 0:
200
- continue
201
- if len([p for p in rule['paths'] if req.url.path.startswith(p)]) <= 0:
202
- continue
203
- jadge = rule['rule']
204
- if self.logger.level == logging.DEBUG:
205
- self.logger.debug(f"rule: {req.url.path}: {jadge}")
206
- if jadge == 'allow':
207
- res.headers['signin'] = 'success'
208
- return None
209
- self.logger.warning(f"Unauthorized site. user={find_user['name']}, path={req.url.path}")
210
- return RedirectResponse(url=f'/signin{req.url.path}?error=unauthorizedsite')
211
-
212
- def check_signin(self, req:Request, res:Response):
213
- """
214
- サインインをチェックする
215
-
216
- Args:
217
- req (Request): リクエスト
218
- res (Response): レスポンス
219
-
220
- Returns:
221
- Response: サインインエラーの場合はリダイレクトレスポンス
222
- """
223
- self.enable_cors(req, res)
224
- if self.signin_file is None:
225
- return None
226
- if self.signin_file_data is None:
227
- raise ValueError(f'signin_file_data is None. ({self.signin_file})')
228
- if 'signin' in req.session:
229
- self.load_signin_file() # サインインファイルの更新をチェック
230
- path_jadge = self.check_path(req, req.url.path)
231
- if path_jadge is not None:
232
- return path_jadge
233
- return None
234
- self.logger.info(f"Not found siginin session. Try check_apikey. path={req.url.path}")
235
- ret = self.check_apikey(req, res)
236
- if ret is not None and self.logger.level == logging.DEBUG:
237
- self.logger.debug(f"Not signed in.")
238
- return ret
239
-
240
- def check_path(self, req:Request, path:str):
241
- if 'signin' not in req.session:
242
- return None
243
- path = path if path.startswith('/') else f'/{path}'
244
- # パスルールチェック
245
- user_groups = req.session['signin']['groups']
246
- jadge = self.signin_file_data['pathrule']['policy']
247
- for rule in self.signin_file_data['pathrule']['rules']:
248
- if len([g for g in rule['groups'] if g in user_groups]) <= 0:
249
- continue
250
- if len([p for p in rule['paths'] if path.startswith(p)]) <= 0:
251
- continue
252
- jadge = rule['rule']
253
- if self.logger.level == logging.DEBUG:
254
- self.logger.debug(f"rule: {path}: {jadge}")
255
- if jadge == 'allow':
256
- return None
257
- else:
258
- self.logger.warning(f"Unauthorized site. user={req.session['signin']['name']}, path={path}")
259
- return RedirectResponse(url=f'/signin{path}?error=unauthorizedsite')
260
-
261
- def check_cmd(self, req:Request, res:Response, mode:str, cmd:str):
262
- if self.signin_file is None:
263
- return True
264
- if self.signin_file_data is None:
265
- raise ValueError(f'signin_file_data is None. ({self.signin_file})')
266
- if 'signin' not in req.session or 'groups' not in req.session['signin']:
267
- return False
268
- # コマンドチェック
269
- user_groups = req.session['signin']['groups']
270
- jadge = self.signin_file_data['cmdrule']['policy']
271
- for rule in self.signin_file_data['cmdrule']['rules']:
272
- if len([g for g in rule['groups'] if g in user_groups]) <= 0:
273
- continue
274
- if rule['mode'] is not None:
275
- if rule['mode'] != mode:
276
- continue
277
- if len([c for c in rule['cmds'] if cmd == c]) <= 0:
278
- continue
279
- jadge = rule['rule']
280
- if self.logger.level == logging.DEBUG:
281
- self.logger.debug(f"rule: mode={mode}, cmd={cmd}: {jadge}")
282
- return jadge == 'allow'
283
-
284
- def get_enable_modes(self, req:Request, res:Response):
285
- if self.signin_file is None:
286
- return self.options.get_modes().copy()
287
- if self.signin_file_data is None:
288
- raise ValueError(f'signin_file_data is None. ({self.signin_file})')
289
- if 'signin' not in req.session or 'groups' not in req.session['signin']:
290
- return []
291
- modes = self.options.get_modes().copy()
292
- user_groups = req.session['signin']['groups']
293
- jadge = self.signin_file_data['cmdrule']['policy']
294
- jadge_modes = []
295
- if jadge == 'allow':
296
- for m in modes:
297
- jadge_modes += list(m.keys()) if type(m) is dict else [m]
298
- for rule in self.signin_file_data['cmdrule']['rules']:
299
- if len([g for g in rule['groups'] if g in user_groups]) <= 0:
300
- continue
301
- if 'mode' not in rule:
302
- continue
303
- if rule['mode'] is not None:
304
- if rule['rule'] == 'allow':
305
- jadge_modes.append(rule['mode'])
306
- elif rule['rule'] == 'deny':
307
- jadge_modes.remove(rule['mode'])
308
- elif rule['mode'] is None and len(rule['cmds']) <= 0:
309
- if rule['rule'] == 'allow':
310
- for m in modes:
311
- jadge_modes += list(m.keys()) if type(m) is dict else [m]
312
- elif rule['rule'] == 'deny':
313
- jadge_modes = []
314
- return sorted(list(set(['']+jadge_modes)), key=lambda m: m)
315
-
316
- def get_enable_cmds(self, mode:str, req:Request, res:Response):
317
- if self.signin_file is None:
318
- cmds = self.options.get_cmds(mode).copy()
319
- return cmds
320
- if self.signin_file_data is None:
321
- raise ValueError(f'signin_file_data is None. ({self.signin_file})')
322
- if 'signin' not in req.session or 'groups' not in req.session['signin']:
323
- return []
324
- cmds = self.options.get_cmds(mode).copy()
325
- if mode == '':
326
- return cmds
327
- user_groups = req.session['signin']['groups']
328
- jadge = self.signin_file_data['cmdrule']['policy']
329
- jadge_cmds = []
330
- if jadge == 'allow':
331
- for c in cmds:
332
- jadge_cmds += list(c.keys()) if type(c) is dict else [c]
333
- for rule in self.signin_file_data['cmdrule']['rules']:
334
- if len([g for g in rule['groups'] if g in user_groups]) <= 0:
335
- continue
336
- if 'mode' not in rule:
337
- continue
338
- if 'cmds' not in rule:
339
- continue
340
- if rule['mode'] is not None and rule['mode'] != mode:
341
- continue
342
- if len(rule['cmds']) > 0:
343
- if rule['rule'] == 'allow':
344
- jadge_cmds += rule['cmds']
345
- elif rule['rule'] == 'deny':
346
- for c in rule['cmds']:
347
- jadge_cmds.remove[c]
348
- elif rule['mode'] is None and len(rule['cmds']) <= 0:
349
- if rule['rule'] == 'allow':
350
- for c in cmds:
351
- jadge_cmds += list(c.keys()) if type(c) is dict else [c]
352
- elif rule['rule'] == 'deny':
353
- jadge_cmds = []
354
- return sorted(list(set(['']+jadge_cmds)), key=lambda c: c)
355
-
356
- def correct_group(self, group_names, master_groups=None):
357
- master_groups = self.signin_file_data['groups'] if master_groups is None else master_groups
358
- gns = []
359
- for gn in group_names.copy():
360
- gns = [gr['name'] for gr in master_groups if 'parent' in gr and gr['parent']==gn]
361
- gns += self.correct_group(gns, master_groups)
362
- return group_names + gns
363
-
364
138
  def init_webfeatures(self, app:FastAPI):
365
139
  self.filemenu = dict()
366
140
  self.toolmenu = dict()
@@ -393,270 +167,6 @@ class Web:
393
167
  for route in app.routes:
394
168
  self.logger.debug(f"loaded webfeature: {route}")
395
169
 
396
- def load_signin_file(self):
397
- """
398
- サインインファイルを読み込む
399
-
400
- Raises:
401
- HTTPException: サインインファイルのフォーマットエラー
402
- """
403
- if self.signin_file is not None:
404
- if not self.signin_file.is_file():
405
- raise HTTPException(status_code=500, detail=f'signin_file is not found. ({self.signin_file})')
406
- # サインインファイル読込み済みなら返すが、別プロセスがサインインファイルを更新していたら読込みを実施する。
407
- if not hasattr(self, 'signin_file_last'):
408
- self.signin_file_last = self.signin_file.stat().st_mtime
409
- if self.signin_file_last >= self.signin_file.stat().st_mtime and self.signin_file_data is not None:
410
- return
411
- self.signin_file_last = self.signin_file.stat().st_mtime
412
- yml = common.load_yml(self.signin_file)
413
- # usersのフォーマットチェック
414
- if 'users' not in yml:
415
- raise HTTPException(status_code=500, detail=f'signin_file format error. "users" not found. ({self.signin_file})')
416
- uids = set()
417
- unames = set()
418
- groups = [g['name'] for g in yml['groups']]
419
- for user in yml['users']:
420
- if 'uid' not in user or user['uid'] is None:
421
- raise HTTPException(status_code=500, detail=f'signin_file format error. "uid" not found or empty. ({self.signin_file})')
422
- if user['uid'] in uids:
423
- raise HTTPException(status_code=500, detail=f'signin_file format error. Duplicate uid found. ({self.signin_file}). uid={user["uid"]}')
424
- if 'name' not in user or user['name'] is None:
425
- raise HTTPException(status_code=500, detail=f'signin_file format error. "name" not found or empty. ({self.signin_file})')
426
- if user['name'] in unames:
427
- raise HTTPException(status_code=500, detail=f'signin_file format error. Duplicate name found. ({self.signin_file}). name={user["name"]}')
428
- if 'password' not in user:
429
- raise HTTPException(status_code=500, detail=f'signin_file format error. "password" not found or empty. ({self.signin_file})')
430
- if 'hash' not in user or user['hash'] is None:
431
- raise HTTPException(status_code=500, detail=f'signin_file format error. "hash" not found or empty. ({self.signin_file})')
432
- if user['hash'] not in ['oauth2', 'plain', 'md5', 'sha1', 'sha256']:
433
- raise HTTPException(status_code=500, detail=f'signin_file format error. Algorithms not supported. ({self.signin_file}). hash={user["hash"]} "oauth2", "plain", "md5", "sha1", "sha256" only.')
434
- if 'groups' not in user or type(user['groups']) is not list:
435
- raise HTTPException(status_code=500, detail=f'signin_file format error. "groups" not found or not list type. ({self.signin_file})')
436
- if len([ug for ug in user['groups'] if ug not in groups]) > 0:
437
- raise HTTPException(status_code=500, detail=f'signin_file format error. Group not found. ({self.signin_file}). {user["groups"]}')
438
- uids.add(user['uid'])
439
- unames.add(user['name'])
440
- # groupsのフォーマットチェック
441
- if 'groups' not in yml:
442
- raise HTTPException(status_code=500, detail=f'signin_file format error. "groups" not found. ({self.signin_file})')
443
- gids = set()
444
- gnames = set()
445
- for group in yml['groups']:
446
- if 'gid' not in group or group['gid'] is None:
447
- raise HTTPException(status_code=500, detail=f'signin_file format error. "gid" not found or empty. ({self.signin_file})')
448
- if group['gid'] in gids:
449
- raise HTTPException(status_code=500, detail=f'signin_file format error. Duplicate gid found. ({self.signin_file}). gid={group["gid"]}')
450
- if 'name' not in group or group['name'] is None:
451
- raise HTTPException(status_code=500, detail=f'signin_file format error. "name" not found or empty. ({self.signin_file})')
452
- if group['name'] in gnames:
453
- raise HTTPException(status_code=500, detail=f'signin_file format error. Duplicate name found. ({self.signin_file}). name={group["name"]}')
454
- if 'parent' in group:
455
- if group['parent'] not in groups:
456
- raise HTTPException(status_code=500, detail=f'signin_file format error. Parent group not found. ({self.signin_file}). parent={group["parent"]}')
457
- gids.add(group['gid'])
458
- gnames.add(group['name'])
459
- # cmdruleのフォーマットチェック
460
- if 'cmdrule' not in yml:
461
- raise HTTPException(status_code=500, detail=f'signin_file format error. "cmdrule" not found. ({self.signin_file})')
462
- if 'policy' not in yml['cmdrule']:
463
- raise HTTPException(status_code=500, detail=f'signin_file format error. "policy" not found in "cmdrule". ({self.signin_file})')
464
- if yml['cmdrule']['policy'] not in ['allow', 'deny']:
465
- raise HTTPException(status_code=500, detail=f'signin_file format error. "policy" not supported in "cmdrule". ({self.signin_file}). "allow" or "deny" only.')
466
- if 'rules' not in yml['cmdrule']:
467
- raise HTTPException(status_code=500, detail=f'signin_file format error. "rules" not found in "cmdrule". ({self.signin_file})')
468
- if type(yml['cmdrule']['rules']) is not list:
469
- raise HTTPException(status_code=500, detail=f'signin_file format error. "rules" not list type in "cmdrule". ({self.signin_file})')
470
- for rule in yml['cmdrule']['rules']:
471
- if 'groups' not in rule:
472
- raise HTTPException(status_code=500, detail=f'signin_file format error. "groups" not found in "cmdrule.rules" ({self.signin_file})')
473
- if type(rule['groups']) is not list:
474
- raise HTTPException(status_code=500, detail=f'signin_file format error. "groups" not list type in "cmdrule.rules". ({self.signin_file})')
475
- rule['groups'] = list(set(copy.deepcopy(self.correct_group(rule['groups'], yml['groups']))))
476
- if 'rule' not in rule:
477
- raise HTTPException(status_code=500, detail=f'signin_file format error. "rule" not found in "cmdrule.rules" ({self.signin_file})')
478
- if rule['rule'] not in ['allow', 'deny']:
479
- raise HTTPException(status_code=500, detail=f'signin_file format error. "rule" not supported in "cmdrule.rules". ({self.signin_file}). "allow" or "deny" only.')
480
- if 'mode' not in rule:
481
- rule['mode'] = None
482
- if 'cmds' not in rule:
483
- rule['cmds'] = []
484
- if rule['mode'] is not None and len(rule['cmds']) <= 0:
485
- raise HTTPException(status_code=500, detail=f'signin_file format error. When “cmds” is specified, “mode” must be specified. ({self.signin_file})')
486
- if type(rule['cmds']) is not list:
487
- raise HTTPException(status_code=500, detail=f'signin_file format error. "cmds" not list type in "cmdrule.rules". ({self.signin_file})')
488
- # pathruleのフォーマットチェック
489
- if 'pathrule' not in yml:
490
- raise HTTPException(status_code=500, detail=f'signin_file format error. "pathrule" not found. ({self.signin_file})')
491
- if 'policy' not in yml['pathrule']:
492
- raise HTTPException(status_code=500, detail=f'signin_file format error. "policy" not found in "pathrule". ({self.signin_file})')
493
- if yml['pathrule']['policy'] not in ['allow', 'deny']:
494
- raise HTTPException(status_code=500, detail=f'signin_file format error. "policy" not supported in "pathrule". ({self.signin_file}). "allow" or "deny" only.')
495
- if 'rules' not in yml['pathrule']:
496
- raise HTTPException(status_code=500, detail=f'signin_file format error. "rules" not found in "pathrule". ({self.signin_file})')
497
- if type(yml['pathrule']['rules']) is not list:
498
- raise HTTPException(status_code=500, detail=f'signin_file format error. "rules" not list type in "pathrule". ({self.signin_file})')
499
- for rule in yml['pathrule']['rules']:
500
- if 'groups' not in rule:
501
- raise HTTPException(status_code=500, detail=f'signin_file format error. "groups" not found in "pathrule.rules" ({self.signin_file})')
502
- if type(rule['groups']) is not list:
503
- raise HTTPException(status_code=500, detail=f'signin_file format error. "groups" not list type in "pathrule.rules". ({self.signin_file})')
504
- rule['groups'] = list(set(copy.deepcopy(self.correct_group(rule['groups'], yml['groups']))))
505
- if 'rule' not in rule:
506
- raise HTTPException(status_code=500, detail=f'signin_file format error. "rule" not found in "pathrule.rules" ({self.signin_file})')
507
- if rule['rule'] not in ['allow', 'deny']:
508
- raise HTTPException(status_code=500, detail=f'signin_file format error. "rule" not supported in "pathrule.rules". ({self.signin_file}). "allow" or "deny" only.')
509
- if 'paths' not in rule:
510
- rule['paths'] = []
511
- if type(rule['paths']) is not list:
512
- raise HTTPException(status_code=500, detail=f'signin_file format error. "paths" not list type in "pathrule.rules". ({self.signin_file})')
513
- # passwordのフォーマットチェック
514
- if 'password' in yml:
515
- if 'policy' not in yml['password']:
516
- raise HTTPException(status_code=500, detail=f'signin_file format error. "policy" not found in "password". ({self.signin_file})')
517
- if 'enabled' not in yml['password']['policy']:
518
- raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not found in "password.policy". ({self.signin_file})')
519
- if type(yml['password']['policy']['enabled']) is not bool:
520
- raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not bool type in "password.policy". ({self.signin_file})')
521
- if type(yml['password']['policy']['not_same_before']) is not bool:
522
- raise HTTPException(status_code=500, detail=f'signin_file format error. "not_same_before" not bool type in "password.policy". ({self.signin_file})')
523
- if 'min_length' not in yml['password']['policy']:
524
- raise HTTPException(status_code=500, detail=f'signin_file format error. "min_length" not found in "password.policy". ({self.signin_file})')
525
- if type(yml['password']['policy']['min_length']) is not int:
526
- raise HTTPException(status_code=500, detail=f'signin_file format error. "min_length" not int type in "password.policy". ({self.signin_file})')
527
- if 'max_length' not in yml['password']['policy']:
528
- raise HTTPException(status_code=500, detail=f'signin_file format error. "max_length" not found in "password.policy". ({self.signin_file})')
529
- if type(yml['password']['policy']['max_length']) is not int:
530
- raise HTTPException(status_code=500, detail=f'signin_file format error. "max_length" not int type in "password.policy". ({self.signin_file})')
531
- if 'min_lowercase' not in yml['password']['policy']:
532
- raise HTTPException(status_code=500, detail=f'signin_file format error. "min_lowercase" not found in "password.policy". ({self.signin_file})')
533
- if type(yml['password']['policy']['min_lowercase']) is not int:
534
- raise HTTPException(status_code=500, detail=f'signin_file format error. "min_lowercase" not int type in "password.policy". ({self.signin_file})')
535
- if 'min_uppercase' not in yml['password']['policy']:
536
- raise HTTPException(status_code=500, detail=f'signin_file format error. "min_uppercase" not found in "password.policy". ({self.signin_file})')
537
- if type(yml['password']['policy']['min_uppercase']) is not int:
538
- raise HTTPException(status_code=500, detail=f'signin_file format error. "min_uppercase" not int type in "password.policy". ({self.signin_file})')
539
- if 'min_digit' not in yml['password']['policy']:
540
- raise HTTPException(status_code=500, detail=f'signin_file format error. "min_digit" not found in "password.policy". ({self.signin_file})')
541
- if type(yml['password']['policy']['min_digit']) is not int:
542
- raise HTTPException(status_code=500, detail=f'signin_file format error. "min_digit" not int type in "password.policy". ({self.signin_file})')
543
- if 'min_symbol' not in yml['password']['policy']:
544
- raise HTTPException(status_code=500, detail=f'signin_file format error. "min_symbol" not found in "password.policy". ({self.signin_file})')
545
- if type(yml['password']['policy']['min_symbol']) is not int:
546
- raise HTTPException(status_code=500, detail=f'signin_file format error. "min_symbol" not int type in "password.policy". ({self.signin_file})')
547
- if 'not_contain_username' not in yml['password']['policy']:
548
- raise HTTPException(status_code=500, detail=f'signin_file format error. "not_contain_username" not found in "password.policy". ({self.signin_file})')
549
- if type(yml['password']['policy']['not_contain_username']) is not bool:
550
- raise HTTPException(status_code=500, detail=f'signin_file format error. "not_contain_username" not bool type in "password.policy". ({self.signin_file})')
551
- if 'expiration' not in yml['password']:
552
- raise HTTPException(status_code=500, detail=f'signin_file format error. "expiration" not found in "password". ({self.signin_file})')
553
- if 'enabled' not in yml['password']['expiration']:
554
- raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not found in "password.expiration". ({self.signin_file})')
555
- if type(yml['password']['expiration']['enabled']) is not bool:
556
- raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not bool type in "password.expiration". ({self.signin_file})')
557
- if 'period' not in yml['password']['expiration']:
558
- raise HTTPException(status_code=500, detail=f'signin_file format error. "period" not found in "password.expiration". ({self.signin_file})')
559
- if type(yml['password']['expiration']['period']) is not int:
560
- raise HTTPException(status_code=500, detail=f'signin_file format error. "period" not int type in "password.expiration". ({self.signin_file})')
561
- if 'notify' not in yml['password']['expiration']:
562
- raise HTTPException(status_code=500, detail=f'signin_file format error. "notify" not found in "password.expiration". ({self.signin_file})')
563
- if type(yml['password']['expiration']['notify']) is not int:
564
- raise HTTPException(status_code=500, detail=f'signin_file format error. "notify" not int type in "password.expiration". ({self.signin_file})')
565
- if 'lockout' not in yml['password']:
566
- raise HTTPException(status_code=500, detail=f'signin_file format error. "lockout" not found in "password". ({self.signin_file})')
567
- if 'enabled' not in yml['password']['lockout']:
568
- raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not found in "password.lockout". ({self.signin_file})')
569
- if type(yml['password']['lockout']['enabled']) is not bool:
570
- raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not bool type in "password.lockout". ({self.signin_file})')
571
- if 'threshold' not in yml['password']['lockout']:
572
- raise HTTPException(status_code=500, detail=f'signin_file format error. "threshold" not found in "password.lockout". ({self.signin_file})')
573
- if type(yml['password']['lockout']['threshold']) is not int:
574
- raise HTTPException(status_code=500, detail=f'signin_file format error. "threshold" not int type in "password.lockout". ({self.signin_file})')
575
- if 'reset' not in yml['password']['lockout']:
576
- raise HTTPException(status_code=500, detail=f'signin_file format error. "reset" not found in "password.lockout". ({self.signin_file})')
577
- if type(yml['password']['lockout']['reset']) is not int:
578
- raise HTTPException(status_code=500, detail=f'signin_file format error. "reset" not int type in "password.lockout". ({self.signin_file})')
579
- # oauth2のフォーマットチェック
580
- if 'oauth2' not in yml:
581
- raise HTTPException(status_code=500, detail=f'signin_file format error. "oauth2" not found. ({self.signin_file})')
582
- if 'providers' not in yml['oauth2']:
583
- raise HTTPException(status_code=500, detail=f'signin_file format error. "providers" not found in "oauth2". ({self.signin_file})')
584
- if 'google' not in yml['oauth2']['providers']:
585
- raise HTTPException(status_code=500, detail=f'signin_file format error. "google" not found in "providers". ({self.signin_file})')
586
- if 'enabled' not in yml['oauth2']['providers']['google']:
587
- raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not found in "google". ({self.signin_file})')
588
- if type(yml['oauth2']['providers']['google']['enabled']) is not bool:
589
- raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not bool type in "google". ({self.signin_file})')
590
- if 'client_id' not in yml['oauth2']['providers']['google']:
591
- raise HTTPException(status_code=500, detail=f'signin_file format error. "client_id" not found in "google". ({self.signin_file})')
592
- if 'client_secret' not in yml['oauth2']['providers']['google']:
593
- raise HTTPException(status_code=500, detail=f'signin_file format error. "client_secret" not found in "google". ({self.signin_file})')
594
- if 'redirect_uri' not in yml['oauth2']['providers']['google']:
595
- raise HTTPException(status_code=500, detail=f'signin_file format error. "redirect_uri" not found in "google". ({self.signin_file})')
596
- if 'scope' not in yml['oauth2']['providers']['google']:
597
- raise HTTPException(status_code=500, detail=f'signin_file format error. "scope" not found in "google". ({self.signin_file})')
598
- if type(yml['oauth2']['providers']['google']['scope']) is not list:
599
- raise HTTPException(status_code=500, detail=f'signin_file format error. "scope" not list type in "google". ({self.signin_file})')
600
- if 'github' not in yml['oauth2']['providers']:
601
- raise HTTPException(status_code=500, detail=f'signin_file format error. "github" not found in "providers". ({self.signin_file})')
602
- if 'enabled' not in yml['oauth2']['providers']['github']:
603
- raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not found in "github". ({self.signin_file})')
604
- if type(yml['oauth2']['providers']['github']['enabled']) is not bool:
605
- raise HTTPException(status_code=500, detail=f'signin_file format error. "enabled" not bool type in "github". ({self.signin_file})')
606
- if 'client_id' not in yml['oauth2']['providers']['github']:
607
- raise HTTPException(status_code=500, detail=f'signin_file format error. "client_id" not found in "github". ({self.signin_file})')
608
- if 'client_secret' not in yml['oauth2']['providers']['github']:
609
- raise HTTPException(status_code=500, detail=f'signin_file format error. "client_secret" not found in "github". ({self.signin_file})')
610
- if 'redirect_uri' not in yml['oauth2']['providers']['github']:
611
- raise HTTPException(status_code=500, detail=f'signin_file format error. "redirect_uri" not found in "github". ({self.signin_file})')
612
- if 'scope' not in yml['oauth2']['providers']['github']:
613
- raise HTTPException(status_code=500, detail=f'signin_file format error. "scope" not found in "github". ({self.signin_file})')
614
- if type(yml['oauth2']['providers']['github']['scope']) is not list:
615
- raise HTTPException(status_code=500, detail=f'signin_file format error. "scope" not list type in "github". ({self.signin_file})')
616
- # フォーマットチェックOK
617
- self.signin_file_data = yml
618
-
619
- def check_password_policy(self, user_name:str, password:str, new_password:str) -> Tuple[bool, str]:
620
- """
621
- パスワードポリシーをチェックする
622
-
623
- Args:
624
- user_name (str): ユーザー名
625
- password (str): 元パスワード
626
- new_password (str): 新しいパスワード
627
- Returns:
628
- bool: True:ポリシーOK, False:ポリシーNG
629
- str: メッセージ
630
- """
631
- if self.signin_file_data is None or 'password' not in self.signin_file_data:
632
- return True, "There is no password policy set."
633
- policy = self.signin_file_data['password']['policy']
634
- if not policy['enabled']:
635
- return True, "Password policy is disabled."
636
- if policy['not_same_before'] and password == new_password:
637
- self.logger.warning(f"Password policy error. The same password cannot be changed.")
638
- return False, f"Password policy error. The same password cannot be changed."
639
- if len(new_password) < policy['min_length'] or len(new_password) > policy['max_length']:
640
- self.logger.warning(f"Password policy error. min_length={policy['min_length']}, max_length={policy['max_length']}")
641
- return False, f"Password policy error. min_length={policy['min_length']}, max_length={policy['max_length']}"
642
- if len([c for c in new_password if c.islower()]) < policy['min_lowercase']:
643
- self.logger.warning(f"Password policy error. min_lowercase={policy['min_lowercase']}")
644
- return False, f"Password policy error. min_lowercase={policy['min_lowercase']}"
645
- if len([c for c in new_password if c.isupper()]) < policy['min_uppercase']:
646
- self.logger.warning(f"Password policy error. min_uppercase={policy['min_uppercase']}")
647
- return False, f"Password policy error. min_uppercase={policy['min_uppercase']}"
648
- if len([c for c in new_password if c.isdigit()]) < policy['min_digit']:
649
- self.logger.warning(f"Password policy error. min_digit={policy['min_digit']}")
650
- return False, f"Password policy error. min_digit={policy['min_digit']}"
651
- if len([c for c in new_password if c in string.punctuation]) < policy['min_symbol']:
652
- self.logger.warning(f"Password policy error. min_symbol={policy['min_symbol']}")
653
- return False, f"Password policy error. min_symbol={policy['min_symbol']}"
654
- if policy['not_contain_username'] and (user_name is None or user_name in new_password):
655
- self.logger.warning(f"Password policy error. not_contain_username=True")
656
- return False, f"Password policy error. not_contain_username=True"
657
- self.logger.info(f"Password policy OK.")
658
- return True, "Password policy OK."
659
-
660
170
  def change_password(self, user_name:str, password:str, new_password:str, confirm_password:str):
661
171
  """
662
172
  パスワードを変更する
@@ -670,7 +180,8 @@ class Web:
670
180
  HTTPException: パスワードが一致しない場合
671
181
  HTTPException: ユーザーが存在しない場合
672
182
  """
673
- if self.signin_file_data is None:
183
+ signin_data = self.signin.get_data()
184
+ if signin_data is None:
674
185
  raise ValueError(f'signin_file_data is None. ({self.signin_file})')
675
186
  if self.signin_file is None:
676
187
  raise ValueError(f"signin_file is None.")
@@ -684,19 +195,19 @@ class Web:
684
195
  return dict(warn="Confirm password is empty.")
685
196
  if new_password != confirm_password:
686
197
  return dict(warn="Password does not match.")
687
- for u in self.signin_file_data['users']:
198
+ for u in signin_data['users']:
688
199
  if u['name'] == user_name:
689
200
  p = password if u['hash'] == 'plain' else common.hash_password(password, u['hash'])
690
201
  if u['password'] != p:
691
202
  return dict(warn="Password does not match.")
692
- jadge, msg = self.check_password_policy(user_name, password, new_password)
203
+ jadge, msg = self.signin.check_password_policy(user_name, password, new_password)
693
204
  if not jadge:
694
205
  return dict(warn=msg)
695
206
  u['password'] = new_password if u['hash'] == 'plain' else common.hash_password(new_password, u['hash'])
696
207
  # パスワード更新日時の保存
697
208
  self.user_data(None, u['uid'], user_name, 'password', 'last_update', datetime.datetime.now())
698
209
  # サインインファイルの保存
699
- common.save_yml(self.signin_file, self.signin_file_data)
210
+ common.save_yml(self.signin_file, signin_data)
700
211
  return dict(success="Password changed.")
701
212
  return dict(warn="User not found.")
702
213
 
@@ -710,12 +221,13 @@ class Web:
710
221
  Returns:
711
222
  List[Dict[str, Any]]: ユーザー一覧
712
223
  """
713
- if self.signin_file_data is None:
224
+ signin_data = self.signin.get_data()
225
+ if signin_data is None:
714
226
  raise ValueError(f'signin_file_data is None. ({self.signin_file})')
715
227
  if self.signin_file is None:
716
228
  raise ValueError(f"signin_file is None.")
717
229
  ret = []
718
- for u in copy.deepcopy(self.signin_file_data['users']):
230
+ for u in copy.deepcopy(signin_data['users']):
719
231
  u['password'] = '********'
720
232
  if 'apikeys' in u:
721
233
  u['apikeys'] = dict([(ak, '********') for ak in u['apikeys']])
@@ -741,7 +253,8 @@ class Web:
741
253
  Returns:
742
254
  str: ApiKey
743
255
  """
744
- if self.signin_file_data is None:
256
+ signin_data = self.signin.get_data()
257
+ if signin_data is None:
745
258
  raise ValueError(f'signin_file_data is None. ({self.signin_file})')
746
259
  if self.signin_file is None:
747
260
  raise ValueError(f"signin_file is None.")
@@ -749,10 +262,10 @@ class Web:
749
262
  raise ValueError(f"User name is not found. ({user})")
750
263
  if 'apikey_name' not in user:
751
264
  raise ValueError(f"ApiKey name is not found. ({user})")
752
- if len([u for u in self.signin_file_data['users'] if u['name'] == user['name']]) <= 0:
265
+ if len([u for u in signin_data['users'] if u['name'] == user['name']]) <= 0:
753
266
  raise ValueError(f"User name is not exists. ({user})")
754
267
  apikey:str = None
755
- for u in self.signin_file_data['users']:
268
+ for u in signin_data['users']:
756
269
  if u['name'] == user['name']:
757
270
  if 'apikeys' not in u:
758
271
  u['apikeys'] = dict()
@@ -765,7 +278,7 @@ class Web:
765
278
  raise ValueError(f"signin_file is None.")
766
279
  if self.logger.level == logging.DEBUG:
767
280
  self.logger.debug(f"apikey_add: {user} -> {self.signin_file}")
768
- common.save_yml(self.signin_file, self.signin_file_data)
281
+ common.save_yml(self.signin_file, signin_data)
769
282
  return apikey
770
283
 
771
284
  def apikey_del(self, user:Dict[str, Any]):
@@ -775,7 +288,8 @@ class Web:
775
288
  Args:
776
289
  user (Dict[str, Any]): ユーザー情報
777
290
  """
778
- if self.signin_file_data is None:
291
+ signin_data = self.signin.get_data()
292
+ if signin_data is None:
779
293
  raise ValueError(f'signin_file_data is None. ({self.signin_file})')
780
294
  if self.signin_file is None:
781
295
  raise ValueError(f"signin_file is None.")
@@ -783,10 +297,10 @@ class Web:
783
297
  raise ValueError(f"User name is not found. ({user})")
784
298
  if 'apikey_name' not in user:
785
299
  raise ValueError(f"ApiKey name is not found. ({user})")
786
- if len([u for u in self.signin_file_data['users'] if u['name'] == user['name']]) <= 0:
300
+ if len([u for u in signin_data['users'] if u['name'] == user['name']]) <= 0:
787
301
  raise ValueError(f"User name is not exists. ({user})")
788
302
  apikey:str = None
789
- for u in self.signin_file_data['users']:
303
+ for u in signin_data['users']:
790
304
  if u['name'] == user['name']:
791
305
  if 'apikeys' not in u:
792
306
  continue
@@ -803,7 +317,7 @@ class Web:
803
317
  raise ValueError(f"signin_file is None.")
804
318
  if self.logger.level == logging.DEBUG:
805
319
  self.logger.debug(f"apikey_del: {user} -> {self.signin_file}")
806
- common.save_yml(self.signin_file, self.signin_file_data)
320
+ common.save_yml(self.signin_file, signin_data)
807
321
 
808
322
  def user_add(self, user:Dict[str, Any]):
809
323
  """
@@ -812,7 +326,8 @@ class Web:
812
326
  Args:
813
327
  user (Dict[str, Any]): ユーザー情報
814
328
  """
815
- if self.signin_file_data is None:
329
+ signin_data = self.signin.get_data()
330
+ if signin_data is None:
816
331
  raise ValueError(f'signin_file_data is None. ({self.signin_file})')
817
332
  if self.signin_file is None:
818
333
  raise ValueError(f"signin_file is None.")
@@ -838,26 +353,26 @@ class Web:
838
353
  for gn in user['groups']:
839
354
  if len(self.group_list(gn)) <= 0:
840
355
  raise ValueError(f"Group is not found. ({gn})")
841
- if len([u for u in self.signin_file_data['users'] if u['uid'] == user['uid']]) > 0:
356
+ if len([u for u in signin_data['users'] if u['uid'] == user['uid']]) > 0:
842
357
  raise ValueError(f"User uid is already exists. ({user})")
843
- if len([u for u in self.signin_file_data['users'] if u['name'] == user['name']]) > 0:
358
+ if len([u for u in signin_data['users'] if u['name'] == user['name']]) > 0:
844
359
  raise ValueError(f"User name is already exists. ({user})")
845
360
  if hash not in ['oauth2', 'plain', 'md5', 'sha1', 'sha256']:
846
361
  raise ValueError(f"User hash is not supported. ({user})")
847
- jadge, msg = self.check_password_policy(user['name'], '', user['password'])
362
+ jadge, msg = self.signin.check_password_policy(user['name'], '', user['password'])
848
363
  if not jadge:
849
364
  raise ValueError(msg)
850
365
  if hash != 'plain':
851
366
  user['password'] = common.hash_password(user['password'], hash if hash != 'oauth2' else 'sha1')
852
367
  else:
853
368
  user['password'] = user['password']
854
- self.signin_file_data['users'].append(user)
369
+ signin_data['users'].append(user)
855
370
  if self.logger.level == logging.DEBUG:
856
371
  self.logger.debug(f"user_add: {user} -> {self.signin_file}")
857
372
  # パスワード更新日時の保存
858
373
  self.user_data(None, user['uid'], user['name'], 'password', 'last_update', datetime.datetime.now())
859
374
  # サインインファイルの保存
860
- common.save_yml(self.signin_file, self.signin_file_data)
375
+ common.save_yml(self.signin_file, signin_data)
861
376
 
862
377
  def user_edit(self, user:Dict[str, Any]):
863
378
  """
@@ -866,7 +381,8 @@ class Web:
866
381
  Args:
867
382
  user (Dict[str, Any]): ユーザー情報
868
383
  """
869
- if self.signin_file_data is None:
384
+ signin_data = self.signin.get_data()
385
+ if signin_data is None:
870
386
  raise ValueError(f'signin_file_data is None. ({self.signin_file})')
871
387
  if self.signin_file is None:
872
388
  raise ValueError(f"signin_file is None.")
@@ -890,17 +406,17 @@ class Web:
890
406
  for gn in user['groups']:
891
407
  if len(self.group_list(gn)) <= 0:
892
408
  raise ValueError(f"Group is not found. ({gn})")
893
- if len([u for u in self.signin_file_data['users'] if u['uid'] == user['uid']]) <= 0:
409
+ if len([u for u in signin_data['users'] if u['uid'] == user['uid']]) <= 0:
894
410
  raise ValueError(f"User uid is not found. ({user})")
895
- if len([u for u in self.signin_file_data['users'] if u['name'] == user['name']]) <= 0:
411
+ if len([u for u in signin_data['users'] if u['name'] == user['name']]) <= 0:
896
412
  raise ValueError(f"User name is not found. ({user})")
897
413
  if hash not in ['oauth2', 'plain', 'md5', 'sha1', 'sha256']:
898
414
  raise ValueError(f"User hash is not supported. ({user})")
899
- for u in self.signin_file_data['users']:
415
+ for u in signin_data['users']:
900
416
  if u['uid'] == user['uid']:
901
417
  u['name'] = user['name']
902
418
  if 'password' in user and user['password'] != '':
903
- jadge, msg = self.check_password_policy(user['name'], u['password'], user['password'])
419
+ jadge, msg = self.signin.check_password_policy(user['name'], u['password'], user['password'])
904
420
  if not jadge:
905
421
  raise ValueError(msg)
906
422
  if hash != 'plain':
@@ -915,7 +431,7 @@ class Web:
915
431
  if self.logger.level == logging.DEBUG:
916
432
  self.logger.debug(f"user_edit: {user} -> {self.signin_file}")
917
433
  # サインインファイルの保存
918
- common.save_yml(self.signin_file, self.signin_file_data)
434
+ common.save_yml(self.signin_file, signin_data)
919
435
 
920
436
  def user_del(self, uid:int):
921
437
  """
@@ -924,7 +440,8 @@ class Web:
924
440
  Args:
925
441
  uid (int): ユーザーID
926
442
  """
927
- if self.signin_file_data is None:
443
+ signin_data = self.signin.get_data()
444
+ if signin_data is None:
928
445
  raise ValueError(f'signin_file_data is None. ({self.signin_file})')
929
446
  if self.signin_file is None:
930
447
  raise ValueError(f"signin_file is None.")
@@ -932,13 +449,13 @@ class Web:
932
449
  uid = int(uid)
933
450
  except:
934
451
  raise ValueError(f"User uid is not number. ({uid})")
935
- users = [u for u in self.signin_file_data['users'] if u['uid'] != uid]
936
- if len(users) == len(self.signin_file_data['users']):
452
+ users = [u for u in signin_data['users'] if u['uid'] != uid]
453
+ if len(users) == len(signin_data['users']):
937
454
  raise ValueError(f"User uid is not found. ({uid})")
938
- self.signin_file_data['users'] = users
455
+ signin_data['users'] = users
939
456
  if self.logger.level == logging.DEBUG:
940
457
  self.logger.debug(f"user_del: {uid} -> {self.signin_file}")
941
- common.save_yml(self.signin_file, self.signin_file_data)
458
+ common.save_yml(self.signin_file, signin_data)
942
459
 
943
460
  def group_list(self, name:str=None) -> List[Dict[str, Any]]:
944
461
  """
@@ -950,11 +467,12 @@ class Web:
950
467
  Returns:
951
468
  List[Dict[str, Any]]: グループ一覧
952
469
  """
953
- if self.signin_file_data is None:
470
+ signin_data = self.signin.get_data()
471
+ if signin_data is None:
954
472
  raise ValueError(f'signin_file_data is None. ({self.signin_file})')
955
473
  if name is None:
956
- return copy.deepcopy(self.signin_file_data['groups'])
957
- for g in copy.deepcopy(self.signin_file_data['groups']):
474
+ return copy.deepcopy(signin_data['groups'])
475
+ for g in copy.deepcopy(signin_data['groups']):
958
476
  if g['name'] == name:
959
477
  return [g]
960
478
  return []
@@ -966,7 +484,8 @@ class Web:
966
484
  Args:
967
485
  group (Dict[str, Any]): グループ情報
968
486
  """
969
- if self.signin_file_data is None:
487
+ signin_data = self.signin.get_data()
488
+ if signin_data is None:
970
489
  raise ValueError(f'signin_file_data is None. ({self.signin_file})')
971
490
  if self.signin_file is None:
972
491
  raise ValueError(f"signin_file is None.")
@@ -980,20 +499,20 @@ class Web:
980
499
  raise ValueError(f"Group name is not found. ({group})")
981
500
  if 'parent' in group and (group['parent'] is None or group['parent'] == ''):
982
501
  del group['parent']
983
- elif 'parent' in group and group['parent'] not in [g['name'] for g in self.signin_file_data['groups']]:
502
+ elif 'parent' in group and group['parent'] not in [g['name'] for g in signin_data['groups']]:
984
503
  raise ValueError(f"Group parent is not found. ({group})")
985
504
  if 'parent' in group and group['parent'] == group['name']:
986
505
  raise ValueError(f"Group parent is same as group name. ({group})")
987
- if len([g for g in self.signin_file_data['groups'] if g['gid'] == group['gid']]) > 0:
506
+ if len([g for g in signin_data['groups'] if g['gid'] == group['gid']]) > 0:
988
507
  raise ValueError(f"Group gid is already exists. ({group})")
989
- if len([g for g in self.signin_file_data['groups'] if g['name'] == group['name']]) > 0:
508
+ if len([g for g in signin_data['groups'] if g['name'] == group['name']]) > 0:
990
509
  raise ValueError(f"Group name is already exists. ({group})")
991
- self.signin_file_data['groups'].append(group)
510
+ signin_data['groups'].append(group)
992
511
  if self.signin_file is None:
993
512
  raise ValueError(f"signin_file is None.")
994
513
  if self.logger.level == logging.DEBUG:
995
514
  self.logger.debug(f"group_add: {group} -> {self.signin_file}")
996
- common.save_yml(self.signin_file, self.signin_file_data)
515
+ common.save_yml(self.signin_file, signin_data)
997
516
 
998
517
  def group_edit(self, group:Dict[str, Any]):
999
518
  """
@@ -1002,7 +521,8 @@ class Web:
1002
521
  Args:
1003
522
  group (Dict[str, Any]): グループ情報
1004
523
  """
1005
- if self.signin_file_data is None:
524
+ signin_data = self.signin.get_data()
525
+ if signin_data is None:
1006
526
  raise ValueError(f'signin_file_data is None. ({self.signin_file})')
1007
527
  if self.signin_file is None:
1008
528
  raise ValueError(f"signin_file is None.")
@@ -1016,15 +536,15 @@ class Web:
1016
536
  raise ValueError(f"Group name is not found. ({group})")
1017
537
  if 'parent' in group and (group['parent'] is None or group['parent'] == ''):
1018
538
  del group['parent']
1019
- elif 'parent' in group and group['parent'] not in [g['name'] for g in self.signin_file_data['groups']]:
539
+ elif 'parent' in group and group['parent'] not in [g['name'] for g in signin_data['groups']]:
1020
540
  raise ValueError(f"Group parent is not found. ({group})")
1021
541
  if 'parent' in group and group['parent'] == group['name']:
1022
542
  raise ValueError(f"Group parent is same as group name. ({group})")
1023
- if len([g for g in self.signin_file_data['groups'] if g['gid'] == group['gid']]) <= 0:
543
+ if len([g for g in signin_data['groups'] if g['gid'] == group['gid']]) <= 0:
1024
544
  raise ValueError(f"Group gid is not found. ({group})")
1025
- if len([g for g in self.signin_file_data['groups'] if g['name'] == group['name']]) <= 0:
545
+ if len([g for g in signin_data['groups'] if g['name'] == group['name']]) <= 0:
1026
546
  raise ValueError(f"Group name is not found. ({group})")
1027
- for g in self.signin_file_data['groups']:
547
+ for g in signin_data['groups']:
1028
548
  if g['gid'] == group['gid']:
1029
549
  g['name'] = group['name']
1030
550
  g['parent'] = group['parent']
@@ -1032,7 +552,7 @@ class Web:
1032
552
  raise ValueError(f"signin_file is None.")
1033
553
  if self.logger.level == logging.DEBUG:
1034
554
  self.logger.debug(f"group_edit: {group} -> {self.signin_file}")
1035
- common.save_yml(self.signin_file, self.signin_file_data)
555
+ common.save_yml(self.signin_file, signin_data)
1036
556
 
1037
557
  def group_del(self, gid:int):
1038
558
  """
@@ -1041,7 +561,8 @@ class Web:
1041
561
  Args:
1042
562
  gid (int): グループID
1043
563
  """
1044
- if self.signin_file_data is None:
564
+ signin_data = self.signin.get_data()
565
+ if signin_data is None:
1045
566
  raise ValueError(f'signin_file_data is None. ({self.signin_file})')
1046
567
  if self.signin_file is None:
1047
568
  raise ValueError(f"signin_file is None.")
@@ -1051,41 +572,41 @@ class Web:
1051
572
  raise ValueError(f"Group gid is not number. ({gid})")
1052
573
  # グループがユーザーに使用されているかチェック
1053
574
  user_group_ids = []
1054
- for user in self.signin_file_data['users']:
575
+ for user in signin_data['users']:
1055
576
  for group in user['groups']:
1056
- user_group_ids += [g['gid'] for g in self.signin_file_data['groups'] if g['name'] == group]
577
+ user_group_ids += [g['gid'] for g in signin_data['groups'] if g['name'] == group]
1057
578
  if gid in user_group_ids:
1058
579
  raise ValueError(f"Group gid is used by user. ({gid})")
1059
580
  # グループが親グループに使用されているかチェック
1060
581
  parent_group_ids = []
1061
- for group in self.signin_file_data['groups']:
582
+ for group in signin_data['groups']:
1062
583
  if 'parent' in group:
1063
- parent_group_ids += [g['gid'] for g in self.signin_file_data['groups'] if g['name'] == group['parent']]
584
+ parent_group_ids += [g['gid'] for g in signin_data['groups'] if g['name'] == group['parent']]
1064
585
  if gid in parent_group_ids:
1065
586
  raise ValueError(f"Group gid is used by parent group. ({gid})")
1066
587
  # グループがcmdruleグループに使用されているかチェック
1067
588
  cmdrule_group_ids = []
1068
- for rule in self.signin_file_data['cmdrule']['rules']:
589
+ for rule in signin_data['cmdrule']['rules']:
1069
590
  for group in rule['groups']:
1070
- cmdrule_group_ids += [g['gid'] for g in self.signin_file_data['groups'] if g['name'] == group]
591
+ cmdrule_group_ids += [g['gid'] for g in signin_data['groups'] if g['name'] == group]
1071
592
  if gid in cmdrule_group_ids:
1072
593
  raise ValueError(f"Group gid is used by cmdrule group. ({gid})")
1073
594
  # グループがpathruleグループに使用されているかチェック
1074
595
  pathrule_group_ids = []
1075
- for rule in self.signin_file_data['pathrule']['rules']:
596
+ for rule in signin_data['pathrule']['rules']:
1076
597
  for group in rule['groups']:
1077
- pathrule_group_ids += [g['gid'] for g in self.signin_file_data['groups'] if g['name'] == group]
598
+ pathrule_group_ids += [g['gid'] for g in signin_data['groups'] if g['name'] == group]
1078
599
  if gid in pathrule_group_ids:
1079
600
  raise ValueError(f"Group gid is used by pathrule group. ({gid})")
1080
601
 
1081
602
  # グループ削除
1082
- groups = [g for g in self.signin_file_data['groups'] if g['gid'] != gid]
1083
- if len(groups) == len(self.signin_file_data['groups']):
603
+ groups = [g for g in signin_data['groups'] if g['gid'] != gid]
604
+ if len(groups) == len(signin_data['groups']):
1084
605
  raise ValueError(f"Group gid is not found. ({gid})")
1085
- self.signin_file_data['groups'] = groups
606
+ signin_data['groups'] = groups
1086
607
  if self.logger.level == logging.DEBUG:
1087
608
  self.logger.debug(f"group_del: {gid} -> {self.signin_file}")
1088
- common.save_yml(self.signin_file, self.signin_file_data)
609
+ common.save_yml(self.signin_file, signin_data)
1089
610
 
1090
611
  def user_data(self, req:Request, uid:str, user_name:str, categoly:str, key:str=None, val:Any=None, delkey:bool=False) -> Any:
1091
612
  """
@@ -1137,7 +658,7 @@ class Web:
1137
658
 
1138
659
  def start(self, allow_host:str="0.0.0.0", listen_port:int=8081, ssl_listen_port:int=8443,
1139
660
  ssl_cert:Path=None, ssl_key:Path=None, ssl_keypass:str=None, ssl_ca_certs:Path=None,
1140
- session_domain:str=None, session_path:str='/', session_secure:bool=False, session_timeout:int=600, outputs_key:List[str]=[],
661
+ session_domain:str=None, session_path:str='/', session_secure:bool=False, session_timeout:int=900, outputs_key:List[str]=[],
1141
662
  guvicorn_workers:int=-1, guvicorn_timeout:int=30):
1142
663
  """
1143
664
  Webサーバを起動する
@@ -1153,7 +674,7 @@ class Web:
1153
674
  session_domain (str, optional): セッションドメイン. Defaults to None.
1154
675
  session_path (str, optional): セッションパス. Defaults to '/'.
1155
676
  session_secure (bool, optional): セッションセキュア. Defaults to False.
1156
- session_timeout (int, optional): セッションタイムアウト. Defaults to 600.
677
+ session_timeout (int, optional): セッションタイムアウト. Defaults to 900.
1157
678
  outputs_key (list, optional): 出力キー. Defaults to [].
1158
679
  guvicorn_workers (int, optional): Gunicornワーカー数. Defaults to -1.
1159
680
  guvicorn_timeout (int, optional): Gunicornタイムアウト. Defaults to 30.