flask-appbuilder 3.2.1__py3-none-any.whl → 5.0.2__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.
- flask_appbuilder/__init__.py +2 -3
- flask_appbuilder/_compat.py +0 -1
- flask_appbuilder/actions.py +14 -14
- flask_appbuilder/api/__init__.py +741 -527
- flask_appbuilder/api/convert.py +104 -98
- flask_appbuilder/api/manager.py +14 -8
- flask_appbuilder/api/schemas.py +12 -1
- flask_appbuilder/babel/manager.py +12 -16
- flask_appbuilder/base.py +353 -280
- flask_appbuilder/basemanager.py +1 -1
- flask_appbuilder/baseviews.py +241 -164
- flask_appbuilder/charts/jsontools.py +10 -10
- flask_appbuilder/charts/views.py +56 -60
- flask_appbuilder/cli.py +115 -70
- flask_appbuilder/const.py +52 -52
- flask_appbuilder/exceptions.py +67 -5
- flask_appbuilder/fields.py +32 -23
- flask_appbuilder/fieldwidgets.py +34 -27
- flask_appbuilder/filemanager.py +33 -45
- flask_appbuilder/filters.py +11 -13
- flask_appbuilder/forms.py +31 -35
- flask_appbuilder/hooks.py +90 -0
- flask_appbuilder/menu.py +35 -10
- flask_appbuilder/models/base.py +47 -57
- flask_appbuilder/models/decorators.py +13 -13
- flask_appbuilder/models/filters.py +42 -38
- flask_appbuilder/models/generic/__init__.py +29 -29
- flask_appbuilder/models/generic/filters.py +11 -3
- flask_appbuilder/models/generic/interface.py +1 -3
- flask_appbuilder/models/group.py +37 -39
- flask_appbuilder/models/mixins.py +22 -18
- flask_appbuilder/models/sqla/__init__.py +19 -72
- flask_appbuilder/models/sqla/base.py +24 -0
- flask_appbuilder/models/sqla/base_legacy.py +132 -0
- flask_appbuilder/models/sqla/filters.py +132 -19
- flask_appbuilder/models/sqla/interface.py +390 -276
- flask_appbuilder/security/api.py +31 -35
- flask_appbuilder/security/decorators.py +181 -83
- flask_appbuilder/security/forms.py +20 -31
- flask_appbuilder/security/manager.py +715 -489
- flask_appbuilder/security/registerviews.py +29 -112
- flask_appbuilder/security/schemas.py +43 -0
- flask_appbuilder/security/sqla/apis/__init__.py +8 -0
- flask_appbuilder/security/sqla/apis/group/__init__.py +1 -0
- flask_appbuilder/security/sqla/apis/group/api.py +227 -0
- flask_appbuilder/security/sqla/apis/group/schema.py +73 -0
- flask_appbuilder/security/sqla/apis/permission/__init__.py +1 -0
- flask_appbuilder/security/sqla/apis/permission/api.py +19 -0
- flask_appbuilder/security/sqla/apis/permission_view_menu/__init__.py +1 -0
- flask_appbuilder/security/sqla/apis/permission_view_menu/api.py +16 -0
- flask_appbuilder/security/sqla/apis/role/__init__.py +1 -0
- flask_appbuilder/security/sqla/apis/role/api.py +306 -0
- flask_appbuilder/security/sqla/apis/role/schema.py +27 -0
- flask_appbuilder/security/sqla/apis/user/__init__.py +1 -0
- flask_appbuilder/security/sqla/apis/user/api.py +292 -0
- flask_appbuilder/security/sqla/apis/user/schema.py +97 -0
- flask_appbuilder/security/sqla/apis/user/validator.py +27 -0
- flask_appbuilder/security/sqla/apis/view_menu/__init__.py +1 -0
- flask_appbuilder/security/sqla/apis/view_menu/api.py +18 -0
- flask_appbuilder/security/sqla/manager.py +421 -203
- flask_appbuilder/security/sqla/models.py +192 -57
- flask_appbuilder/security/utils.py +9 -0
- flask_appbuilder/security/views.py +232 -229
- flask_appbuilder/static/.DS_Store +0 -0
- flask_appbuilder/static/appbuilder/css/ab.css +20 -12
- flask_appbuilder/static/appbuilder/css/bootstrap-datepicker/bootstrap-datepicker3.min.css +7 -0
- flask_appbuilder/static/appbuilder/css/bootstrap.min.css.map +1 -0
- flask_appbuilder/static/appbuilder/css/flags/flags16.css +249 -245
- flask_appbuilder/static/appbuilder/css/fontawesome/all.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/brands.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/fontawesome.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/regular.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/solid.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/svg-with-js.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/v4-font-face.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/v4-shims.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/v5-font-face.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/images/flags16.png +0 -0
- flask_appbuilder/static/appbuilder/css/select2/select2-bootstrap.min.css +7 -0
- flask_appbuilder/static/appbuilder/css/select2/select2.min.css +1 -0
- flask_appbuilder/static/appbuilder/css/swagger/swagger-ui.css +3 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-brands-400.ttf +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-brands-400.woff2 +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-regular-400.ttf +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-regular-400.woff2 +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-solid-900.ttf +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-solid-900.woff2 +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-v4compatibility.ttf +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-v4compatibility.woff2 +0 -0
- flask_appbuilder/static/appbuilder/js/ab.js +33 -23
- flask_appbuilder/static/appbuilder/js/ab_filters.js +91 -84
- flask_appbuilder/static/appbuilder/js/bootstrap-datepicker/bootstrap-datepicker.min.js +8 -0
- flask_appbuilder/static/appbuilder/js/jquery-latest.js +2 -2
- flask_appbuilder/static/appbuilder/js/select2/select2.min.js +2 -0
- flask_appbuilder/static/appbuilder/js/swagger-ui-bundle.js +3 -0
- flask_appbuilder/templates/appbuilder/baselib.html +9 -3
- flask_appbuilder/templates/appbuilder/general/lib.html +60 -34
- flask_appbuilder/templates/appbuilder/general/model/edit.html +1 -1
- flask_appbuilder/templates/appbuilder/general/model/edit_cascade.html +1 -1
- flask_appbuilder/templates/appbuilder/general/model/search.html +3 -2
- flask_appbuilder/templates/appbuilder/general/model/show.html +1 -1
- flask_appbuilder/templates/appbuilder/general/model/show_cascade.html +1 -1
- flask_appbuilder/templates/appbuilder/general/security/login_db.html +7 -7
- flask_appbuilder/templates/appbuilder/general/security/login_ldap.html +5 -5
- flask_appbuilder/templates/appbuilder/general/security/login_oauth.html +24 -49
- flask_appbuilder/templates/appbuilder/general/widgets/base_list.html +2 -1
- flask_appbuilder/templates/appbuilder/general/widgets/chart.html +4 -2
- flask_appbuilder/templates/appbuilder/general/widgets/direct_chart.html +4 -3
- flask_appbuilder/templates/appbuilder/general/widgets/multiple_chart.html +3 -2
- flask_appbuilder/templates/appbuilder/general/widgets/search.html +11 -10
- flask_appbuilder/templates/appbuilder/init.html +37 -43
- flask_appbuilder/templates/appbuilder/navbar_menu.html +1 -1
- flask_appbuilder/templates/appbuilder/navbar_right.html +2 -2
- flask_appbuilder/templates/appbuilder/swagger/swagger.html +22 -19
- flask_appbuilder/translations/de/LC_MESSAGES/messages.mo +0 -0
- flask_appbuilder/translations/de/LC_MESSAGES/messages.po +305 -161
- flask_appbuilder/translations/fa/LC_MESSAGES/messages.mo +0 -0
- flask_appbuilder/translations/fa/LC_MESSAGES/messages.po +802 -0
- flask_appbuilder/translations/fr/LC_MESSAGES/messages.po +461 -319
- flask_appbuilder/translations/pt_BR/LC_MESSAGES/messages.po +650 -650
- flask_appbuilder/translations/ru/LC_MESSAGES/messages.po +1 -1
- flask_appbuilder/translations/sl/LC_MESSAGES/messages.mo +0 -0
- flask_appbuilder/translations/sl/LC_MESSAGES/messages.po +690 -0
- flask_appbuilder/translations/tr/LC_MESSAGES/messages.mo +0 -0
- flask_appbuilder/translations/tr/LC_MESSAGES/messages.po +1015 -0
- flask_appbuilder/upload.py +20 -22
- flask_appbuilder/urltools.py +39 -19
- flask_appbuilder/utils/base.py +76 -0
- flask_appbuilder/utils/legacy.py +33 -0
- flask_appbuilder/utils/limit.py +20 -0
- flask_appbuilder/validators.py +73 -14
- flask_appbuilder/views.py +75 -424
- flask_appbuilder/widgets.py +50 -51
- {Flask_AppBuilder-3.2.1.dist-info → flask_appbuilder-5.0.2.dist-info}/METADATA +36 -76
- flask_appbuilder-5.0.2.dist-info/RECORD +240 -0
- {Flask_AppBuilder-3.2.1.dist-info → flask_appbuilder-5.0.2.dist-info}/WHEEL +1 -1
- flask_appbuilder-5.0.2.dist-info/entry_points.txt +2 -0
- Flask_AppBuilder-3.2.1.dist-info/RECORD +0 -270
- Flask_AppBuilder-3.2.1.dist-info/entry_points.txt +0 -6
- flask_appbuilder/console.py +0 -426
- flask_appbuilder/models/mongoengine/__init__.py +0 -0
- flask_appbuilder/models/mongoengine/fields.py +0 -65
- flask_appbuilder/models/mongoengine/filters.py +0 -145
- flask_appbuilder/models/mongoengine/interface.py +0 -328
- flask_appbuilder/security/mongoengine/__init__.py +0 -0
- flask_appbuilder/security/mongoengine/manager.py +0 -402
- flask_appbuilder/security/mongoengine/models.py +0 -120
- flask_appbuilder/static/appbuilder/css/font-awesome.min.css +0 -4
- flask_appbuilder/static/appbuilder/datepicker/bootstrap-datepicker.css +0 -9
- flask_appbuilder/static/appbuilder/datepicker/bootstrap-datepicker.js +0 -28
- flask_appbuilder/static/appbuilder/fonts/FontAwesome.otf +0 -0
- flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.eot +0 -0
- flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.svg +0 -2671
- flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.ttf +0 -0
- flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.woff +0 -0
- flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.woff2 +0 -0
- flask_appbuilder/static/appbuilder/img/aol.png +0 -0
- flask_appbuilder/static/appbuilder/img/flags/flags16.png +0 -0
- flask_appbuilder/static/appbuilder/img/flickr.png +0 -0
- flask_appbuilder/static/appbuilder/img/google.png +0 -0
- flask_appbuilder/static/appbuilder/img/myopenid.png +0 -0
- flask_appbuilder/static/appbuilder/img/yahoo.png +0 -0
- flask_appbuilder/static/appbuilder/js/_google_charts.js +0 -39
- flask_appbuilder/static/appbuilder/js/html5shiv.js +0 -8
- flask_appbuilder/static/appbuilder/js/respond.min.js +0 -6
- flask_appbuilder/static/appbuilder/select2/select2-spinner.gif +0 -0
- flask_appbuilder/static/appbuilder/select2/select2.css +0 -1205
- flask_appbuilder/static/appbuilder/select2/select2.js +0 -23
- flask_appbuilder/static/appbuilder/select2/select2.png +0 -0
- flask_appbuilder/static/appbuilder/select2/select2x2.png +0 -0
- flask_appbuilder/templates/appbuilder/general/security/login_oid.html +0 -129
- flask_appbuilder/templates/appbuilder/general/security/resetpassword.html +0 -29
- flask_appbuilder/tests/__init__.py +0 -0
- flask_appbuilder/tests/__pycache__/__init__.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/__init__.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/_test_auth_ldap.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/_test_auth_oauth.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/_test_ldapsearch.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/_test_oauth_registration_role.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/base.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/base.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/config_api.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/config_api.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/const.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/const.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_0_fixture.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_0_fixture.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_api.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_api.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_fab_cli.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_fab_cli.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_menu.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_menu.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_mongoengine.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_mvc.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_mvc.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_sqlalchemy.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_sqlalchemy.cpython-37.pyc +0 -0
- flask_appbuilder/tests/_test_auth_ldap.py +0 -1045
- flask_appbuilder/tests/_test_auth_oauth.py +0 -419
- flask_appbuilder/tests/_test_ldapsearch.py +0 -135
- flask_appbuilder/tests/_test_oauth_registration_role.py +0 -59
- flask_appbuilder/tests/app.db +0 -0
- flask_appbuilder/tests/base.py +0 -90
- flask_appbuilder/tests/config_api.py +0 -21
- flask_appbuilder/tests/const.py +0 -9
- flask_appbuilder/tests/mongoengine/__init__.py +0 -0
- flask_appbuilder/tests/mongoengine/__pycache__/__init__.cpython-36.pyc +0 -0
- flask_appbuilder/tests/mongoengine/__pycache__/__init__.cpython-37.pyc +0 -0
- flask_appbuilder/tests/mongoengine/__pycache__/models.cpython-36.pyc +0 -0
- flask_appbuilder/tests/mongoengine/models.py +0 -41
- flask_appbuilder/tests/sqla/__init__.py +0 -0
- flask_appbuilder/tests/sqla/__pycache__/__init__.cpython-36.pyc +0 -0
- flask_appbuilder/tests/sqla/__pycache__/__init__.cpython-37.pyc +0 -0
- flask_appbuilder/tests/sqla/__pycache__/models.cpython-36.pyc +0 -0
- flask_appbuilder/tests/sqla/__pycache__/models.cpython-37.pyc +0 -0
- flask_appbuilder/tests/sqla/models.py +0 -340
- flask_appbuilder/tests/test_0_fixture.py +0 -39
- flask_appbuilder/tests/test_api.py +0 -2790
- flask_appbuilder/tests/test_fab_cli.py +0 -72
- flask_appbuilder/tests/test_menu.py +0 -122
- flask_appbuilder/tests/test_mongoengine.py +0 -572
- flask_appbuilder/tests/test_mvc.py +0 -1710
- flask_appbuilder/tests/test_sqlalchemy.py +0 -24
- flask_appbuilder/translations/__pycache__/__init__.cpython-36.pyc +0 -0
- flask_appbuilder/translations/es/LC_MESSAGES/messages.po~ +0 -582
- {Flask_AppBuilder-3.2.1.dist-info → flask_appbuilder-5.0.2.dist-info}/LICENSE +0 -0
- {Flask_AppBuilder-3.2.1.dist-info → flask_appbuilder-5.0.2.dist-info}/top_level.txt +0 -0
|
@@ -1,419 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import os
|
|
3
|
-
import unittest
|
|
4
|
-
|
|
5
|
-
from flask import Flask
|
|
6
|
-
from flask_appbuilder import AppBuilder, SQLA
|
|
7
|
-
from flask_appbuilder.const import AUTH_OAUTH
|
|
8
|
-
import jinja2
|
|
9
|
-
|
|
10
|
-
logging.basicConfig(format="%(asctime)s:%(levelname)s:%(name)s:%(message)s")
|
|
11
|
-
logging.getLogger().setLevel(logging.DEBUG)
|
|
12
|
-
log = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class OAuthRegistrationRoleTestCase(unittest.TestCase):
|
|
16
|
-
def setUp(self):
|
|
17
|
-
# start Flask
|
|
18
|
-
self.app = Flask(__name__)
|
|
19
|
-
self.app.jinja_env.undefined = jinja2.StrictUndefined
|
|
20
|
-
self.app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get(
|
|
21
|
-
"SQLALCHEMY_DATABASE_URI"
|
|
22
|
-
)
|
|
23
|
-
self.app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
|
24
|
-
self.app.config["AUTH_TYPE"] = AUTH_OAUTH
|
|
25
|
-
self.app.config[
|
|
26
|
-
"OAUTH_PROVIDERS"
|
|
27
|
-
] = [] # can be empty, because we dont use the external providers in tests
|
|
28
|
-
|
|
29
|
-
# start Database
|
|
30
|
-
self.db = SQLA(self.app)
|
|
31
|
-
|
|
32
|
-
def tearDown(self):
|
|
33
|
-
# stop Flask
|
|
34
|
-
self.app = None
|
|
35
|
-
|
|
36
|
-
# stop Flask-AppBuilder
|
|
37
|
-
self.appbuilder = None
|
|
38
|
-
|
|
39
|
-
# stop Database
|
|
40
|
-
self.db.session.remove()
|
|
41
|
-
self.db.drop_all()
|
|
42
|
-
self.db = None
|
|
43
|
-
|
|
44
|
-
# ----------------
|
|
45
|
-
# Userinfo Objects
|
|
46
|
-
# ----------------
|
|
47
|
-
userinfo_alice = {
|
|
48
|
-
"username": "alice",
|
|
49
|
-
"first_name": "Alice",
|
|
50
|
-
"last_name": "Doe",
|
|
51
|
-
"email": "alice@example.com",
|
|
52
|
-
"role_keys": ["GROUP_1", "GROUP_2"],
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
# ----------------
|
|
56
|
-
# Unit Tests
|
|
57
|
-
# ----------------
|
|
58
|
-
def test__inactive_user(self):
|
|
59
|
-
"""
|
|
60
|
-
OAUTH: test login flow for - inactive user
|
|
61
|
-
"""
|
|
62
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
63
|
-
sm = self.appbuilder.sm
|
|
64
|
-
|
|
65
|
-
# validate - no users are registered
|
|
66
|
-
self.assertEqual(sm.get_all_users(), [])
|
|
67
|
-
|
|
68
|
-
# register a user
|
|
69
|
-
new_user = sm.add_user(
|
|
70
|
-
username="alice",
|
|
71
|
-
first_name="Alice",
|
|
72
|
-
last_name="Doe",
|
|
73
|
-
email="alice@example.com",
|
|
74
|
-
role=[],
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
# validate - user was registered
|
|
78
|
-
self.assertEqual(len(sm.get_all_users()), 1)
|
|
79
|
-
|
|
80
|
-
# set user inactive
|
|
81
|
-
new_user.active = False
|
|
82
|
-
|
|
83
|
-
# attempt login
|
|
84
|
-
user = sm.auth_user_oauth(self.userinfo_alice)
|
|
85
|
-
|
|
86
|
-
# validate - user was not allowed to log in
|
|
87
|
-
self.assertIsNone(user)
|
|
88
|
-
|
|
89
|
-
def test__missing_username(self):
|
|
90
|
-
"""
|
|
91
|
-
OAUTH: test login flow for - missing credentials
|
|
92
|
-
"""
|
|
93
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
94
|
-
sm = self.appbuilder.sm
|
|
95
|
-
|
|
96
|
-
# validate - no users are registered
|
|
97
|
-
self.assertEqual(sm.get_all_users(), [])
|
|
98
|
-
|
|
99
|
-
# create userinfo with missing info
|
|
100
|
-
userinfo_missing = self.userinfo_alice.copy()
|
|
101
|
-
userinfo_missing["username"] = ""
|
|
102
|
-
|
|
103
|
-
# attempt login
|
|
104
|
-
user = sm.auth_user_oauth(userinfo_missing)
|
|
105
|
-
|
|
106
|
-
# validate - login failure (missing username)
|
|
107
|
-
self.assertIsNone(user)
|
|
108
|
-
|
|
109
|
-
# validate - no users were created
|
|
110
|
-
self.assertEqual(sm.get_all_users(), [])
|
|
111
|
-
|
|
112
|
-
def test__unregistered(self):
|
|
113
|
-
"""
|
|
114
|
-
OAUTH: test login flow for - unregistered user
|
|
115
|
-
"""
|
|
116
|
-
self.app.config["AUTH_USER_REGISTRATION"] = True
|
|
117
|
-
self.app.config["AUTH_USER_REGISTRATION_ROLE"] = "Public"
|
|
118
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
119
|
-
sm = self.appbuilder.sm
|
|
120
|
-
|
|
121
|
-
# validate - no users are registered
|
|
122
|
-
self.assertEqual(sm.get_all_users(), [])
|
|
123
|
-
|
|
124
|
-
# attempt login
|
|
125
|
-
user = sm.auth_user_oauth(self.userinfo_alice)
|
|
126
|
-
|
|
127
|
-
# validate - user was allowed to log in
|
|
128
|
-
self.assertIsInstance(user, sm.user_model)
|
|
129
|
-
|
|
130
|
-
# validate - user was registered
|
|
131
|
-
self.assertEqual(len(sm.get_all_users()), 1)
|
|
132
|
-
|
|
133
|
-
# validate - user was given the AUTH_USER_REGISTRATION_ROLE role
|
|
134
|
-
self.assertEqual(user.roles, [sm.find_role("Public")])
|
|
135
|
-
|
|
136
|
-
# validate - user was given the correct attributes
|
|
137
|
-
self.assertEqual(user.first_name, "Alice")
|
|
138
|
-
self.assertEqual(user.last_name, "Doe")
|
|
139
|
-
self.assertEqual(user.email, "alice@example.com")
|
|
140
|
-
|
|
141
|
-
def test__unregistered__no_self_register(self):
|
|
142
|
-
"""
|
|
143
|
-
OAUTH: test login flow for - unregistered user - no self-registration
|
|
144
|
-
"""
|
|
145
|
-
self.app.config["AUTH_USER_REGISTRATION"] = False
|
|
146
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
147
|
-
sm = self.appbuilder.sm
|
|
148
|
-
|
|
149
|
-
# validate - no users are registered
|
|
150
|
-
self.assertEqual(sm.get_all_users(), [])
|
|
151
|
-
|
|
152
|
-
# attempt login
|
|
153
|
-
user = sm.auth_user_oauth(self.userinfo_alice)
|
|
154
|
-
|
|
155
|
-
# validate - user was not allowed to log in
|
|
156
|
-
self.assertIsNone(user)
|
|
157
|
-
|
|
158
|
-
# validate - no users were registered
|
|
159
|
-
self.assertEqual(sm.get_all_users(), [])
|
|
160
|
-
|
|
161
|
-
def test__unregistered__single_role(self):
|
|
162
|
-
"""
|
|
163
|
-
OAUTH: test login flow for - unregistered user
|
|
164
|
-
- single role mapping
|
|
165
|
-
"""
|
|
166
|
-
self.app.config["AUTH_ROLES_MAPPING"] = {
|
|
167
|
-
"GROUP_1": ["Admin"],
|
|
168
|
-
"GROUP_2": ["User"],
|
|
169
|
-
}
|
|
170
|
-
self.app.config["AUTH_USER_REGISTRATION"] = True
|
|
171
|
-
self.app.config["AUTH_USER_REGISTRATION_ROLE"] = "Public"
|
|
172
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
173
|
-
sm = self.appbuilder.sm
|
|
174
|
-
|
|
175
|
-
# add User role
|
|
176
|
-
sm.add_role("User")
|
|
177
|
-
|
|
178
|
-
# validate - no users are registered
|
|
179
|
-
self.assertEqual(sm.get_all_users(), [])
|
|
180
|
-
|
|
181
|
-
# attempt login
|
|
182
|
-
user = sm.auth_user_oauth(self.userinfo_alice)
|
|
183
|
-
|
|
184
|
-
# validate - user was allowed to log in
|
|
185
|
-
self.assertIsInstance(user, sm.user_model)
|
|
186
|
-
|
|
187
|
-
# validate - user was registered
|
|
188
|
-
self.assertEqual(len(sm.get_all_users()), 1)
|
|
189
|
-
|
|
190
|
-
# validate - user was given the correct roles
|
|
191
|
-
self.assertListEqual(
|
|
192
|
-
user.roles,
|
|
193
|
-
[sm.find_role("Admin"), sm.find_role("Public"), sm.find_role("User")],
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
# validate - user was given the correct attributes (read from LDAP)
|
|
197
|
-
self.assertEqual(user.first_name, "Alice")
|
|
198
|
-
self.assertEqual(user.last_name, "Doe")
|
|
199
|
-
self.assertEqual(user.email, "alice@example.com")
|
|
200
|
-
|
|
201
|
-
def test__unregistered__multi_role(self):
|
|
202
|
-
"""
|
|
203
|
-
OAUTH: test login flow for - unregistered user - multi role mapping
|
|
204
|
-
"""
|
|
205
|
-
self.app.config["AUTH_ROLES_MAPPING"] = {"GROUP_1": ["Admin", "User"]}
|
|
206
|
-
self.app.config["AUTH_USER_REGISTRATION"] = True
|
|
207
|
-
self.app.config["AUTH_USER_REGISTRATION_ROLE"] = "Public"
|
|
208
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
209
|
-
sm = self.appbuilder.sm
|
|
210
|
-
|
|
211
|
-
# add User role
|
|
212
|
-
sm.add_role("User")
|
|
213
|
-
|
|
214
|
-
# validate - no users are registered
|
|
215
|
-
self.assertEqual(sm.get_all_users(), [])
|
|
216
|
-
|
|
217
|
-
# attempt login
|
|
218
|
-
user = sm.auth_user_oauth(self.userinfo_alice)
|
|
219
|
-
|
|
220
|
-
# validate - user was allowed to log in
|
|
221
|
-
self.assertIsInstance(user, sm.user_model)
|
|
222
|
-
|
|
223
|
-
# validate - user was registered
|
|
224
|
-
self.assertEqual(len(sm.get_all_users()), 1)
|
|
225
|
-
|
|
226
|
-
# validate - user was given the correct roles
|
|
227
|
-
self.assertListEqual(
|
|
228
|
-
user.roles,
|
|
229
|
-
[sm.find_role("Admin"), sm.find_role("Public"), sm.find_role("User")],
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
# validate - user was given the correct attributes (read from LDAP)
|
|
233
|
-
self.assertEqual(user.first_name, "Alice")
|
|
234
|
-
self.assertEqual(user.last_name, "Doe")
|
|
235
|
-
self.assertEqual(user.email, "alice@example.com")
|
|
236
|
-
|
|
237
|
-
def test__unregistered__jmespath_role(self):
|
|
238
|
-
"""
|
|
239
|
-
OAUTH: test login flow for - unregistered user - jmespath registration role
|
|
240
|
-
"""
|
|
241
|
-
self.app.config["AUTH_USER_REGISTRATION"] = True
|
|
242
|
-
self.app.config[
|
|
243
|
-
"AUTH_USER_REGISTRATION_ROLE_JMESPATH"
|
|
244
|
-
] = "contains(['alice'], username) && 'User' || 'Public'"
|
|
245
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
246
|
-
sm = self.appbuilder.sm
|
|
247
|
-
|
|
248
|
-
# add User role
|
|
249
|
-
sm.add_role("User")
|
|
250
|
-
|
|
251
|
-
# validate - no users are registered
|
|
252
|
-
self.assertEqual(sm.get_all_users(), [])
|
|
253
|
-
|
|
254
|
-
# attempt login
|
|
255
|
-
user = sm.auth_user_oauth(self.userinfo_alice)
|
|
256
|
-
|
|
257
|
-
# validate - user was allowed to log in
|
|
258
|
-
self.assertIsInstance(user, sm.user_model)
|
|
259
|
-
|
|
260
|
-
# validate - user was registered
|
|
261
|
-
self.assertEqual(len(sm.get_all_users()), 1)
|
|
262
|
-
|
|
263
|
-
# validate - user was given the correct roles
|
|
264
|
-
self.assertListEqual(user.roles, [sm.find_role("User")])
|
|
265
|
-
|
|
266
|
-
# validate - user was given the correct attributes (read from LDAP)
|
|
267
|
-
self.assertEqual(user.first_name, "Alice")
|
|
268
|
-
self.assertEqual(user.last_name, "Doe")
|
|
269
|
-
self.assertEqual(user.email, "alice@example.com")
|
|
270
|
-
|
|
271
|
-
def test__registered__multi_role__no_role_sync(self):
|
|
272
|
-
"""
|
|
273
|
-
OAUTH: test login flow for - registered user - multi role mapping - no login role-sync
|
|
274
|
-
""" # noqa
|
|
275
|
-
self.app.config["AUTH_ROLES_MAPPING"] = {"GROUP_1": ["Admin", "User"]}
|
|
276
|
-
self.app.config["AUTH_ROLES_SYNC_AT_LOGIN"] = False
|
|
277
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
278
|
-
sm = self.appbuilder.sm
|
|
279
|
-
|
|
280
|
-
# add User role
|
|
281
|
-
sm.add_role("User")
|
|
282
|
-
|
|
283
|
-
# validate - no users are registered
|
|
284
|
-
self.assertEqual(sm.get_all_users(), [])
|
|
285
|
-
|
|
286
|
-
# register a user
|
|
287
|
-
new_user = sm.add_user( # noqa
|
|
288
|
-
username="alice",
|
|
289
|
-
first_name="Alice",
|
|
290
|
-
last_name="Doe",
|
|
291
|
-
email="alice@example.com",
|
|
292
|
-
role=[],
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
# validate - user was registered
|
|
296
|
-
self.assertEqual(len(sm.get_all_users()), 1)
|
|
297
|
-
|
|
298
|
-
# attempt login
|
|
299
|
-
user = sm.auth_user_oauth(self.userinfo_alice)
|
|
300
|
-
|
|
301
|
-
# validate - user was allowed to log in
|
|
302
|
-
self.assertIsInstance(user, sm.user_model)
|
|
303
|
-
|
|
304
|
-
# validate - user was given no roles
|
|
305
|
-
self.assertListEqual(user.roles, [])
|
|
306
|
-
|
|
307
|
-
def test__registered__multi_role__with_role_sync(self):
|
|
308
|
-
"""
|
|
309
|
-
OAUTH: test login flow for - registered user - multi role mapping - with login role-sync
|
|
310
|
-
""" # noqa
|
|
311
|
-
self.app.config["AUTH_ROLES_MAPPING"] = {"GROUP_1": ["Admin", "User"]}
|
|
312
|
-
self.app.config["AUTH_ROLES_SYNC_AT_LOGIN"] = True
|
|
313
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
314
|
-
sm = self.appbuilder.sm
|
|
315
|
-
|
|
316
|
-
# add User role
|
|
317
|
-
sm.add_role("User")
|
|
318
|
-
|
|
319
|
-
# validate - no users are registered
|
|
320
|
-
self.assertEqual(sm.get_all_users(), [])
|
|
321
|
-
|
|
322
|
-
# register a user
|
|
323
|
-
new_user = sm.add_user( # noqa
|
|
324
|
-
username="alice",
|
|
325
|
-
first_name="Alice",
|
|
326
|
-
last_name="Doe",
|
|
327
|
-
email="alice@example.com",
|
|
328
|
-
role=[],
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
# validate - user was registered
|
|
332
|
-
self.assertEqual(len(sm.get_all_users()), 1)
|
|
333
|
-
|
|
334
|
-
# attempt login
|
|
335
|
-
user = sm.auth_user_oauth(self.userinfo_alice)
|
|
336
|
-
|
|
337
|
-
# validate - user was allowed to log in
|
|
338
|
-
self.assertIsInstance(user, sm.user_model)
|
|
339
|
-
|
|
340
|
-
# validate - user was given the correct roles
|
|
341
|
-
self.assertListEqual(user.roles, [sm.find_role("Admin"), sm.find_role("User")])
|
|
342
|
-
|
|
343
|
-
def test__registered__jmespath_role__no_role_sync(self):
|
|
344
|
-
"""
|
|
345
|
-
OAUTH: test login flow for - registered user - jmespath registration role - no login role-sync
|
|
346
|
-
""" # noqa
|
|
347
|
-
self.app.config["AUTH_ROLES_SYNC_AT_LOGIN"] = False
|
|
348
|
-
self.app.config["AUTH_USER_REGISTRATION"] = True
|
|
349
|
-
self.app.config[
|
|
350
|
-
"AUTH_USER_REGISTRATION_ROLE_JMESPATH"
|
|
351
|
-
] = "contains(['alice'], username) && 'User' || 'Public'"
|
|
352
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
353
|
-
sm = self.appbuilder.sm
|
|
354
|
-
|
|
355
|
-
# add User role
|
|
356
|
-
sm.add_role("User")
|
|
357
|
-
|
|
358
|
-
# validate - no users are registered
|
|
359
|
-
self.assertEqual(sm.get_all_users(), [])
|
|
360
|
-
|
|
361
|
-
# register a user
|
|
362
|
-
new_user = sm.add_user( # noqa
|
|
363
|
-
username="alice",
|
|
364
|
-
first_name="Alice",
|
|
365
|
-
last_name="Doe",
|
|
366
|
-
email="alice@example.com",
|
|
367
|
-
role=[],
|
|
368
|
-
)
|
|
369
|
-
|
|
370
|
-
# validate - user was registered
|
|
371
|
-
self.assertEqual(len(sm.get_all_users()), 1)
|
|
372
|
-
|
|
373
|
-
# attempt login
|
|
374
|
-
user = sm.auth_user_oauth(self.userinfo_alice)
|
|
375
|
-
|
|
376
|
-
# validate - user was allowed to log in
|
|
377
|
-
self.assertIsInstance(user, sm.user_model)
|
|
378
|
-
|
|
379
|
-
# validate - user was given no roles
|
|
380
|
-
self.assertListEqual(user.roles, [])
|
|
381
|
-
|
|
382
|
-
def test__registered__jmespath_role__with_role_sync(self):
|
|
383
|
-
"""
|
|
384
|
-
OAUTH: test login flow for - registered user - jmespath registration role - with login role-sync
|
|
385
|
-
""" # noqa
|
|
386
|
-
self.app.config["AUTH_ROLES_SYNC_AT_LOGIN"] = True
|
|
387
|
-
self.app.config["AUTH_USER_REGISTRATION"] = True
|
|
388
|
-
self.app.config[
|
|
389
|
-
"AUTH_USER_REGISTRATION_ROLE_JMESPATH"
|
|
390
|
-
] = "contains(['alice'], username) && 'User' || 'Public'"
|
|
391
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
392
|
-
sm = self.appbuilder.sm
|
|
393
|
-
|
|
394
|
-
# add User role
|
|
395
|
-
sm.add_role("User")
|
|
396
|
-
|
|
397
|
-
# validate - no users are registered
|
|
398
|
-
self.assertEqual(sm.get_all_users(), [])
|
|
399
|
-
|
|
400
|
-
# register a user
|
|
401
|
-
new_user = sm.add_user( # noqa
|
|
402
|
-
username="alice",
|
|
403
|
-
first_name="Alice",
|
|
404
|
-
last_name="Doe",
|
|
405
|
-
email="alice@example.com",
|
|
406
|
-
role=[],
|
|
407
|
-
)
|
|
408
|
-
|
|
409
|
-
# validate - user was registered
|
|
410
|
-
self.assertEqual(len(sm.get_all_users()), 1)
|
|
411
|
-
|
|
412
|
-
# attempt login
|
|
413
|
-
user = sm.auth_user_oauth(self.userinfo_alice)
|
|
414
|
-
|
|
415
|
-
# validate - user was allowed to log in
|
|
416
|
-
self.assertIsInstance(user, sm.user_model)
|
|
417
|
-
|
|
418
|
-
# validate - user was given the correct roles
|
|
419
|
-
self.assertListEqual(user.roles, [sm.find_role("User")])
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import unittest
|
|
3
|
-
|
|
4
|
-
from flask import Flask
|
|
5
|
-
from flask_appbuilder import AppBuilder, SQLA
|
|
6
|
-
import jinja2
|
|
7
|
-
import ldap
|
|
8
|
-
from mockldap import MockLdap
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
logging.basicConfig(format="%(asctime)s:%(levelname)s:%(name)s:%(message)s")
|
|
12
|
-
logging.getLogger().setLevel(logging.DEBUG)
|
|
13
|
-
log = logging.getLogger(__name__)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class LDAPSearchTestCase(unittest.TestCase):
|
|
17
|
-
|
|
18
|
-
top = ("o=test", {"o": ["test"]})
|
|
19
|
-
example = ("ou=example,o=test", {"ou": ["example"]})
|
|
20
|
-
manager = (
|
|
21
|
-
"cn=manager,ou=example,o=test",
|
|
22
|
-
{"cn": ["manager"], "userPassword": ["ldaptest"]},
|
|
23
|
-
)
|
|
24
|
-
alice = (
|
|
25
|
-
"cn=alice,ou=example,o=test",
|
|
26
|
-
{
|
|
27
|
-
"cn": ["alice"],
|
|
28
|
-
"memberOf": ["cn=group,ou=groups,o=test"],
|
|
29
|
-
"userPassword": ["alicepw"],
|
|
30
|
-
},
|
|
31
|
-
)
|
|
32
|
-
group = (
|
|
33
|
-
"cn=group,ou=groups,o=test",
|
|
34
|
-
{"cn": ["group"], "member": ["cn=alice,ou=example,o=test"]},
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
directory = dict([top, example, group, manager, alice])
|
|
38
|
-
|
|
39
|
-
@classmethod
|
|
40
|
-
def setUpClass(cls):
|
|
41
|
-
# We only need to create the MockLdap instance once. The content we
|
|
42
|
-
# pass in will be used for all LDAP connections.
|
|
43
|
-
cls.mockldap = MockLdap(cls.directory)
|
|
44
|
-
|
|
45
|
-
@classmethod
|
|
46
|
-
def tearDownClass(cls):
|
|
47
|
-
del cls.mockldap
|
|
48
|
-
|
|
49
|
-
def setUp(self):
|
|
50
|
-
|
|
51
|
-
self.mockldap.start()
|
|
52
|
-
self.ldapobj = self.mockldap["ldap://localhost/"]
|
|
53
|
-
|
|
54
|
-
self.app = Flask(__name__)
|
|
55
|
-
self.app.jinja_env.undefined = jinja2.StrictUndefined
|
|
56
|
-
self.db = SQLA(self.app)
|
|
57
|
-
|
|
58
|
-
self.app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
|
59
|
-
self.app.config["AUTH_LDAP_UID_FIELD"] = "cn"
|
|
60
|
-
self.app.config["AUTH_LDAP_ALLOW_SELF_SIGNED"] = False
|
|
61
|
-
self.app.config["AUTH_LDAP_USE_TLS"] = False
|
|
62
|
-
self.app.config["AUTH_LDAP_SERVER"] = "ldap://localhost/"
|
|
63
|
-
self.app.config["AUTH_LDAP_SEARCH"] = "ou=example,o=test"
|
|
64
|
-
self.app.config["AUTH_LDAP_APPEND_DOMAIN"] = False
|
|
65
|
-
self.app.config["AUTH_LDAP_FIRSTNAME_FIELD"] = None
|
|
66
|
-
self.app.config["AUTH_LDAP_LASTNAME_FIELD"] = None
|
|
67
|
-
self.app.config["AUTH_LDAP_EMAIL_FIELD"] = None
|
|
68
|
-
|
|
69
|
-
def tearDown(self):
|
|
70
|
-
self.mockldap.stop()
|
|
71
|
-
del self.ldapobj
|
|
72
|
-
log.debug("TEAR DOWN")
|
|
73
|
-
|
|
74
|
-
self.appbuilder = None
|
|
75
|
-
self.app = None
|
|
76
|
-
self.db = None
|
|
77
|
-
|
|
78
|
-
def test_ldapsearch(self):
|
|
79
|
-
con = ldap.initialize("ldap://localhost/")
|
|
80
|
-
con.simple_bind_s("cn=manager,ou=example,o=test", "ldaptest")
|
|
81
|
-
|
|
82
|
-
self.app.config["AUTH_LDAP_SEARCH_FILTER"] = ""
|
|
83
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
84
|
-
|
|
85
|
-
initialize_call = ("initialize", ("ldap://localhost/",), {})
|
|
86
|
-
simple_bind_s_call = (
|
|
87
|
-
"simple_bind_s",
|
|
88
|
-
("cn=manager,ou=example,o=test", "ldaptest"),
|
|
89
|
-
{},
|
|
90
|
-
)
|
|
91
|
-
search_s_call = (
|
|
92
|
-
"search_s",
|
|
93
|
-
("ou=example,o=test", 2, "(cn=alice)", [None, None, None]),
|
|
94
|
-
{},
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
user = self.appbuilder.sm._search_ldap(ldap, con, "alice")
|
|
98
|
-
self.assertEqual(
|
|
99
|
-
self.ldapobj.methods_called(with_args=True),
|
|
100
|
-
[initialize_call, simple_bind_s_call, search_s_call],
|
|
101
|
-
)
|
|
102
|
-
self.assertEqual(user[0][0], self.alice[0])
|
|
103
|
-
|
|
104
|
-
def test_ldapsearchfilter(self):
|
|
105
|
-
con = ldap.initialize("ldap://localhost/")
|
|
106
|
-
con.simple_bind_s("cn=manager,ou=example,o=test", "ldaptest")
|
|
107
|
-
|
|
108
|
-
self.app.config[
|
|
109
|
-
"AUTH_LDAP_SEARCH_FILTER"
|
|
110
|
-
] = "(memberOf=cn=group,ou=groups,o=test)"
|
|
111
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
112
|
-
|
|
113
|
-
initialize_call = ("initialize", ("ldap://localhost/",), {})
|
|
114
|
-
simple_bind_s_call = (
|
|
115
|
-
"simple_bind_s",
|
|
116
|
-
("cn=manager,ou=example,o=test", "ldaptest"),
|
|
117
|
-
{},
|
|
118
|
-
)
|
|
119
|
-
search_s_call = (
|
|
120
|
-
"search_s",
|
|
121
|
-
(
|
|
122
|
-
"ou=example,o=test",
|
|
123
|
-
2,
|
|
124
|
-
"(&(memberOf=cn=group,ou=groups,o=test)(cn=alice))",
|
|
125
|
-
[None, None, None],
|
|
126
|
-
),
|
|
127
|
-
{},
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
user = self.appbuilder.sm._search_ldap(ldap, con, "alice")
|
|
131
|
-
self.assertEqual(
|
|
132
|
-
self.ldapobj.methods_called(with_args=True),
|
|
133
|
-
[initialize_call, simple_bind_s_call, search_s_call],
|
|
134
|
-
)
|
|
135
|
-
self.assertEqual(user[0][0], self.alice[0])
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import unittest
|
|
3
|
-
|
|
4
|
-
from flask import Flask
|
|
5
|
-
from flask_appbuilder import AppBuilder, SQLA
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
logging.basicConfig(format="%(asctime)s:%(levelname)s:%(name)s:%(message)s")
|
|
9
|
-
logging.getLogger().setLevel(logging.DEBUG)
|
|
10
|
-
log = logging.getLogger(__name__)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class OAuthRegistrationRoleTestCase(unittest.TestCase):
|
|
14
|
-
def setUp(self):
|
|
15
|
-
self.app = Flask(__name__)
|
|
16
|
-
self.app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
|
17
|
-
self.db = SQLA(self.app)
|
|
18
|
-
|
|
19
|
-
def tearDown(self):
|
|
20
|
-
self.appbuilder = None
|
|
21
|
-
self.app = None
|
|
22
|
-
self.db = None
|
|
23
|
-
|
|
24
|
-
def test_self_registration_not_enabled(self):
|
|
25
|
-
self.app.config["AUTH_USER_REGISTRATION"] = False
|
|
26
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
27
|
-
|
|
28
|
-
result = self.appbuilder.sm.auth_user_oauth(userinfo={"username": "testuser"})
|
|
29
|
-
|
|
30
|
-
self.assertIsNone(result)
|
|
31
|
-
self.assertEqual(len(self.appbuilder.sm.get_all_users()), 0)
|
|
32
|
-
|
|
33
|
-
def test_register_and_attach_static_role(self):
|
|
34
|
-
self.app.config["AUTH_USER_REGISTRATION"] = True
|
|
35
|
-
self.app.config["AUTH_USER_REGISTRATION_ROLE"] = "Public"
|
|
36
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
37
|
-
|
|
38
|
-
user = self.appbuilder.sm.auth_user_oauth(userinfo={"username": "testuser"})
|
|
39
|
-
|
|
40
|
-
self.assertEqual(user.roles[0].name, "Public")
|
|
41
|
-
|
|
42
|
-
def test_register_and_attach_dynamic_role(self):
|
|
43
|
-
self.app.config["AUTH_USER_REGISTRATION"] = True
|
|
44
|
-
self.app.config[
|
|
45
|
-
"AUTH_USER_REGISTRATION_ROLE_JMESPATH"
|
|
46
|
-
] = "contains(['alice', 'celine'], username) && 'Admin' || 'Public'"
|
|
47
|
-
self.appbuilder = AppBuilder(self.app, self.db.session)
|
|
48
|
-
|
|
49
|
-
# Role for admin
|
|
50
|
-
user = self.appbuilder.sm.auth_user_oauth(
|
|
51
|
-
userinfo={"username": "alice", "email": "alice@example.com"}
|
|
52
|
-
)
|
|
53
|
-
self.assertEqual(user.roles[0].name, "Admin")
|
|
54
|
-
|
|
55
|
-
# Role for non-admin
|
|
56
|
-
user = self.appbuilder.sm.auth_user_oauth(
|
|
57
|
-
userinfo={"username": "bob", "email": "bob@example.com"}
|
|
58
|
-
)
|
|
59
|
-
self.assertEqual(user.roles[0].name, "Public")
|
flask_appbuilder/tests/app.db
DELETED
|
Binary file
|
flask_appbuilder/tests/base.py
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import unittest
|
|
3
|
-
|
|
4
|
-
from flask_appbuilder.const import (
|
|
5
|
-
API_SECURITY_PASSWORD_KEY,
|
|
6
|
-
API_SECURITY_PROVIDER_KEY,
|
|
7
|
-
API_SECURITY_USERNAME_KEY,
|
|
8
|
-
API_SECURITY_VERSION,
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class FABTestCase(unittest.TestCase):
|
|
13
|
-
@staticmethod
|
|
14
|
-
def auth_client_get(client, token, uri):
|
|
15
|
-
return client.get(uri, headers={"Authorization": "Bearer {}".format(token)})
|
|
16
|
-
|
|
17
|
-
@staticmethod
|
|
18
|
-
def auth_client_delete(client, token, uri):
|
|
19
|
-
return client.delete(uri, headers={"Authorization": "Bearer {}".format(token)})
|
|
20
|
-
|
|
21
|
-
@staticmethod
|
|
22
|
-
def auth_client_put(client, token, uri, json):
|
|
23
|
-
return client.put(
|
|
24
|
-
uri, json=json, headers={"Authorization": "Bearer {}".format(token)}
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
@staticmethod
|
|
28
|
-
def auth_client_post(client, token, uri, json):
|
|
29
|
-
return client.post(
|
|
30
|
-
uri, json=json, headers={"Authorization": "Bearer {}".format(token)}
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
@staticmethod
|
|
34
|
-
def _login(client, username, password):
|
|
35
|
-
"""
|
|
36
|
-
Login help method
|
|
37
|
-
:param client: Flask test client
|
|
38
|
-
:param username: username
|
|
39
|
-
:param password: password
|
|
40
|
-
:return: Flask client response class
|
|
41
|
-
"""
|
|
42
|
-
return client.post(
|
|
43
|
-
"api/{}/security/login".format(API_SECURITY_VERSION),
|
|
44
|
-
data=json.dumps(
|
|
45
|
-
{
|
|
46
|
-
API_SECURITY_USERNAME_KEY: username,
|
|
47
|
-
API_SECURITY_PASSWORD_KEY: password,
|
|
48
|
-
API_SECURITY_PROVIDER_KEY: "db",
|
|
49
|
-
}
|
|
50
|
-
),
|
|
51
|
-
content_type="application/json",
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
def login(self, client, username, password):
|
|
55
|
-
# Login with default admin
|
|
56
|
-
rv = self._login(client, username, password)
|
|
57
|
-
try:
|
|
58
|
-
return json.loads(rv.data.decode("utf-8")).get("access_token")
|
|
59
|
-
except Exception:
|
|
60
|
-
return rv
|
|
61
|
-
|
|
62
|
-
def browser_login(self, client, username, password):
|
|
63
|
-
# Login with default admin
|
|
64
|
-
return client.post(
|
|
65
|
-
"/login/",
|
|
66
|
-
data=dict(username=username, password=password),
|
|
67
|
-
follow_redirects=True,
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
@staticmethod
|
|
71
|
-
def browser_logout(client):
|
|
72
|
-
return client.get("/logout/")
|
|
73
|
-
|
|
74
|
-
def create_admin_user(self, appbuilder, username, password):
|
|
75
|
-
self.create_user(appbuilder, username, password, "Admin")
|
|
76
|
-
|
|
77
|
-
@staticmethod
|
|
78
|
-
def create_user(
|
|
79
|
-
appbuilder,
|
|
80
|
-
username,
|
|
81
|
-
password,
|
|
82
|
-
role_name,
|
|
83
|
-
first_name="admin",
|
|
84
|
-
last_name="user",
|
|
85
|
-
email="admin@fab.org",
|
|
86
|
-
):
|
|
87
|
-
role_admin = appbuilder.sm.find_role(role_name)
|
|
88
|
-
appbuilder.sm.add_user(
|
|
89
|
-
username, first_name, last_name, email, role_admin, password
|
|
90
|
-
)
|