cmdbox 0.5.3.1__py3-none-any.whl → 0.5.4__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.
- cmdbox/app/auth/__init__.py +0 -0
- cmdbox/app/auth/azure_signin.py +38 -0
- cmdbox/app/auth/azure_signin_saml.py +12 -0
- cmdbox/app/auth/github_signin.py +38 -0
- cmdbox/app/auth/google_signin.py +32 -0
- cmdbox/app/auth/signin.py +47 -4
- cmdbox/app/auth/signin_saml.py +61 -0
- cmdbox/app/edge.py +198 -61
- cmdbox/app/feature.py +0 -1
- cmdbox/app/features/cli/cmdbox_edge_config.py +19 -5
- cmdbox/app/features/cli/cmdbox_web_user_add.py +3 -3
- cmdbox/app/features/cli/cmdbox_web_user_edit.py +3 -3
- cmdbox/app/features/web/cmdbox_web_do_signin.py +79 -103
- cmdbox/app/features/web/cmdbox_web_signin.py +23 -1
- cmdbox/app/web.py +13 -12
- cmdbox/extensions/sample_project/sample/extensions/features.yml +23 -0
- cmdbox/extensions/sample_project/sample/extensions/user_list.yml +40 -6
- cmdbox/extensions/user_list.yml +36 -6
- cmdbox/licenses/{LICENSE.python-multipart.0.0.17(Apache Software License).txt → LICENSE.async-timeout.5.0.1(Apache Software License).txt } +2 -3
- cmdbox/licenses/files.txt +10 -9
- cmdbox/version.py +2 -2
- cmdbox/web/assets/cmdbox/signin.js +13 -0
- cmdbox/web/assets/cmdbox/users.js +1 -1
- cmdbox/web/signin.html +10 -6
- {cmdbox-0.5.3.1.dist-info → cmdbox-0.5.4.dist-info}/METADATA +64 -10
- {cmdbox-0.5.3.1.dist-info → cmdbox-0.5.4.dist-info}/RECORD +39 -83
- cmdbox/app/features/web/cmdbox_web_load_pin.py +0 -43
- cmdbox/app/features/web/cmdbox_web_save_pin.py +0 -42
- cmdbox/licenses/LICENSE.Jinja2.3.1.4(BSD License).txt +0 -28
- cmdbox/licenses/LICENSE.Pygments.2.18.0(BSD License).txt +0 -25
- cmdbox/licenses/LICENSE.Sphinx.8.1.3(BSD License).txt +0 -31
- cmdbox/licenses/LICENSE.anyio.4.6.2.post1(MIT License).txt +0 -20
- cmdbox/licenses/LICENSE.argcomplete.3.5.1(Apache Software License).txt +0 -177
- cmdbox/licenses/LICENSE.argcomplete.3.6.1(Apache Software License).txt +0 -177
- cmdbox/licenses/LICENSE.babel.2.16.0(BSD License).txt +0 -27
- cmdbox/licenses/LICENSE.certifi.2025.1.31(Mozilla Public License 2.0 (MPL 2.0)).txt +0 -20
- cmdbox/licenses/LICENSE.charset-normalizer.3.4.0(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.click.8.1.7(BSD License).txt +0 -28
- cmdbox/licenses/LICENSE.cryptography.43.0.3(Apache Software License; BSD License).txt +0 -3
- cmdbox/licenses/LICENSE.fastapi.0.115.5(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.gevent.25.4.1(MIT).txt +0 -25
- cmdbox/licenses/LICENSE.greenlet.3.2.0(MIT AND Python-2.0).txt +0 -30
- cmdbox/licenses/LICENSE.importlib_metadata.8.6.1(Apache Software License).txt +0 -202
- cmdbox/licenses/LICENSE.keyring.25.5.0(MIT License).txt +0 -17
- cmdbox/licenses/LICENSE.more-itertools.10.6.0(MIT License).txt +0 -19
- cmdbox/licenses/LICENSE.nh3.0.2.18(MIT).txt +0 -1
- cmdbox/licenses/LICENSE.numpy.2.2.4(BSD License).txt +0 -950
- cmdbox/licenses/LICENSE.pillow.11.0.0(CMU License (MIT-CMU)).txt +0 -1226
- cmdbox/licenses/LICENSE.pillow.11.1.0(CMU License (MIT-CMU)).txt +0 -1213
- cmdbox/licenses/LICENSE.pkginfo.1.10.0(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.prettytable.3.12.0(BSD License).txt +0 -30
- cmdbox/licenses/LICENSE.prompt_toolkit.3.0.50(BSD License).txt +0 -27
- cmdbox/licenses/LICENSE.psycopg-pool.3.2.6(GNU Lesser General Public License v3 (LGPLv3)).txt +0 -165
- cmdbox/licenses/LICENSE.pydantic.2.10.2(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.pydantic.2.11.1(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.pydantic_core.2.27.1(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.pydantic_core.2.33.0(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.python-dotenv.1.0.1(BSD License).txt +0 -27
- cmdbox/licenses/LICENSE.redis.5.2.0(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.rich.13.9.4(MIT License).txt +0 -19
- cmdbox/licenses/LICENSE.six.1.16.0(MIT License).txt +0 -18
- cmdbox/licenses/LICENSE.sphinx-intl.2.3.0(BSD License).txt +0 -25
- cmdbox/licenses/LICENSE.starlette.0.41.3(BSD License).txt +0 -27
- cmdbox/licenses/LICENSE.starlette.0.46.1(BSD License).txt +0 -27
- cmdbox/licenses/LICENSE.tomli.2.1.0(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.twine.5.1.1(Apache Software License).txt +0 -174
- cmdbox/licenses/LICENSE.typing_extensions.4.12.2(Python Software Foundation License).txt +0 -279
- cmdbox/licenses/LICENSE.typing_extensions.4.13.0(UNKNOWN).txt +0 -279
- cmdbox/licenses/LICENSE.urllib3.2.2.3(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.urllib3.2.3.0(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.uvicorn.0.34.0(BSD License).txt +0 -27
- cmdbox/licenses/LICENSE.uvicorn.0.34.1(BSD License).txt +0 -27
- cmdbox/licenses/LICENSE.watchfiles.1.0.0(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.watchfiles.1.0.4(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.websockets.14.1(BSD License).txt +0 -24
- cmdbox/licenses/LICENSE.zope.interface.7.1.1(Zope Public License).txt +0 -44
- /cmdbox/licenses/{LICENSE.certifi.2024.8.30(Mozilla Public License 2.0 (MPL 2.0)).txt → LICENSE.certifi.2025.4.26(Mozilla Public License 2.0 (MPL 2.0)).txt} +0 -0
- /cmdbox/licenses/{LICENSE.gevent.24.11.1(MIT License).txt → LICENSE.gevent.25.4.2(MIT).txt} +0 -0
- /cmdbox/licenses/{LICENSE.greenlet.3.1.1(MIT License).txt → LICENSE.greenlet.3.2.1(MIT AND Python-2.0).txt} +0 -0
- /cmdbox/licenses/{LICENSE.h11.0.14.0(MIT License).txt → LICENSE.h11.0.16.0(MIT License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.importlib_metadata.8.5.0(Apache Software License).txt → LICENSE.importlib_metadata.8.7.0(Apache Software License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.more-itertools.10.5.0(MIT License).txt → LICENSE.more-itertools.10.7.0(MIT License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.numpy.2.1.3(BSD License).txt → LICENSE.numpy.2.2.5(BSD License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.packaging.24.2(Apache Software License; BSD License).txt → LICENSE.packaging.25.0(Apache Software License; BSD License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.uvicorn.0.32.1(BSD License).txt → LICENSE.uvicorn.0.34.2(BSD License).txt} +0 -0
- {cmdbox-0.5.3.1.dist-info → cmdbox-0.5.4.dist-info}/LICENSE +0 -0
- {cmdbox-0.5.3.1.dist-info → cmdbox-0.5.4.dist-info}/WHEEL +0 -0
- {cmdbox-0.5.3.1.dist-info → cmdbox-0.5.4.dist-info}/entry_points.txt +0 -0
- {cmdbox-0.5.3.1.dist-info → cmdbox-0.5.4.dist-info}/top_level.txt +0 -0
|
@@ -1,18 +1,19 @@
|
|
|
1
|
+
import urllib.parse
|
|
1
2
|
from cmdbox.app import common
|
|
2
|
-
from cmdbox.app.auth
|
|
3
|
+
from cmdbox.app.auth import signin, signin_saml, azure_signin, azure_signin_saml, github_signin, google_signin
|
|
3
4
|
from cmdbox.app.commons import convert
|
|
4
5
|
from cmdbox.app.features.web import cmdbox_web_signin
|
|
5
6
|
from cmdbox.app.web import Web
|
|
6
7
|
from fastapi import FastAPI, Request, Response, HTTPException
|
|
7
8
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
|
9
|
+
from typing import Any, Dict
|
|
8
10
|
import copy
|
|
9
11
|
import datetime
|
|
10
12
|
import importlib
|
|
11
13
|
import inspect
|
|
12
14
|
import json
|
|
13
15
|
import logging
|
|
14
|
-
import
|
|
15
|
-
import urllib.parse
|
|
16
|
+
import urllib
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class DoSignin(cmdbox_web_signin.Signin):
|
|
@@ -60,7 +61,7 @@ class DoSignin(cmdbox_web_signin.Signin):
|
|
|
60
61
|
if name == '' or passwd == '':
|
|
61
62
|
web.options.audit_exec(req, res, web, body=dict(msg='signin failed.'), audit_type='auth')
|
|
62
63
|
return RedirectResponse(url=f'/signin/{next}?error=1')
|
|
63
|
-
user = [u for u in signin_data['users'] if u['name'] == name and u['hash'] != 'oauth2']
|
|
64
|
+
user = [u for u in signin_data['users'] if u['name'] == name and u['hash'] != 'oauth2' and u['hash'] != 'saml']
|
|
64
65
|
if len(user) <= 0:
|
|
65
66
|
web.options.audit_exec(req, res, web, body=dict(msg='signin failed.'), audit_type='auth')
|
|
66
67
|
return RedirectResponse(url=f'/signin/{next}?error=1')
|
|
@@ -108,7 +109,7 @@ class DoSignin(cmdbox_web_signin.Signin):
|
|
|
108
109
|
last_update = web.user_data(None, uid, name, 'password', 'last_update')
|
|
109
110
|
notify_passchange = True if last_update is None else False
|
|
110
111
|
# パスワード認証の場合はパスワード有効期限チェック
|
|
111
|
-
if user['hash']!='oauth2' and 'password' in signin_data and not notify_passchange:
|
|
112
|
+
if user['hash']!='oauth2' and user['hash']!='saml' and 'password' in signin_data and not notify_passchange:
|
|
112
113
|
last_update = datetime.datetime.strptime(last_update, '%Y-%m-%dT%H:%M:%S')
|
|
113
114
|
# パスワード有効期限
|
|
114
115
|
expiration = signin_data['password']['expiration']
|
|
@@ -152,7 +153,7 @@ class DoSignin(cmdbox_web_signin.Signin):
|
|
|
152
153
|
members = inspect.getmembers(mod, inspect.isclass)
|
|
153
154
|
signin_data = web.signin.get_data()
|
|
154
155
|
for name, cls in members:
|
|
155
|
-
if cls is Signin or issubclass(cls, Signin):
|
|
156
|
+
if cls is signin.Signin or issubclass(cls, signin.Signin):
|
|
156
157
|
sobj = cls(web.logger, web.signin_file, signin_data, appcls, ver)
|
|
157
158
|
return sobj
|
|
158
159
|
return None
|
|
@@ -161,9 +162,10 @@ class DoSignin(cmdbox_web_signin.Signin):
|
|
|
161
162
|
raise e
|
|
162
163
|
|
|
163
164
|
signin_data = web.signin.get_data()
|
|
164
|
-
self.google_signin =
|
|
165
|
-
self.github_signin =
|
|
166
|
-
self.azure_signin =
|
|
165
|
+
self.google_signin = google_signin.GoogleSignin(web.logger, web.signin_file, signin_data, self.appcls, self.ver)
|
|
166
|
+
self.github_signin = github_signin.GithubSignin(web.logger, web.signin_file, signin_data, self.appcls, self.ver)
|
|
167
|
+
self.azure_signin = azure_signin.AzureSignin(web.logger, web.signin_file, signin_data, self.appcls, self.ver)
|
|
168
|
+
self.azure_saml_signin = azure_signin_saml.AzyreSigninSAML(web.logger, web.signin_file, signin_data, self.appcls, self.ver)
|
|
167
169
|
if signin_data is not None:
|
|
168
170
|
# signinオブジェクトの指定があった場合読込む
|
|
169
171
|
if 'signin_module' in signin_data['oauth2']['providers']['google']:
|
|
@@ -175,6 +177,9 @@ class DoSignin(cmdbox_web_signin.Signin):
|
|
|
175
177
|
if 'signin_module' in signin_data['oauth2']['providers']['azure']:
|
|
176
178
|
sobj = _load_signin(web, signin_data['oauth2']['providers']['azure']['signin_module'], self.appcls, self.ver)
|
|
177
179
|
self.azure_signin = sobj if sobj is not None else self.azure_signin
|
|
180
|
+
if 'signin_module' in signin_data['saml']['providers']['azure']:
|
|
181
|
+
sobj = _load_signin(web, signin_data['saml']['providers']['azure']['signin_module'], self.appcls, self.ver)
|
|
182
|
+
self.azure_saml_signin = sobj if sobj is not None else self.azure_saml_signin
|
|
178
183
|
|
|
179
184
|
def _set_session(req:Request, user:dict, email:str, hashed_password:str, access_token:str, group_names:list, gids:list):
|
|
180
185
|
"""
|
|
@@ -209,20 +214,10 @@ class DoSignin(cmdbox_web_signin.Signin):
|
|
|
209
214
|
@app.get('/oauth2/google/callback')
|
|
210
215
|
async def oauth2_google_callback(req:Request, res:Response):
|
|
211
216
|
conf = web.signin.get_data()['oauth2']['providers']['google']
|
|
212
|
-
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
|
213
217
|
next = req.query_params['state']
|
|
214
|
-
data = {'code': req.query_params['code'],
|
|
215
|
-
'client_id': conf['client_id'],
|
|
216
|
-
'client_secret': conf['client_secret'],
|
|
217
|
-
'redirect_uri': conf['redirect_uri'],
|
|
218
|
-
'grant_type': 'authorization_code'}
|
|
219
|
-
query = '&'.join([f'{k}={urllib.parse.quote(v)}' for k, v in data.items()])
|
|
220
218
|
try:
|
|
221
219
|
# アクセストークン取得
|
|
222
|
-
|
|
223
|
-
token_resp.raise_for_status()
|
|
224
|
-
token_json = token_resp.json()
|
|
225
|
-
access_token = token_json['access_token']
|
|
220
|
+
access_token = self.google_signin.request_access_token(conf, req, res)
|
|
226
221
|
return await oauth2_google_session(access_token, next, req, res)
|
|
227
222
|
except Exception as e:
|
|
228
223
|
web.logger.warning(f'Failed to get token. {e}', exc_info=True)
|
|
@@ -230,45 +225,15 @@ class DoSignin(cmdbox_web_signin.Signin):
|
|
|
230
225
|
|
|
231
226
|
@app.get('/oauth2/google/session/{access_token}/{next}')
|
|
232
227
|
async def oauth2_google_session(access_token:str, next:str, req:Request, res:Response):
|
|
233
|
-
|
|
234
|
-
# ユーザー情報取得(email)
|
|
235
|
-
user_info_resp = requests.get(
|
|
236
|
-
url='https://www.googleapis.com/oauth2/v1/userinfo',
|
|
237
|
-
headers={'Authorization': f'Bearer {access_token}'}
|
|
238
|
-
)
|
|
239
|
-
user_info_resp.raise_for_status()
|
|
240
|
-
user_info_json = user_info_resp.json()
|
|
241
|
-
email = user_info_json['email']
|
|
242
|
-
# サインイン判定
|
|
243
|
-
jadge, user = self.google_signin.jadge(access_token, email)
|
|
244
|
-
if not jadge:
|
|
245
|
-
return RedirectResponse(url=f'/signin/{next}?error=appdeny')
|
|
246
|
-
# グループ取得
|
|
247
|
-
group_names, gids = self.google_signin.get_groups(access_token, user)
|
|
248
|
-
# セッションに保存
|
|
249
|
-
_set_session(req, user, email, None, access_token, group_names, gids)
|
|
250
|
-
return RedirectResponse(url=f'../../{next}', headers=dict(signin="success")) # nginxのリバプロ対応のための相対パス
|
|
251
|
-
except Exception as e:
|
|
252
|
-
web.logger.warning(f'Failed to get token. {e}', exc_info=True)
|
|
253
|
-
raise HTTPException(status_code=500, detail=f'Failed to get token. {e}')
|
|
228
|
+
return await oauth2_login_session(self.google_signin, access_token, next, req, res)
|
|
254
229
|
|
|
255
230
|
@app.get('/oauth2/github/callback')
|
|
256
231
|
async def oauth2_github_callback(req:Request, res:Response):
|
|
257
232
|
conf = web.signin.get_data()['oauth2']['providers']['github']
|
|
258
|
-
headers = {'Content-Type': 'application/x-www-form-urlencoded',
|
|
259
|
-
'Accept': 'application/json'}
|
|
260
233
|
next = req.query_params['state']
|
|
261
|
-
data = {'code': req.query_params['code'],
|
|
262
|
-
'client_id': conf['client_id'],
|
|
263
|
-
'client_secret': conf['client_secret'],
|
|
264
|
-
'redirect_uri': conf['redirect_uri']}
|
|
265
|
-
query = '&'.join([f'{k}={urllib.parse.quote(v)}' for k, v in data.items()])
|
|
266
234
|
try:
|
|
267
235
|
# アクセストークン取得
|
|
268
|
-
|
|
269
|
-
token_resp.raise_for_status()
|
|
270
|
-
token_json = token_resp.json()
|
|
271
|
-
access_token = token_json['access_token']
|
|
236
|
+
access_token = self.github_signin.request_access_token(conf, req, res)
|
|
272
237
|
return await oauth2_github_session(access_token, next, req, res)
|
|
273
238
|
except Exception as e:
|
|
274
239
|
web.logger.warning(f'Failed to get token. {e}', exc_info=True)
|
|
@@ -276,53 +241,15 @@ class DoSignin(cmdbox_web_signin.Signin):
|
|
|
276
241
|
|
|
277
242
|
@app.get('/oauth2/github/session/{access_token}/{next}')
|
|
278
243
|
async def oauth2_github_session(access_token:str, next:str, req:Request, res:Response):
|
|
279
|
-
|
|
280
|
-
# ユーザー情報取得(email)
|
|
281
|
-
user_info_resp = requests.get(
|
|
282
|
-
url='https://api.github.com/user/emails',
|
|
283
|
-
headers={'Authorization': f'Bearer {access_token}'}
|
|
284
|
-
)
|
|
285
|
-
user_info_resp.raise_for_status()
|
|
286
|
-
user_info_json = user_info_resp.json()
|
|
287
|
-
if type(user_info_json) == list:
|
|
288
|
-
email = 'notfound'
|
|
289
|
-
for u in user_info_json:
|
|
290
|
-
if u['primary']:
|
|
291
|
-
email = u['email']
|
|
292
|
-
break
|
|
293
|
-
# サインイン判定
|
|
294
|
-
jadge, user = self.github_signin.jadge(access_token, email)
|
|
295
|
-
if not jadge:
|
|
296
|
-
return RedirectResponse(url=f'/signin/{next}?error=appdeny')
|
|
297
|
-
# グループ取得
|
|
298
|
-
group_names, gids = self.github_signin.get_groups(access_token, user)
|
|
299
|
-
# セッションに保存
|
|
300
|
-
_set_session(req, user, email, None, access_token, group_names, gids)
|
|
301
|
-
return RedirectResponse(url=f'../../{next}', headers=dict(signin="success")) # nginxのリバプロ対応のための相対パス
|
|
302
|
-
except Exception as e:
|
|
303
|
-
web.logger.warning(f'Failed to get token. {e}', exc_info=True)
|
|
304
|
-
raise HTTPException(status_code=500, detail=f'Failed to get token. {e}')
|
|
244
|
+
return await oauth2_login_session(self.github_signin, access_token, next, req, res)
|
|
305
245
|
|
|
306
246
|
@app.get('/oauth2/azure/callback')
|
|
307
247
|
async def oauth2_azure_callback(req:Request, res:Response):
|
|
308
248
|
conf = web.signin.get_data()['oauth2']['providers']['azure']
|
|
309
|
-
headers = {'Content-Type': 'application/x-www-form-urlencoded',
|
|
310
|
-
'Accept': 'application/json'}
|
|
311
249
|
next = req.query_params['state']
|
|
312
|
-
data = {'tenant': conf['tenant_id'],
|
|
313
|
-
'code': req.query_params['code'],
|
|
314
|
-
'scope': " ".join(conf['scope']),
|
|
315
|
-
'client_id': conf['client_id'],
|
|
316
|
-
#'client_secret': conf['client_secret'],
|
|
317
|
-
'redirect_uri': conf['redirect_uri'],
|
|
318
|
-
'grant_type': 'authorization_code'}
|
|
319
|
-
query = '&'.join([f'{k}={urllib.parse.quote(v)}' for k, v in data.items()])
|
|
320
250
|
try:
|
|
321
251
|
# アクセストークン取得
|
|
322
|
-
|
|
323
|
-
token_resp.raise_for_status()
|
|
324
|
-
token_json = token_resp.json()
|
|
325
|
-
access_token = token_json['access_token']
|
|
252
|
+
access_token = self.azure_signin.request_access_token(conf, req, res)
|
|
326
253
|
return await oauth2_azure_session(access_token, next, req, res)
|
|
327
254
|
except Exception as e:
|
|
328
255
|
web.logger.warning(f'Failed to get token. {e}', exc_info=True)
|
|
@@ -330,26 +257,75 @@ class DoSignin(cmdbox_web_signin.Signin):
|
|
|
330
257
|
|
|
331
258
|
@app.get('/oauth2/azure/session/{access_token}/{next}')
|
|
332
259
|
async def oauth2_azure_session(access_token:str, next:str, req:Request, res:Response):
|
|
260
|
+
return await oauth2_login_session(self.azure_signin, access_token, next, req, res)
|
|
261
|
+
|
|
262
|
+
async def oauth2_login_session(signin:signin.Signin, access_token:str, next:str, req:Request, res:Response):
|
|
333
263
|
try:
|
|
334
264
|
# ユーザー情報取得(email)
|
|
335
|
-
|
|
336
|
-
url='https://graph.microsoft.com/v1.0/me',
|
|
337
|
-
#url='https://graph.microsoft.com/v1.0/me/transitiveMemberOf?$Top=999',
|
|
338
|
-
headers={'Authorization': f'Bearer {access_token}'}
|
|
339
|
-
)
|
|
340
|
-
user_info_resp.raise_for_status()
|
|
341
|
-
user_info_json = user_info_resp.json()
|
|
342
|
-
if isinstance(user_info_json, dict):
|
|
343
|
-
email = user_info_json.get('mail', 'notfound')
|
|
265
|
+
email = signin.get_email(access_token)
|
|
344
266
|
# サインイン判定
|
|
345
|
-
jadge, user =
|
|
267
|
+
jadge, user = signin.jadge(email)
|
|
346
268
|
if not jadge:
|
|
347
269
|
return RedirectResponse(url=f'/signin/{next}?error=appdeny')
|
|
348
270
|
# グループ取得
|
|
349
|
-
group_names, gids =
|
|
271
|
+
group_names, gids = signin.get_groups(access_token, user)
|
|
350
272
|
# セッションに保存
|
|
351
273
|
_set_session(req, user, email, None, access_token, group_names, gids)
|
|
352
274
|
return RedirectResponse(url=f'../../{next}', headers=dict(signin="success")) # nginxのリバプロ対応のための相対パス
|
|
353
275
|
except Exception as e:
|
|
354
276
|
web.logger.warning(f'Failed to get token. {e}', exc_info=True)
|
|
355
277
|
raise HTTPException(status_code=500, detail=f'Failed to get token. {e}')
|
|
278
|
+
|
|
279
|
+
@app.post('/saml/azure/callback')
|
|
280
|
+
async def saml_azure_callback(req:Request, res:Response):
|
|
281
|
+
form = await req.form()
|
|
282
|
+
return await saml_login_callback('azure', self.azure_saml_signin, form, None, req, res)
|
|
283
|
+
|
|
284
|
+
@app.get('/saml/azure/session/{saml_token}/{next}')
|
|
285
|
+
async def saml_azure_session(saml_token:str, next:str, req:Request, res:Response):
|
|
286
|
+
form = json.loads(convert.b64str2str(saml_token))
|
|
287
|
+
return await saml_login_callback('azure', self.azure_saml_signin, form, next, req, res)
|
|
288
|
+
|
|
289
|
+
async def saml_login_callback(prov, saml_signin:signin_saml.SigninSAML, form:Dict[str, Any], next:str, req:Request, res:Response):
|
|
290
|
+
"""
|
|
291
|
+
SAML認証のコールバック処理を行います
|
|
292
|
+
Args:
|
|
293
|
+
prov (str): SAMLプロバイダ名
|
|
294
|
+
saml_signin (signin_saml.SigninSAML): SAMLサインインオブジェクト
|
|
295
|
+
form (Dict[str, Any]): フォームデータ
|
|
296
|
+
req (Request): Requestオブジェクト
|
|
297
|
+
res (Response): Responseオブジェクト
|
|
298
|
+
"""
|
|
299
|
+
relay = form.get('RelayState')
|
|
300
|
+
query = urllib.parse.urlparse(relay).query if relay is not None else None
|
|
301
|
+
if next is None:
|
|
302
|
+
next = urllib.parse.parse_qs(query).get('next', None) if query is not None else None
|
|
303
|
+
next = next[0] if next is not None and len(next) > 0 else None
|
|
304
|
+
auth = await saml_signin.make_saml(prov, next, form, req, res)
|
|
305
|
+
auth.process_response() # Process IdP response
|
|
306
|
+
errors = auth.get_errors() # This method receives an array with the errors
|
|
307
|
+
if len(errors) == 0:
|
|
308
|
+
if not auth.is_authenticated(): # This check if the response was ok and the user data retrieved or not (user authenticated)
|
|
309
|
+
return RedirectResponse(url=f'/signin/{next}?error=saml_not_auth')
|
|
310
|
+
else:
|
|
311
|
+
# ユーザー情報取得
|
|
312
|
+
email = saml_signin.get_email(auth)
|
|
313
|
+
# サインイン判定
|
|
314
|
+
jadge, user = saml_signin.jadge(email)
|
|
315
|
+
if not jadge:
|
|
316
|
+
return RedirectResponse(url=f'/signin/{next}?error=appdeny')
|
|
317
|
+
# グループ取得
|
|
318
|
+
group_names, gids = saml_signin.get_groups(None, user)
|
|
319
|
+
# セッションに保存
|
|
320
|
+
_set_session(req, user, email, None, None, group_names, gids)
|
|
321
|
+
# SAML場合、ブラウザ制限によりリダイレクトでセッションクッキーが消えるので、HTMLで移動する
|
|
322
|
+
html = """
|
|
323
|
+
<html><head><meta http-equiv="refresh" content="0;url=../../{next}"></head>
|
|
324
|
+
<body style="background-color:#212529;color:#fff;">loading..</body>
|
|
325
|
+
<script type="text/javascript">window.location.href="../../{next}";</script></html>
|
|
326
|
+
""".format(next=next)
|
|
327
|
+
return HTMLResponse(content=html, headers=dict(signin="success"))
|
|
328
|
+
else:
|
|
329
|
+
msg = f"Error when processing SAML Response: {', '.join(errors)} {auth.get_last_error_reason()}"
|
|
330
|
+
web.logger.warning(msg)
|
|
331
|
+
raise HTTPException(status_code=500, detail=msg)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import urllib.parse
|
|
2
1
|
from cmdbox.app import feature
|
|
3
2
|
from cmdbox.app.web import Web
|
|
4
3
|
from fastapi import FastAPI, Request, Response, HTTPException
|
|
5
4
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
|
6
5
|
import urllib
|
|
6
|
+
import urllib.parse
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class Signin(feature.WebFeature):
|
|
@@ -83,3 +83,25 @@ class Signin(feature.WebFeature):
|
|
|
83
83
|
return dict(google=signin_data['oauth2']['providers']['google']['enabled'],
|
|
84
84
|
github=signin_data['oauth2']['providers']['github']['enabled'],
|
|
85
85
|
azure=signin_data['oauth2']['providers']['azure']['enabled'],)
|
|
86
|
+
|
|
87
|
+
@app.get('/saml/{prov}/{next}')
|
|
88
|
+
async def saml_login(prov:str, next:str, req:Request, res:Response):
|
|
89
|
+
"""
|
|
90
|
+
SAML認証のログイン処理を行います
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
prov (str): SAMLプロバイダ名
|
|
94
|
+
next (str): リダイレクト先のURL
|
|
95
|
+
req (Request): Requestオブジェクト
|
|
96
|
+
res (Response): Responseオブジェクト
|
|
97
|
+
"""
|
|
98
|
+
form = await req.form()
|
|
99
|
+
auth = await web.signin_saml.make_saml(prov, next, form, req, res)
|
|
100
|
+
return RedirectResponse(url=auth.login())
|
|
101
|
+
|
|
102
|
+
@app.get('/saml/enabled')
|
|
103
|
+
async def saml_enabled(req:Request, res:Response):
|
|
104
|
+
if web.signin_html_data is None:
|
|
105
|
+
return dict(azure=False)
|
|
106
|
+
signin_data = web.signin_saml.get_data()
|
|
107
|
+
return dict(azure=signin_data['saml']['providers']['azure']['enabled'],)
|
cmdbox/app/web.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from cmdbox.app import common, options
|
|
2
|
-
from cmdbox.app.auth
|
|
2
|
+
from cmdbox.app.auth import signin, signin_saml
|
|
3
3
|
from cmdbox.app.commons import module
|
|
4
4
|
from fastapi import FastAPI, Request, Response, HTTPException
|
|
5
5
|
from fastapi.responses import RedirectResponse
|
|
@@ -116,8 +116,9 @@ class Web:
|
|
|
116
116
|
self.cb_queue = queue.Queue(1000)
|
|
117
117
|
self.options = options.Options.getInstance()
|
|
118
118
|
self.webcap_client = requests.Session()
|
|
119
|
-
signin_file_data = Signin.load_signin_file(self.signin_file)
|
|
120
|
-
self.signin = Signin(self.logger, self.signin_file, signin_file_data, self.appcls, self.ver)
|
|
119
|
+
signin_file_data = signin.Signin.load_signin_file(self.signin_file)
|
|
120
|
+
self.signin = signin.Signin(self.logger, self.signin_file, signin_file_data, self.appcls, self.ver)
|
|
121
|
+
self.signin_saml = signin_saml.SigninSAML(self.logger, self.signin_file, signin_file_data, self.appcls, self.ver)
|
|
121
122
|
|
|
122
123
|
if self.logger.level == logging.DEBUG:
|
|
123
124
|
self.logger.debug(f"web init parameter: data={self.data} -> {self.data.absolute() if self.data is not None else None}")
|
|
@@ -348,12 +349,12 @@ class Web:
|
|
|
348
349
|
if 'hash' not in user or user['hash'] == '':
|
|
349
350
|
raise ValueError(f"User hash is not found or empty. ({user})")
|
|
350
351
|
hash = user['hash']
|
|
351
|
-
if hash!='oauth2' and ('password' not in user or user['password'] == ''):
|
|
352
|
+
if hash!='oauth2' and hash!='saml' and ('password' not in user or user['password'] == ''):
|
|
352
353
|
raise ValueError(f"User password is not found or empty. ({user})")
|
|
353
354
|
if 'email' not in user:
|
|
354
355
|
raise ValueError(f"User email is not found. ({user})")
|
|
355
|
-
if hash=='oauth2' and (user['email'] is None or user['email']==''):
|
|
356
|
-
raise ValueError(f"Required when `email` is `oauth2`. ({user})")
|
|
356
|
+
if (hash=='oauth2' or hash=='saml') and (user['email'] is None or user['email']==''):
|
|
357
|
+
raise ValueError(f"Required when `email` is `oauth2` or `saml`. ({user})")
|
|
357
358
|
if 'groups' not in user or type(user['groups']) is not list:
|
|
358
359
|
raise ValueError(f"User groups is not found or empty. ({user})")
|
|
359
360
|
for gn in user['groups']:
|
|
@@ -363,13 +364,13 @@ class Web:
|
|
|
363
364
|
raise ValueError(f"User uid is already exists. ({user})")
|
|
364
365
|
if len([u for u in signin_data['users'] if u['name'] == user['name']]) > 0:
|
|
365
366
|
raise ValueError(f"User name is already exists. ({user})")
|
|
366
|
-
if hash not in ['oauth2', 'plain', 'md5', 'sha1', 'sha256']:
|
|
367
|
+
if hash not in ['oauth2', 'saml', 'plain', 'md5', 'sha1', 'sha256']:
|
|
367
368
|
raise ValueError(f"User hash is not supported. ({user})")
|
|
368
369
|
jadge, msg = self.signin.check_password_policy(user['name'], '', user['password'])
|
|
369
370
|
if not jadge:
|
|
370
371
|
raise ValueError(msg)
|
|
371
372
|
if hash != 'plain':
|
|
372
|
-
user['password'] = common.hash_password(user['password'], hash if hash != 'oauth2' else 'sha1')
|
|
373
|
+
user['password'] = common.hash_password(user['password'], hash if hash != 'oauth2' and hash != 'saml' else 'sha1')
|
|
373
374
|
else:
|
|
374
375
|
user['password'] = user['password']
|
|
375
376
|
signin_data['users'].append(user)
|
|
@@ -405,8 +406,8 @@ class Web:
|
|
|
405
406
|
if 'email' not in user:
|
|
406
407
|
raise ValueError(f"User email is not found. ({user})")
|
|
407
408
|
hash = user['hash']
|
|
408
|
-
if hash=='oauth2' and (user['email'] is None or user['email']==''):
|
|
409
|
-
raise ValueError(f"Required when `email` is `oauth2`. ({user})")
|
|
409
|
+
if (hash=='oauth2' or hash=='saml') and (user['email'] is None or user['email']==''):
|
|
410
|
+
raise ValueError(f"Required when `email` is `oauth2` or `saml`. ({user})")
|
|
410
411
|
if 'groups' not in user or type(user['groups']) is not list:
|
|
411
412
|
raise ValueError(f"User groups is not found or empty. ({user})")
|
|
412
413
|
for gn in user['groups']:
|
|
@@ -416,7 +417,7 @@ class Web:
|
|
|
416
417
|
raise ValueError(f"User uid is not found. ({user})")
|
|
417
418
|
if len([u for u in signin_data['users'] if u['name'] == user['name']]) <= 0:
|
|
418
419
|
raise ValueError(f"User name is not found. ({user})")
|
|
419
|
-
if hash not in ['oauth2', 'plain', 'md5', 'sha1', 'sha256']:
|
|
420
|
+
if hash not in ['oauth2', 'saml', 'plain', 'md5', 'sha1', 'sha256']:
|
|
420
421
|
raise ValueError(f"User hash is not supported. ({user})")
|
|
421
422
|
for u in signin_data['users']:
|
|
422
423
|
if u['uid'] == user['uid']:
|
|
@@ -426,7 +427,7 @@ class Web:
|
|
|
426
427
|
if not jadge:
|
|
427
428
|
raise ValueError(msg)
|
|
428
429
|
if hash != 'plain':
|
|
429
|
-
u['password'] = common.hash_password(user['password'], hash if hash != 'oauth2' else 'sha1')
|
|
430
|
+
u['password'] = common.hash_password(user['password'], hash if hash != 'oauth2' and hash != 'saml' else 'sha1')
|
|
430
431
|
else:
|
|
431
432
|
u['password'] = user['password']
|
|
432
433
|
# パスワード更新日時の保存
|
|
@@ -46,3 +46,26 @@ aliases: # Specify the alias for the specified co
|
|
|
46
46
|
# e.g. /{1}_exec
|
|
47
47
|
move: # Specify whether to move the regular expression group of the source to the target.
|
|
48
48
|
# e.g. true
|
|
49
|
+
audit:
|
|
50
|
+
enabled: true # Specify whether to enable the audit function.
|
|
51
|
+
write:
|
|
52
|
+
mode: audit # Specify the mode of the feature to be writed.
|
|
53
|
+
cmd: write # Specify the command to be writed.
|
|
54
|
+
search:
|
|
55
|
+
mode: audit # Specify the mode of the feature to be searched.
|
|
56
|
+
cmd: search # Specify the command to be searched.
|
|
57
|
+
options: # Specify the options for the audit function.
|
|
58
|
+
host: localhost # Specify the service host of the audit Redis server.However, if it is specified as a command line argument, it is ignored.
|
|
59
|
+
port: 6379 # Specify the service port of the audit Redis server.However, if it is specified as a command line argument, it is ignored.
|
|
60
|
+
password: password # Specify the access password of the audit Redis server.However, if it is specified as a command line argument, it is ignored.
|
|
61
|
+
svname: cmdbox # Specify the audit service name of the inference server.However, if it is specified as a command line argument, it is ignored.
|
|
62
|
+
retry_count: 3 # Specifies the number of reconnections to the audit Redis server.If less than 0 is specified, reconnection is forever.
|
|
63
|
+
retry_interval: 1 # Specifies the number of seconds before reconnecting to the audit Redis server.
|
|
64
|
+
timeout: 15 # Specify the maximum waiting time until the server responds.
|
|
65
|
+
pg_enabled: False # Specify True if using the postgresql database server.
|
|
66
|
+
pg_host: localhost # Specify the postgresql host.
|
|
67
|
+
pg_port: 5432 # Specify the postgresql port.
|
|
68
|
+
pg_user: postgres # Specify the postgresql user name.
|
|
69
|
+
pg_password: password # Specify the postgresql password.
|
|
70
|
+
pg_dbname: audit # Specify the postgresql database name.
|
|
71
|
+
retention_period_days: 365 # Specify the number of days to retain audit logs.
|
|
@@ -2,9 +2,9 @@ users: # A list of users, each of which is a map that co
|
|
|
2
2
|
- uid: 1 # An ID that identifies a user. No two users can have the same ID.
|
|
3
3
|
name: admin # A name that identifies the user. No two users can have the same name.
|
|
4
4
|
password: admin # The user's password. The value is hashed with the hash function specified in the next hash field.
|
|
5
|
-
hash: plain # The hash function used to hash the password, which can be plain, md5, sha1, or sha256, or oauth2.
|
|
5
|
+
hash: plain # The hash function used to hash the password, which can be plain, md5, sha1, or sha256, or oauth2, or saml.
|
|
6
6
|
groups: [admin] # A list of groups to which the user belongs, as specified in the groups field.
|
|
7
|
-
email: admin@aaa.bbb.jp # The email address of the user, used when authenticating using the provider specified in the oauth2 field.
|
|
7
|
+
email: admin@aaa.bbb.jp # The email address of the user, used when authenticating using the provider specified in the oauth2 or saml field.
|
|
8
8
|
- uid: 101
|
|
9
9
|
name: user01
|
|
10
10
|
password: b75705d7e35e7014521a46b532236ec3
|
|
@@ -36,7 +36,6 @@ groups: # A list of groups, each of which is a map that c
|
|
|
36
36
|
- gid: 103
|
|
37
37
|
name: editor
|
|
38
38
|
parent: user
|
|
39
|
-
|
|
40
39
|
cmdrule: # A list of command rules, Specify a rule that determines whether or not a command is executable when executed by a user in web mode.
|
|
41
40
|
policy: deny # Specify the default policy for the rule. The value can be allow or deny.
|
|
42
41
|
rules: # Specify rules to allow or deny execution of the command, depending on the group the user belongs to.
|
|
@@ -50,6 +49,10 @@ cmdrule: # A list of command rules, Specify a rule that de
|
|
|
50
49
|
mode: server
|
|
51
50
|
cmds: [list]
|
|
52
51
|
rule: allow
|
|
52
|
+
- groups: [user, guest]
|
|
53
|
+
mode: audit
|
|
54
|
+
cmds: [write]
|
|
55
|
+
rule: allow
|
|
53
56
|
- groups: [user, guest]
|
|
54
57
|
mode: web
|
|
55
58
|
cmds: [genpass]
|
|
@@ -70,6 +73,7 @@ pathrule: # List of RESTAPI rules, rules that determine whe
|
|
|
70
73
|
rule: allow
|
|
71
74
|
- groups: [user]
|
|
72
75
|
paths: [/signin, /assets, /bbforce_cmd, /copyright, /dosignin, /dosignout, /password/change,
|
|
76
|
+
/gui/user_data/load, /gui/user_data/save, /gui/user_data/delete,
|
|
73
77
|
/exec_cmd, /exec_pipe, /filer, /gui, /get_server_opt, /usesignout, /versions_cmdbox, /versions_used]
|
|
74
78
|
rule: allow
|
|
75
79
|
- groups: [readonly]
|
|
@@ -105,7 +109,8 @@ oauth2: # OAuth2 settings.
|
|
|
105
109
|
client_secret: XXXXXXXXXXX # Specify Google's OAuth2 client secret.
|
|
106
110
|
redirect_uri: https://localhost:8443/oauth2/google/callback # Specify Google's OAuth2 redirect URI.
|
|
107
111
|
scope: ['email'] # Specify the scope you want to retrieve with Google's OAuth2. Usually, just reading the email is sufficient.
|
|
108
|
-
signin_module: # Specify the module name that implements the sign-in.
|
|
112
|
+
signin_module: # Specify the module name that implements the sign-in.
|
|
113
|
+
cmdbox.app.auth.google_signin
|
|
109
114
|
note: # Specify a description such as Google's OAuth2 reference site.
|
|
110
115
|
- https://developers.google.com/identity/protocols/oauth2/web-server?hl=ja#httprest
|
|
111
116
|
github: # OAuth2 settings for GitHub.
|
|
@@ -114,7 +119,8 @@ oauth2: # OAuth2 settings.
|
|
|
114
119
|
client_secret: XXXXXXXXXXX # Specify the GitHub OAuth2 client secret.
|
|
115
120
|
redirect_uri: https://localhost:8443/oauth2/github/callback # Specify the OAuth2 redirect URI for GitHub.
|
|
116
121
|
scope: ['user:email'] # Specify the scope you want to get from GitHub's OAuth2. Usually, just reading the email is sufficient.
|
|
117
|
-
signin_module: # Specify the module name that implements the sign-in.
|
|
122
|
+
signin_module: # Specify the module name that implements the sign-in.
|
|
123
|
+
cmdbox.app.auth.github_signin
|
|
118
124
|
note: # Specify a description, such as a reference site for OAuth2 on GitHub.
|
|
119
125
|
- https://docs.github.com/ja/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#scopes
|
|
120
126
|
azure: # OAuth2 settings for Azure AD.
|
|
@@ -124,6 +130,34 @@ oauth2: # OAuth2 settings.
|
|
|
124
130
|
client_secret: XXXXXXXXXXX # Specify the Azure AD OAuth2 client secret.
|
|
125
131
|
redirect_uri: https://localhost:8443/oauth2/azure/callback # Specify the OAuth2 redirect URI for Azure AD.
|
|
126
132
|
scope: ['openid', 'profile', 'email', 'https://graph.microsoft.com/mail.read']
|
|
127
|
-
signin_module: # Specify the module name that implements the sign-in.
|
|
133
|
+
signin_module: # Specify the module name that implements the sign-in.
|
|
134
|
+
cmdbox.app.auth.azure_signin
|
|
128
135
|
note: # Specify a description, such as a reference site for Azure AD's OAuth2.
|
|
129
136
|
- https://learn.microsoft.com/ja-jp/entra/identity-platform/v2-oauth2-auth-code-flow
|
|
137
|
+
saml: # SAML settings.
|
|
138
|
+
providers: # This is a per-provider setting for OAuth2.
|
|
139
|
+
azure: # SAML settings for Azure AD.
|
|
140
|
+
enabled: false # Specify whether to enable SAML authentication for Azure AD.
|
|
141
|
+
signin_module: # Specify the module name that implements the sign-in.
|
|
142
|
+
cmdbox.app.auth.azure_signin_saml # Specify the python3-saml configuration.
|
|
143
|
+
# see) https://github.com/SAML-Toolkits/python3-saml
|
|
144
|
+
sp:
|
|
145
|
+
entityId: https://localhost:8443/
|
|
146
|
+
assertionConsumerService:
|
|
147
|
+
url: https://localhost:8443/saml/azure/callback
|
|
148
|
+
binding: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
|
|
149
|
+
attributeConsumingService: {}
|
|
150
|
+
singleLogoutService:
|
|
151
|
+
binding: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect
|
|
152
|
+
NameIDFormat: urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
|
|
153
|
+
x509cert: ''
|
|
154
|
+
privateKey: ''
|
|
155
|
+
idp:
|
|
156
|
+
entityId: https://sts.windows.net/{tenant-id}/
|
|
157
|
+
singleSignOnService:
|
|
158
|
+
url: https://login.microsoftonline.com/{tenant-id}/saml2
|
|
159
|
+
binding: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect
|
|
160
|
+
x509cert: XXXXXXXXXXX
|
|
161
|
+
singleLogoutService: {}
|
|
162
|
+
certFingerprint: ''
|
|
163
|
+
certFingerprintAlgorithm: sha1
|
cmdbox/extensions/user_list.yml
CHANGED
|
@@ -2,9 +2,9 @@ users: # A list of users, each of which is a map that co
|
|
|
2
2
|
- uid: 1 # An ID that identifies a user. No two users can have the same ID.
|
|
3
3
|
name: admin # A name that identifies the user. No two users can have the same name.
|
|
4
4
|
password: admin # The user's password. The value is hashed with the hash function specified in the next hash field.
|
|
5
|
-
hash: plain # The hash function used to hash the password, which can be plain, md5, sha1, or sha256, or oauth2.
|
|
5
|
+
hash: plain # The hash function used to hash the password, which can be plain, md5, sha1, or sha256, or oauth2, or saml.
|
|
6
6
|
groups: [admin] # A list of groups to which the user belongs, as specified in the groups field.
|
|
7
|
-
email: admin@aaa.bbb.jp # The email address of the user, used when authenticating using the provider specified in the oauth2 field.
|
|
7
|
+
email: admin@aaa.bbb.jp # The email address of the user, used when authenticating using the provider specified in the oauth2 or saml field.
|
|
8
8
|
- uid: 101
|
|
9
9
|
name: user01
|
|
10
10
|
password: b75705d7e35e7014521a46b532236ec3
|
|
@@ -74,7 +74,7 @@ pathrule: # List of RESTAPI rules, rules that determine whe
|
|
|
74
74
|
- groups: [user]
|
|
75
75
|
paths: [/signin, /assets, /bbforce_cmd, /copyright, /dosignin, /dosignout, /password/change,
|
|
76
76
|
/gui/user_data/load, /gui/user_data/save, /gui/user_data/delete,
|
|
77
|
-
/exec_cmd, /exec_pipe, /filer, /gui, /get_server_opt, /usesignout, /versions_cmdbox, /versions_used]
|
|
77
|
+
/exec_cmd, /exec_pipe, /filer, /result, /gui, /get_server_opt, /usesignout, /versions_cmdbox, /versions_used]
|
|
78
78
|
rule: allow
|
|
79
79
|
- groups: [readonly]
|
|
80
80
|
paths: [/gui/del_cmd, /gui/del_pipe, /gui/save_cmd, /gui/save_pipe]
|
|
@@ -109,7 +109,8 @@ oauth2: # OAuth2 settings.
|
|
|
109
109
|
client_secret: XXXXXXXXXXX # Specify Google's OAuth2 client secret.
|
|
110
110
|
redirect_uri: https://localhost:8443/oauth2/google/callback # Specify Google's OAuth2 redirect URI.
|
|
111
111
|
scope: ['email'] # Specify the scope you want to retrieve with Google's OAuth2. Usually, just reading the email is sufficient.
|
|
112
|
-
signin_module: # Specify the module name that implements the sign-in.
|
|
112
|
+
signin_module: # Specify the module name that implements the sign-in.
|
|
113
|
+
cmdbox.app.auth.google_signin
|
|
113
114
|
note: # Specify a description such as Google's OAuth2 reference site.
|
|
114
115
|
- https://developers.google.com/identity/protocols/oauth2/web-server?hl=ja#httprest
|
|
115
116
|
github: # OAuth2 settings for GitHub.
|
|
@@ -118,7 +119,8 @@ oauth2: # OAuth2 settings.
|
|
|
118
119
|
client_secret: XXXXXXXXXXX # Specify the GitHub OAuth2 client secret.
|
|
119
120
|
redirect_uri: https://localhost:8443/oauth2/github/callback # Specify the OAuth2 redirect URI for GitHub.
|
|
120
121
|
scope: ['user:email'] # Specify the scope you want to get from GitHub's OAuth2. Usually, just reading the email is sufficient.
|
|
121
|
-
signin_module: # Specify the module name that implements the sign-in.
|
|
122
|
+
signin_module: # Specify the module name that implements the sign-in.
|
|
123
|
+
cmdbox.app.auth.github_signin
|
|
122
124
|
note: # Specify a description, such as a reference site for OAuth2 on GitHub.
|
|
123
125
|
- https://docs.github.com/ja/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#scopes
|
|
124
126
|
azure: # OAuth2 settings for Azure AD.
|
|
@@ -128,6 +130,34 @@ oauth2: # OAuth2 settings.
|
|
|
128
130
|
client_secret: XXXXXXXXXXX # Specify the Azure AD OAuth2 client secret.
|
|
129
131
|
redirect_uri: https://localhost:8443/oauth2/azure/callback # Specify the OAuth2 redirect URI for Azure AD.
|
|
130
132
|
scope: ['openid', 'profile', 'email', 'https://graph.microsoft.com/mail.read']
|
|
131
|
-
signin_module: # Specify the module name that implements the sign-in.
|
|
133
|
+
signin_module: # Specify the module name that implements the sign-in.
|
|
134
|
+
cmdbox.app.auth.azure_signin
|
|
132
135
|
note: # Specify a description, such as a reference site for Azure AD's OAuth2.
|
|
133
136
|
- https://learn.microsoft.com/ja-jp/entra/identity-platform/v2-oauth2-auth-code-flow
|
|
137
|
+
saml: # SAML settings.
|
|
138
|
+
providers: # This is a per-provider setting for OAuth2.
|
|
139
|
+
azure: # SAML settings for Azure AD.
|
|
140
|
+
enabled: false # Specify whether to enable SAML authentication for Azure AD.
|
|
141
|
+
signin_module: # Specify the module name that implements the sign-in.
|
|
142
|
+
cmdbox.app.auth.azure_signin_saml # Specify the python3-saml configuration.
|
|
143
|
+
# see) https://github.com/SAML-Toolkits/python3-saml
|
|
144
|
+
sp:
|
|
145
|
+
entityId: https://localhost:8443/
|
|
146
|
+
assertionConsumerService:
|
|
147
|
+
url: https://localhost:8443/saml/azure/callback
|
|
148
|
+
binding: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
|
|
149
|
+
attributeConsumingService: {}
|
|
150
|
+
singleLogoutService:
|
|
151
|
+
binding: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect
|
|
152
|
+
NameIDFormat: urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
|
|
153
|
+
x509cert: ''
|
|
154
|
+
privateKey: ''
|
|
155
|
+
idp:
|
|
156
|
+
entityId: https://sts.windows.net/{tenant-id}/
|
|
157
|
+
singleSignOnService:
|
|
158
|
+
url: https://login.microsoftonline.com/{tenant-id}/saml2
|
|
159
|
+
binding: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect
|
|
160
|
+
x509cert: XXXXXXXXXXX
|
|
161
|
+
singleLogoutService: {}
|
|
162
|
+
certFingerprint: ''
|
|
163
|
+
certFingerprintAlgorithm: sha1
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
Copyright
|
|
1
|
+
Copyright 2016-2020 aio-libs collaboration.
|
|
2
2
|
|
|
3
3
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
you may not use this file except in compliance with the License.
|
|
5
5
|
You may obtain a copy of the License at
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
8
|
|
|
9
9
|
Unless required by applicable law or agreed to in writing, software
|
|
10
10
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
11
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
See the License for the specific language governing permissions and
|
|
13
13
|
limitations under the License.
|
|
14
|
-
|