flask-appbuilder 3.2.1rc1__py3-none-any.whl → 5.0.2rc1__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.
Files changed (228) hide show
  1. flask_appbuilder/__init__.py +2 -3
  2. flask_appbuilder/_compat.py +0 -1
  3. flask_appbuilder/actions.py +14 -14
  4. flask_appbuilder/api/__init__.py +741 -527
  5. flask_appbuilder/api/convert.py +104 -98
  6. flask_appbuilder/api/manager.py +14 -8
  7. flask_appbuilder/api/schemas.py +12 -1
  8. flask_appbuilder/babel/manager.py +12 -16
  9. flask_appbuilder/base.py +353 -280
  10. flask_appbuilder/basemanager.py +1 -1
  11. flask_appbuilder/baseviews.py +241 -164
  12. flask_appbuilder/charts/jsontools.py +10 -10
  13. flask_appbuilder/charts/views.py +56 -60
  14. flask_appbuilder/cli.py +115 -70
  15. flask_appbuilder/const.py +52 -52
  16. flask_appbuilder/exceptions.py +67 -5
  17. flask_appbuilder/fields.py +32 -23
  18. flask_appbuilder/fieldwidgets.py +34 -27
  19. flask_appbuilder/filemanager.py +33 -45
  20. flask_appbuilder/filters.py +11 -13
  21. flask_appbuilder/forms.py +31 -35
  22. flask_appbuilder/hooks.py +90 -0
  23. flask_appbuilder/menu.py +35 -10
  24. flask_appbuilder/models/base.py +47 -57
  25. flask_appbuilder/models/decorators.py +13 -13
  26. flask_appbuilder/models/filters.py +42 -38
  27. flask_appbuilder/models/generic/__init__.py +29 -29
  28. flask_appbuilder/models/generic/filters.py +11 -3
  29. flask_appbuilder/models/generic/interface.py +1 -3
  30. flask_appbuilder/models/group.py +37 -39
  31. flask_appbuilder/models/mixins.py +22 -18
  32. flask_appbuilder/models/sqla/__init__.py +19 -72
  33. flask_appbuilder/models/sqla/base.py +24 -0
  34. flask_appbuilder/models/sqla/base_legacy.py +132 -0
  35. flask_appbuilder/models/sqla/filters.py +132 -19
  36. flask_appbuilder/models/sqla/interface.py +390 -276
  37. flask_appbuilder/security/api.py +31 -35
  38. flask_appbuilder/security/decorators.py +181 -83
  39. flask_appbuilder/security/forms.py +20 -31
  40. flask_appbuilder/security/manager.py +715 -489
  41. flask_appbuilder/security/registerviews.py +29 -112
  42. flask_appbuilder/security/schemas.py +43 -0
  43. flask_appbuilder/security/sqla/apis/__init__.py +8 -0
  44. flask_appbuilder/security/sqla/apis/group/__init__.py +1 -0
  45. flask_appbuilder/security/sqla/apis/group/api.py +227 -0
  46. flask_appbuilder/security/sqla/apis/group/schema.py +73 -0
  47. flask_appbuilder/security/sqla/apis/permission/__init__.py +1 -0
  48. flask_appbuilder/security/sqla/apis/permission/api.py +19 -0
  49. flask_appbuilder/security/sqla/apis/permission_view_menu/__init__.py +1 -0
  50. flask_appbuilder/security/sqla/apis/permission_view_menu/api.py +16 -0
  51. flask_appbuilder/security/sqla/apis/role/__init__.py +1 -0
  52. flask_appbuilder/security/sqla/apis/role/api.py +306 -0
  53. flask_appbuilder/security/sqla/apis/role/schema.py +27 -0
  54. flask_appbuilder/security/sqla/apis/user/__init__.py +1 -0
  55. flask_appbuilder/security/sqla/apis/user/api.py +292 -0
  56. flask_appbuilder/security/sqla/apis/user/schema.py +97 -0
  57. flask_appbuilder/security/sqla/apis/user/validator.py +27 -0
  58. flask_appbuilder/security/sqla/apis/view_menu/__init__.py +1 -0
  59. flask_appbuilder/security/sqla/apis/view_menu/api.py +18 -0
  60. flask_appbuilder/security/sqla/manager.py +421 -203
  61. flask_appbuilder/security/sqla/models.py +192 -57
  62. flask_appbuilder/security/utils.py +9 -0
  63. flask_appbuilder/security/views.py +232 -229
  64. flask_appbuilder/static/.DS_Store +0 -0
  65. flask_appbuilder/static/appbuilder/css/ab.css +20 -12
  66. flask_appbuilder/static/appbuilder/css/bootstrap-datepicker/bootstrap-datepicker3.min.css +7 -0
  67. flask_appbuilder/static/appbuilder/css/bootstrap.min.css.map +1 -0
  68. flask_appbuilder/static/appbuilder/css/flags/flags16.css +249 -245
  69. flask_appbuilder/static/appbuilder/css/fontawesome/all.min.css +6 -0
  70. flask_appbuilder/static/appbuilder/css/fontawesome/brands.min.css +6 -0
  71. flask_appbuilder/static/appbuilder/css/fontawesome/fontawesome.min.css +6 -0
  72. flask_appbuilder/static/appbuilder/css/fontawesome/regular.min.css +6 -0
  73. flask_appbuilder/static/appbuilder/css/fontawesome/solid.min.css +6 -0
  74. flask_appbuilder/static/appbuilder/css/fontawesome/svg-with-js.min.css +6 -0
  75. flask_appbuilder/static/appbuilder/css/fontawesome/v4-font-face.min.css +6 -0
  76. flask_appbuilder/static/appbuilder/css/fontawesome/v4-shims.min.css +6 -0
  77. flask_appbuilder/static/appbuilder/css/fontawesome/v5-font-face.min.css +6 -0
  78. flask_appbuilder/static/appbuilder/css/images/flags16.png +0 -0
  79. flask_appbuilder/static/appbuilder/css/select2/select2-bootstrap.min.css +7 -0
  80. flask_appbuilder/static/appbuilder/css/select2/select2.min.css +1 -0
  81. flask_appbuilder/static/appbuilder/css/swagger/swagger-ui.css +3 -0
  82. flask_appbuilder/static/appbuilder/css/webfonts/fa-brands-400.ttf +0 -0
  83. flask_appbuilder/static/appbuilder/css/webfonts/fa-brands-400.woff2 +0 -0
  84. flask_appbuilder/static/appbuilder/css/webfonts/fa-regular-400.ttf +0 -0
  85. flask_appbuilder/static/appbuilder/css/webfonts/fa-regular-400.woff2 +0 -0
  86. flask_appbuilder/static/appbuilder/css/webfonts/fa-solid-900.ttf +0 -0
  87. flask_appbuilder/static/appbuilder/css/webfonts/fa-solid-900.woff2 +0 -0
  88. flask_appbuilder/static/appbuilder/css/webfonts/fa-v4compatibility.ttf +0 -0
  89. flask_appbuilder/static/appbuilder/css/webfonts/fa-v4compatibility.woff2 +0 -0
  90. flask_appbuilder/static/appbuilder/js/ab.js +33 -23
  91. flask_appbuilder/static/appbuilder/js/ab_filters.js +91 -84
  92. flask_appbuilder/static/appbuilder/js/bootstrap-datepicker/bootstrap-datepicker.min.js +8 -0
  93. flask_appbuilder/static/appbuilder/js/jquery-latest.js +2 -2
  94. flask_appbuilder/static/appbuilder/js/select2/select2.min.js +2 -0
  95. flask_appbuilder/static/appbuilder/js/swagger-ui-bundle.js +3 -0
  96. flask_appbuilder/templates/appbuilder/baselib.html +9 -3
  97. flask_appbuilder/templates/appbuilder/general/lib.html +60 -34
  98. flask_appbuilder/templates/appbuilder/general/model/edit.html +1 -1
  99. flask_appbuilder/templates/appbuilder/general/model/edit_cascade.html +1 -1
  100. flask_appbuilder/templates/appbuilder/general/model/search.html +3 -2
  101. flask_appbuilder/templates/appbuilder/general/model/show.html +1 -1
  102. flask_appbuilder/templates/appbuilder/general/model/show_cascade.html +1 -1
  103. flask_appbuilder/templates/appbuilder/general/security/login_db.html +7 -7
  104. flask_appbuilder/templates/appbuilder/general/security/login_ldap.html +5 -5
  105. flask_appbuilder/templates/appbuilder/general/security/login_oauth.html +24 -49
  106. flask_appbuilder/templates/appbuilder/general/widgets/base_list.html +2 -1
  107. flask_appbuilder/templates/appbuilder/general/widgets/chart.html +4 -2
  108. flask_appbuilder/templates/appbuilder/general/widgets/direct_chart.html +4 -3
  109. flask_appbuilder/templates/appbuilder/general/widgets/multiple_chart.html +3 -2
  110. flask_appbuilder/templates/appbuilder/general/widgets/search.html +11 -10
  111. flask_appbuilder/templates/appbuilder/init.html +37 -43
  112. flask_appbuilder/templates/appbuilder/navbar_menu.html +1 -1
  113. flask_appbuilder/templates/appbuilder/navbar_right.html +2 -2
  114. flask_appbuilder/templates/appbuilder/swagger/swagger.html +22 -19
  115. flask_appbuilder/translations/de/LC_MESSAGES/messages.mo +0 -0
  116. flask_appbuilder/translations/de/LC_MESSAGES/messages.po +305 -161
  117. flask_appbuilder/translations/fa/LC_MESSAGES/messages.mo +0 -0
  118. flask_appbuilder/translations/fa/LC_MESSAGES/messages.po +802 -0
  119. flask_appbuilder/translations/fr/LC_MESSAGES/messages.po +461 -319
  120. flask_appbuilder/translations/pt_BR/LC_MESSAGES/messages.po +650 -650
  121. flask_appbuilder/translations/ru/LC_MESSAGES/messages.po +1 -1
  122. flask_appbuilder/translations/sl/LC_MESSAGES/messages.mo +0 -0
  123. flask_appbuilder/translations/sl/LC_MESSAGES/messages.po +690 -0
  124. flask_appbuilder/translations/tr/LC_MESSAGES/messages.mo +0 -0
  125. flask_appbuilder/translations/tr/LC_MESSAGES/messages.po +1015 -0
  126. flask_appbuilder/upload.py +20 -22
  127. flask_appbuilder/urltools.py +39 -19
  128. flask_appbuilder/utils/base.py +76 -0
  129. flask_appbuilder/utils/legacy.py +33 -0
  130. flask_appbuilder/utils/limit.py +20 -0
  131. flask_appbuilder/validators.py +73 -14
  132. flask_appbuilder/views.py +75 -424
  133. flask_appbuilder/widgets.py +50 -51
  134. {Flask_AppBuilder-3.2.1rc1.dist-info → flask_appbuilder-5.0.2rc1.dist-info}/METADATA +36 -76
  135. flask_appbuilder-5.0.2rc1.dist-info/RECORD +240 -0
  136. {Flask_AppBuilder-3.2.1rc1.dist-info → flask_appbuilder-5.0.2rc1.dist-info}/WHEEL +1 -1
  137. flask_appbuilder-5.0.2rc1.dist-info/entry_points.txt +2 -0
  138. Flask_AppBuilder-3.2.1rc1.dist-info/RECORD +0 -270
  139. Flask_AppBuilder-3.2.1rc1.dist-info/entry_points.txt +0 -6
  140. flask_appbuilder/console.py +0 -426
  141. flask_appbuilder/models/mongoengine/__init__.py +0 -0
  142. flask_appbuilder/models/mongoengine/fields.py +0 -65
  143. flask_appbuilder/models/mongoengine/filters.py +0 -145
  144. flask_appbuilder/models/mongoengine/interface.py +0 -328
  145. flask_appbuilder/security/mongoengine/__init__.py +0 -0
  146. flask_appbuilder/security/mongoengine/manager.py +0 -402
  147. flask_appbuilder/security/mongoengine/models.py +0 -120
  148. flask_appbuilder/static/appbuilder/css/font-awesome.min.css +0 -4
  149. flask_appbuilder/static/appbuilder/datepicker/bootstrap-datepicker.css +0 -9
  150. flask_appbuilder/static/appbuilder/datepicker/bootstrap-datepicker.js +0 -28
  151. flask_appbuilder/static/appbuilder/fonts/FontAwesome.otf +0 -0
  152. flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.eot +0 -0
  153. flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.svg +0 -2671
  154. flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.ttf +0 -0
  155. flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.woff +0 -0
  156. flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.woff2 +0 -0
  157. flask_appbuilder/static/appbuilder/img/aol.png +0 -0
  158. flask_appbuilder/static/appbuilder/img/flags/flags16.png +0 -0
  159. flask_appbuilder/static/appbuilder/img/flickr.png +0 -0
  160. flask_appbuilder/static/appbuilder/img/google.png +0 -0
  161. flask_appbuilder/static/appbuilder/img/myopenid.png +0 -0
  162. flask_appbuilder/static/appbuilder/img/yahoo.png +0 -0
  163. flask_appbuilder/static/appbuilder/js/_google_charts.js +0 -39
  164. flask_appbuilder/static/appbuilder/js/html5shiv.js +0 -8
  165. flask_appbuilder/static/appbuilder/js/respond.min.js +0 -6
  166. flask_appbuilder/static/appbuilder/select2/select2-spinner.gif +0 -0
  167. flask_appbuilder/static/appbuilder/select2/select2.css +0 -1205
  168. flask_appbuilder/static/appbuilder/select2/select2.js +0 -23
  169. flask_appbuilder/static/appbuilder/select2/select2.png +0 -0
  170. flask_appbuilder/static/appbuilder/select2/select2x2.png +0 -0
  171. flask_appbuilder/templates/appbuilder/general/security/login_oid.html +0 -129
  172. flask_appbuilder/templates/appbuilder/general/security/resetpassword.html +0 -29
  173. flask_appbuilder/tests/__init__.py +0 -0
  174. flask_appbuilder/tests/__pycache__/__init__.cpython-36.pyc +0 -0
  175. flask_appbuilder/tests/__pycache__/__init__.cpython-37.pyc +0 -0
  176. flask_appbuilder/tests/__pycache__/_test_auth_ldap.cpython-37.pyc +0 -0
  177. flask_appbuilder/tests/__pycache__/_test_auth_oauth.cpython-37.pyc +0 -0
  178. flask_appbuilder/tests/__pycache__/_test_ldapsearch.cpython-36.pyc +0 -0
  179. flask_appbuilder/tests/__pycache__/_test_oauth_registration_role.cpython-36.pyc +0 -0
  180. flask_appbuilder/tests/__pycache__/base.cpython-36.pyc +0 -0
  181. flask_appbuilder/tests/__pycache__/base.cpython-37.pyc +0 -0
  182. flask_appbuilder/tests/__pycache__/config_api.cpython-36.pyc +0 -0
  183. flask_appbuilder/tests/__pycache__/config_api.cpython-37.pyc +0 -0
  184. flask_appbuilder/tests/__pycache__/const.cpython-36.pyc +0 -0
  185. flask_appbuilder/tests/__pycache__/const.cpython-37.pyc +0 -0
  186. flask_appbuilder/tests/__pycache__/test_0_fixture.cpython-36.pyc +0 -0
  187. flask_appbuilder/tests/__pycache__/test_0_fixture.cpython-37.pyc +0 -0
  188. flask_appbuilder/tests/__pycache__/test_api.cpython-36.pyc +0 -0
  189. flask_appbuilder/tests/__pycache__/test_api.cpython-37.pyc +0 -0
  190. flask_appbuilder/tests/__pycache__/test_fab_cli.cpython-36.pyc +0 -0
  191. flask_appbuilder/tests/__pycache__/test_fab_cli.cpython-37.pyc +0 -0
  192. flask_appbuilder/tests/__pycache__/test_menu.cpython-36.pyc +0 -0
  193. flask_appbuilder/tests/__pycache__/test_menu.cpython-37.pyc +0 -0
  194. flask_appbuilder/tests/__pycache__/test_mongoengine.cpython-36.pyc +0 -0
  195. flask_appbuilder/tests/__pycache__/test_mvc.cpython-36.pyc +0 -0
  196. flask_appbuilder/tests/__pycache__/test_mvc.cpython-37.pyc +0 -0
  197. flask_appbuilder/tests/__pycache__/test_sqlalchemy.cpython-36.pyc +0 -0
  198. flask_appbuilder/tests/__pycache__/test_sqlalchemy.cpython-37.pyc +0 -0
  199. flask_appbuilder/tests/_test_auth_ldap.py +0 -1045
  200. flask_appbuilder/tests/_test_auth_oauth.py +0 -419
  201. flask_appbuilder/tests/_test_ldapsearch.py +0 -135
  202. flask_appbuilder/tests/_test_oauth_registration_role.py +0 -59
  203. flask_appbuilder/tests/app.db +0 -0
  204. flask_appbuilder/tests/base.py +0 -90
  205. flask_appbuilder/tests/config_api.py +0 -21
  206. flask_appbuilder/tests/const.py +0 -9
  207. flask_appbuilder/tests/mongoengine/__init__.py +0 -0
  208. flask_appbuilder/tests/mongoengine/__pycache__/__init__.cpython-36.pyc +0 -0
  209. flask_appbuilder/tests/mongoengine/__pycache__/__init__.cpython-37.pyc +0 -0
  210. flask_appbuilder/tests/mongoengine/__pycache__/models.cpython-36.pyc +0 -0
  211. flask_appbuilder/tests/mongoengine/models.py +0 -41
  212. flask_appbuilder/tests/sqla/__init__.py +0 -0
  213. flask_appbuilder/tests/sqla/__pycache__/__init__.cpython-36.pyc +0 -0
  214. flask_appbuilder/tests/sqla/__pycache__/__init__.cpython-37.pyc +0 -0
  215. flask_appbuilder/tests/sqla/__pycache__/models.cpython-36.pyc +0 -0
  216. flask_appbuilder/tests/sqla/__pycache__/models.cpython-37.pyc +0 -0
  217. flask_appbuilder/tests/sqla/models.py +0 -340
  218. flask_appbuilder/tests/test_0_fixture.py +0 -39
  219. flask_appbuilder/tests/test_api.py +0 -2790
  220. flask_appbuilder/tests/test_fab_cli.py +0 -72
  221. flask_appbuilder/tests/test_menu.py +0 -122
  222. flask_appbuilder/tests/test_mongoengine.py +0 -572
  223. flask_appbuilder/tests/test_mvc.py +0 -1710
  224. flask_appbuilder/tests/test_sqlalchemy.py +0 -24
  225. flask_appbuilder/translations/__pycache__/__init__.cpython-36.pyc +0 -0
  226. flask_appbuilder/translations/es/LC_MESSAGES/messages.po~ +0 -582
  227. {Flask_AppBuilder-3.2.1rc1.dist-info → flask_appbuilder-5.0.2rc1.dist-info}/LICENSE +0 -0
  228. {Flask_AppBuilder-3.2.1rc1.dist-info → flask_appbuilder-5.0.2rc1.dist-info}/top_level.txt +0 -0
@@ -1,1045 +0,0 @@
1
- import logging
2
- import os
3
- import unittest
4
- from unittest.mock import patch
5
-
6
- from flask import Flask
7
- from flask_appbuilder import AppBuilder, SQLA
8
- from flask_appbuilder.security.manager import AUTH_LDAP
9
- import jinja2
10
- import ldap
11
- from mockldap import MockLdap
12
-
13
- logging.basicConfig(format="%(asctime)s:%(levelname)s:%(name)s:%(message)s")
14
- logging.getLogger().setLevel(logging.DEBUG)
15
- log = logging.getLogger(__name__)
16
-
17
-
18
- class LDAPSearchTestCase(unittest.TestCase):
19
- @classmethod
20
- def setUpClass(cls):
21
- cls.mockldap = MockLdap(cls.directory)
22
-
23
- @classmethod
24
- def tearDownClass(cls):
25
- del cls.mockldap
26
-
27
- def setUp(self):
28
- # start MockLdap
29
- self.mockldap.start()
30
- self.ldapobj = self.mockldap["ldap://localhost/"]
31
-
32
- # start Flask
33
- self.app = Flask(__name__)
34
- self.app.jinja_env.undefined = jinja2.StrictUndefined
35
- self.app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get(
36
- "SQLALCHEMY_DATABASE_URI"
37
- )
38
- self.app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
39
- self.app.config["AUTH_TYPE"] = AUTH_LDAP
40
- self.app.config["AUTH_LDAP_SERVER"] = "ldap://localhost/"
41
- self.app.config["AUTH_LDAP_UID_FIELD"] = "uid"
42
- self.app.config["AUTH_LDAP_FIRSTNAME_FIELD"] = "givenName"
43
- self.app.config["AUTH_LDAP_LASTNAME_FIELD"] = "sn"
44
- self.app.config["AUTH_LDAP_EMAIL_FIELD"] = "email"
45
-
46
- # start Database
47
- self.db = SQLA(self.app)
48
-
49
- def tearDown(self):
50
- # stop MockLdap
51
- self.mockldap.stop()
52
- del self.ldapobj
53
-
54
- # stop Flask
55
- self.app = None
56
-
57
- # stop Flask-AppBuilder
58
- self.appbuilder = None
59
-
60
- # stop Database
61
- self.db.session.remove()
62
- self.db.drop_all()
63
- self.db = None
64
-
65
- # ----------------
66
- # LDAP Directory
67
- # ----------------
68
- top = ("o=test", {"o": ["test"]})
69
- ou_users = ("ou=users,o=test", {"ou": ["users"]})
70
- ou_groups = ("ou=groups,o=test", {"ou": ["groups"]})
71
- user_admin = (
72
- "uid=admin,ou=users,o=test",
73
- {"uid": ["admin"], "userPassword": ["admin_password"]},
74
- )
75
- user_alice = (
76
- "uid=alice,ou=users,o=test",
77
- {
78
- "uid": ["alice"],
79
- "userPassword": ["alice_password"],
80
- "memberOf": [b"cn=staff,ou=groups,o=test"],
81
- "givenName": [b"Alice"],
82
- "sn": [b"Doe"],
83
- "email": [b"alice@example.com"],
84
- },
85
- )
86
- group_admins = (
87
- "cn=admins,ou=groups,o=test",
88
- {"cn": ["admins"], "member": [user_admin[0]]},
89
- )
90
- group_staff = (
91
- "cn=staff,ou=groups,o=test",
92
- {"cn": ["staff"], "member": [user_alice[0]]},
93
- )
94
- directory = dict(
95
- [top, ou_users, ou_groups, user_admin, user_alice, group_admins, group_staff]
96
- )
97
-
98
- # ----------------
99
- # LDAP Queries
100
- # ----------------
101
- call_initialize = ("initialize", tuple(["ldap://localhost/"]), {})
102
-
103
- call_set_option = ("set_option", tuple([ldap.OPT_REFERRALS, 0]), {})
104
- call_bind_admin = (
105
- "simple_bind_s",
106
- tuple(["uid=admin,ou=users,o=test", "admin_password"]),
107
- {},
108
- )
109
- call_bind_alice = (
110
- "simple_bind_s",
111
- tuple(["uid=alice,ou=users,o=test", "alice_password"]),
112
- {},
113
- )
114
- call_search_alice = (
115
- "search_s",
116
- tuple(["ou=users,o=test", 2, "(uid=alice)", ["givenName", "sn", "email"]]),
117
- {},
118
- )
119
- call_search_alice_memberof = (
120
- "search_s",
121
- tuple(
122
- [
123
- "ou=users,o=test",
124
- 2,
125
- "(uid=alice)",
126
- ["givenName", "sn", "email", "memberOf"],
127
- ]
128
- ),
129
- {},
130
- )
131
- call_search_alice_filter = (
132
- "search_s",
133
- tuple(
134
- [
135
- "ou=users,o=test",
136
- 2,
137
- "(&(memberOf=cn=staff,ou=groups,o=test)(uid=alice))",
138
- ["givenName", "sn", "email"],
139
- ]
140
- ),
141
- {},
142
- )
143
-
144
- # ----------------
145
- # Unit Tests
146
- # ----------------
147
- def test___search_ldap(self):
148
- """
149
- LDAP: test `_search_ldap` method
150
- """
151
- self.app.config["AUTH_LDAP_BIND_USER"] = "uid=admin,ou=users,o=test"
152
- self.app.config["AUTH_LDAP_BIND_PASSWORD"] = "admin_password"
153
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
154
- self.appbuilder = AppBuilder(self.app, self.db.session)
155
- sm = self.appbuilder.sm
156
-
157
- # prepare `con` object
158
- con = ldap.initialize("ldap://localhost/")
159
- sm._ldap_bind_indirect(ldap, con)
160
-
161
- # run `_search_ldap` method
162
- user_dn, user_attributes = sm._search_ldap(ldap, con, "alice")
163
-
164
- # validate - search returned expected data
165
- self.assertEqual(user_dn, self.user_alice[0])
166
- self.assertEqual(user_attributes["givenName"], self.user_alice[1]["givenName"])
167
- self.assertEqual(user_attributes["sn"], self.user_alice[1]["sn"])
168
- self.assertEqual(user_attributes["email"], self.user_alice[1]["email"])
169
-
170
- # validate - expected LDAP methods were called
171
- self.assertEqual(
172
- self.ldapobj.methods_called(with_args=True),
173
- [self.call_initialize, self.call_bind_admin, self.call_search_alice],
174
- )
175
-
176
- def test___search_ldap_filter(self):
177
- """
178
- LDAP: test `_search_ldap` method (with AUTH_LDAP_SEARCH_FILTER)
179
- """
180
- # MockLdap needs non-bytes for search filters, so we patch `memberOf`
181
- # to a string, only for this test
182
- with patch.dict(
183
- self.directory[self.user_alice[0]],
184
- {
185
- "memberOf": [
186
- i.decode() for i in self.directory[self.user_alice[0]]["memberOf"]
187
- ]
188
- },
189
- ):
190
- _mockldap = MockLdap(self.directory)
191
- _mockldap.start()
192
- _ldapobj = _mockldap["ldap://localhost/"]
193
-
194
- self.app.config["AUTH_LDAP_BIND_USER"] = "uid=admin,ou=users,o=test"
195
- self.app.config["AUTH_LDAP_BIND_PASSWORD"] = "admin_password"
196
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
197
- self.app.config[
198
- "AUTH_LDAP_SEARCH_FILTER"
199
- ] = "(memberOf=cn=staff,ou=groups,o=test)"
200
- self.appbuilder = AppBuilder(self.app, self.db.session)
201
- sm = self.appbuilder.sm
202
-
203
- # prepare `con` object
204
- con = ldap.initialize("ldap://localhost/")
205
- sm._ldap_bind_indirect(ldap, con)
206
-
207
- # run `_search_ldap` method
208
- user_dn, user_info = sm._search_ldap(ldap, con, "alice")
209
-
210
- # validate - search returned expected data
211
- self.assertEqual(user_dn, self.user_alice[0])
212
- self.assertEqual(user_info["givenName"], self.user_alice[1]["givenName"])
213
- self.assertEqual(user_info["sn"], self.user_alice[1]["sn"])
214
- self.assertEqual(user_info["email"], self.user_alice[1]["email"])
215
-
216
- # validate - expected LDAP methods were called
217
- self.assertEqual(
218
- _ldapobj.methods_called(with_args=True),
219
- [
220
- self.call_initialize,
221
- self.call_bind_admin,
222
- self.call_search_alice_filter,
223
- ],
224
- )
225
-
226
- def test__missing_credentials(self):
227
- """
228
- LDAP: test login flow for - missing credentials
229
- """
230
- self.appbuilder = AppBuilder(self.app, self.db.session)
231
- sm = self.appbuilder.sm
232
-
233
- # validate - no users are registered
234
- self.assertEqual(sm.get_all_users(), [])
235
-
236
- # validate - login failure (missing username)
237
- self.assertIsNone(sm.auth_user_ldap(None, "password"))
238
- self.assertIsNone(sm.auth_user_ldap("", "password"))
239
-
240
- # validate - login failure (missing password)
241
- self.assertIsNone(sm.auth_user_ldap("username", None))
242
- self.assertIsNone(sm.auth_user_ldap("username", ""))
243
-
244
- # validate - login failure (missing username/password)
245
- self.assertIsNone(sm.auth_user_ldap(None, None))
246
- self.assertIsNone(sm.auth_user_ldap("", None))
247
- self.assertIsNone(sm.auth_user_ldap("", ""))
248
- self.assertIsNone(sm.auth_user_ldap(None, ""))
249
-
250
- # validate - no users were created
251
- self.assertEqual(sm.get_all_users(), [])
252
-
253
- # validate - expected LDAP methods were called
254
- self.assertEquals(self.ldapobj.methods_called(with_args=True), [])
255
-
256
- def test__inactive_user(self):
257
- """
258
- LDAP: test login flow for - inactive user
259
- """
260
- self.appbuilder = AppBuilder(self.app, self.db.session)
261
- sm = self.appbuilder.sm
262
-
263
- # validate - no users are registered
264
- self.assertEqual(sm.get_all_users(), [])
265
-
266
- # register a user
267
- new_user = sm.add_user(
268
- username="alice",
269
- first_name="Alice",
270
- last_name="Doe",
271
- email="alice@example.com",
272
- role=[],
273
- )
274
-
275
- # validate - user was registered
276
- self.assertEqual(len(sm.get_all_users()), 1)
277
-
278
- # set user inactive
279
- new_user.active = False
280
-
281
- # attempt login
282
- user = sm.auth_user_ldap("alice", "alice_password")
283
-
284
- # validate - user was not allowed to log in
285
- self.assertIsNone(user)
286
-
287
- # validate - expected LDAP methods were called
288
- self.assertEquals(self.ldapobj.methods_called(with_args=True), [])
289
-
290
- def test__direct_bind__unregistered(self):
291
- """
292
- LDAP: test login flow for - direct bind - unregistered user
293
- """
294
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
295
- self.app.config["AUTH_LDAP_USERNAME_FORMAT"] = "uid=%s,ou=users,o=test"
296
- self.app.config["AUTH_USER_REGISTRATION"] = True
297
- self.app.config["AUTH_USER_REGISTRATION_ROLE"] = "Public"
298
- self.appbuilder = AppBuilder(self.app, self.db.session)
299
- sm = self.appbuilder.sm
300
-
301
- # validate - no users are registered
302
- self.assertEqual(sm.get_all_users(), [])
303
-
304
- # attempt login
305
- user = sm.auth_user_ldap("alice", "alice_password")
306
-
307
- # validate - user was allowed to log in
308
- self.assertIsInstance(user, sm.user_model)
309
-
310
- # validate - user was registered
311
- self.assertEqual(len(sm.get_all_users()), 1)
312
-
313
- # validate - user was given the AUTH_USER_REGISTRATION_ROLE role
314
- self.assertEqual(user.roles, [sm.find_role("Public")])
315
-
316
- # validate - user was given the correct attributes (read from LDAP)
317
- self.assertEqual(user.first_name, "Alice")
318
- self.assertEqual(user.last_name, "Doe")
319
- self.assertEqual(user.email, "alice@example.com")
320
-
321
- # validate - expected LDAP methods were called
322
- self.assertEqual(
323
- self.ldapobj.methods_called(with_args=True),
324
- [
325
- self.call_initialize,
326
- self.call_set_option,
327
- self.call_bind_alice,
328
- self.call_search_alice,
329
- ],
330
- )
331
-
332
- def test__direct_bind__unregistered__no_self_register(self):
333
- """
334
- LDAP: test login flow for - direct bind - unregistered user - no self-registration
335
- """
336
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
337
- self.app.config["AUTH_LDAP_USERNAME_FORMAT"] = "uid=%s,ou=users,o=test"
338
- self.app.config["AUTH_USER_REGISTRATION"] = False
339
- self.appbuilder = AppBuilder(self.app, self.db.session)
340
- sm = self.appbuilder.sm
341
-
342
- # validate - no users are registered
343
- self.assertEqual(sm.get_all_users(), [])
344
-
345
- # attempt login
346
- user = sm.auth_user_ldap("alice", "alice_password")
347
-
348
- # validate - user was not allowed to log in
349
- self.assertIsNone(user)
350
-
351
- # validate - no users were registered
352
- self.assertEqual(sm.get_all_users(), [])
353
-
354
- # validate - expected LDAP methods were called
355
- self.assertEqual(self.ldapobj.methods_called(with_args=True), [])
356
-
357
- def test__direct_bind__unregistered__no_search(self):
358
- """
359
- LDAP: test login flow for - direct bind - unregistered user - no ldap search
360
- """
361
- self.app.config["AUTH_LDAP_SEARCH"] = None
362
- self.app.config["AUTH_LDAP_USERNAME_FORMAT"] = "uid=%s,ou=users,o=test"
363
- self.app.config["AUTH_USER_REGISTRATION"] = True
364
- self.appbuilder = AppBuilder(self.app, self.db.session)
365
- sm = self.appbuilder.sm
366
-
367
- # validate - no users are registered
368
- self.assertEqual(sm.get_all_users(), [])
369
-
370
- # attempt login
371
- user = sm.auth_user_ldap("alice", "alice_password")
372
-
373
- # validate - user was NOT allowed to log in (because registration requires search)
374
- self.assertIsNone(user)
375
-
376
- # validate - expected LDAP methods were called
377
- self.assertEqual(
378
- self.ldapobj.methods_called(with_args=True),
379
- [self.call_initialize, self.call_set_option, self.call_bind_alice],
380
- )
381
-
382
- def test__direct_bind__registered(self):
383
- """
384
- LDAP: test login flow for - direct bind - registered user
385
- """
386
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
387
- self.app.config["AUTH_LDAP_USERNAME_FORMAT"] = "uid=%s,ou=users,o=test"
388
- self.appbuilder = AppBuilder(self.app, self.db.session)
389
- sm = self.appbuilder.sm
390
-
391
- # validate - no users are registered
392
- self.assertEqual(sm.get_all_users(), [])
393
-
394
- # register a user
395
- new_user = sm.add_user( # noqa
396
- username="alice",
397
- first_name="Alice",
398
- last_name="Doe",
399
- email="alice@example.com",
400
- role=[],
401
- )
402
-
403
- # validate - user was registered
404
- self.assertEqual(len(sm.get_all_users()), 1)
405
-
406
- # attempt login
407
- user = sm.auth_user_ldap("alice", "alice_password")
408
-
409
- # validate - user was allowed to log in
410
- self.assertIsInstance(user, sm.user_model)
411
-
412
- # validate - expected LDAP methods were called
413
- self.assertEqual(
414
- self.ldapobj.methods_called(with_args=True),
415
- [
416
- self.call_initialize,
417
- self.call_set_option,
418
- self.call_bind_alice,
419
- self.call_search_alice,
420
- ],
421
- )
422
-
423
- def test__direct_bind__registered__no_search(self):
424
- """
425
- LDAP: test login flow for - direct bind - registered user - no ldap search
426
- """
427
- self.app.config["AUTH_LDAP_SEARCH"] = None
428
- self.app.config["AUTH_LDAP_USERNAME_FORMAT"] = "uid=%s,ou=users,o=test"
429
- self.appbuilder = AppBuilder(self.app, self.db.session)
430
- sm = self.appbuilder.sm
431
-
432
- # validate - no users are registered
433
- self.assertEqual(sm.get_all_users(), [])
434
-
435
- # register a user
436
- new_user = sm.add_user( # noqa
437
- username="alice",
438
- first_name="Alice",
439
- last_name="Doe",
440
- email="alice@example.com",
441
- role=[],
442
- )
443
-
444
- # validate - user was registered
445
- self.assertEqual(len(sm.get_all_users()), 1)
446
-
447
- # attempt login
448
- user = sm.auth_user_ldap("alice", "alice_password")
449
-
450
- # validate - user was allowed to log in (because they are already registered)
451
- self.assertIsInstance(user, sm.user_model)
452
-
453
- # validate - expected LDAP methods were called
454
- self.assertEqual(
455
- self.ldapobj.methods_called(with_args=True),
456
- [self.call_initialize, self.call_set_option, self.call_bind_alice],
457
- )
458
-
459
- def test__indirect_bind__unregistered(self):
460
- """
461
- LDAP: test login flow for - indirect bind - unregistered user
462
- """
463
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
464
- self.app.config["AUTH_LDAP_BIND_USER"] = "uid=admin,ou=users,o=test"
465
- self.app.config["AUTH_LDAP_BIND_PASSWORD"] = "admin_password"
466
- self.app.config["AUTH_USER_REGISTRATION"] = True
467
- self.app.config["AUTH_USER_REGISTRATION_ROLE"] = "Public"
468
- self.appbuilder = AppBuilder(self.app, self.db.session)
469
- sm = self.appbuilder.sm
470
-
471
- # validate - no users are registered
472
- self.assertEqual(sm.get_all_users(), [])
473
-
474
- # attempt login
475
- user = sm.auth_user_ldap("alice", "alice_password")
476
-
477
- # validate - user was allowed to log in
478
- self.assertIsInstance(user, sm.user_model)
479
-
480
- # validate - user was registered
481
- self.assertEqual(len(sm.get_all_users()), 1)
482
-
483
- # validate - user was given the AUTH_USER_REGISTRATION_ROLE role
484
- self.assertListEqual(user.roles, [sm.find_role("Public")])
485
-
486
- # validate - user was given the correct attributes (read from LDAP)
487
- self.assertEqual(user.first_name, "Alice")
488
- self.assertEqual(user.last_name, "Doe")
489
- self.assertEqual(user.email, "alice@example.com")
490
-
491
- # validate - expected LDAP methods were called
492
- self.assertEqual(
493
- self.ldapobj.methods_called(with_args=True),
494
- [
495
- self.call_initialize,
496
- self.call_set_option,
497
- self.call_bind_admin,
498
- self.call_search_alice,
499
- self.call_bind_alice,
500
- ],
501
- )
502
-
503
- def test__indirect_bind__unregistered__no_self_register(self):
504
- """
505
- LDAP: test login flow for - indirect bind - unregistered user - no self-registration
506
- """ # noqa
507
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
508
- self.app.config["AUTH_LDAP_BIND_USER"] = "uid=admin,ou=users,o=test"
509
- self.app.config["AUTH_LDAP_BIND_PASSWORD"] = "admin_password"
510
- self.app.config["AUTH_USER_REGISTRATION"] = False
511
- self.appbuilder = AppBuilder(self.app, self.db.session)
512
- sm = self.appbuilder.sm
513
-
514
- # validate - no users are registered
515
- self.assertEqual(sm.get_all_users(), [])
516
-
517
- # attempt login
518
- user = sm.auth_user_ldap("alice", "alice_password")
519
-
520
- # validate - user was not allowed to log in
521
- self.assertIsNone(user)
522
-
523
- # validate - no users were registered
524
- self.assertEqual(sm.get_all_users(), [])
525
-
526
- # validate - expected LDAP methods were called
527
- self.assertEqual(self.ldapobj.methods_called(with_args=True), [])
528
-
529
- def test__indirect_bind__unregistered__no_search(self):
530
- """
531
- LDAP: test login flow for - indirect bind - unregistered user - no ldap search
532
- """
533
- self.app.config["AUTH_LDAP_SEARCH"] = None
534
- self.app.config["AUTH_LDAP_BIND_USER"] = "uid=admin,ou=users,o=test"
535
- self.app.config["AUTH_LDAP_BIND_PASSWORD"] = "admin_password"
536
- self.app.config["AUTH_USER_REGISTRATION"] = True
537
- self.app.config["AUTH_USER_REGISTRATION_ROLE"] = "Public"
538
- self.appbuilder = AppBuilder(self.app, self.db.session)
539
- sm = self.appbuilder.sm
540
-
541
- # validate - no users are registered
542
- self.assertEqual(sm.get_all_users(), [])
543
-
544
- # attempt login
545
- user = sm.auth_user_ldap("alice", "alice_password")
546
-
547
- # validate - user was NOT allowed to log in
548
- # (because indirect bind requires search)
549
- self.assertIsNone(user)
550
-
551
- # validate - expected LDAP methods were called
552
- self.assertEqual(
553
- self.ldapobj.methods_called(with_args=True),
554
- [self.call_initialize, self.call_set_option, self.call_bind_admin],
555
- )
556
-
557
- def test__indirect_bind__registered(self):
558
- """
559
- LDAP: test login flow for - indirect bind - registered user
560
- """
561
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
562
- self.app.config["AUTH_LDAP_BIND_USER"] = "uid=admin,ou=users,o=test"
563
- self.app.config["AUTH_LDAP_BIND_PASSWORD"] = "admin_password"
564
- self.appbuilder = AppBuilder(self.app, self.db.session)
565
- sm = self.appbuilder.sm
566
-
567
- # validate - no users are registered
568
- self.assertEqual(sm.get_all_users(), [])
569
-
570
- # register a user
571
- new_user = sm.add_user( # noqa
572
- username="alice",
573
- first_name="Alice",
574
- last_name="Doe",
575
- email="alice@example.com",
576
- role=[],
577
- )
578
-
579
- # validate - user was registered
580
- self.assertEqual(len(sm.get_all_users()), 1)
581
-
582
- # attempt login
583
- user = sm.auth_user_ldap("alice", "alice_password")
584
-
585
- # validate - user was allowed to log in
586
- self.assertIsInstance(user, sm.user_model)
587
-
588
- # validate - expected LDAP methods were called
589
- self.assertEqual(
590
- self.ldapobj.methods_called(with_args=True),
591
- [
592
- self.call_initialize,
593
- self.call_set_option,
594
- self.call_bind_admin,
595
- self.call_search_alice,
596
- self.call_bind_alice,
597
- ],
598
- )
599
-
600
- def test__indirect_bind__registered__no_search(self):
601
- """
602
- LDAP: test login flow for - indirect bind - registered user - no ldap search
603
- """
604
- self.app.config["AUTH_LDAP_SEARCH"] = None
605
- self.app.config["AUTH_LDAP_BIND_USER"] = "uid=admin,ou=users,o=test"
606
- self.app.config["AUTH_LDAP_BIND_PASSWORD"] = "admin_password"
607
- self.appbuilder = AppBuilder(self.app, self.db.session)
608
- sm = self.appbuilder.sm
609
-
610
- # validate - no users are registered
611
- self.assertEqual(sm.get_all_users(), [])
612
-
613
- # register a user
614
- new_user = sm.add_user( # noqa
615
- username="alice",
616
- first_name="Alice",
617
- last_name="Doe",
618
- email="alice@example.com",
619
- role=[],
620
- )
621
-
622
- # validate - user was registered
623
- self.assertEqual(len(sm.get_all_users()), 1)
624
-
625
- # attempt login
626
- user = sm.auth_user_ldap("alice", "alice_password")
627
-
628
- # validate - user was NOT allowed to log in
629
- # (because indirect bind requires search)
630
- self.assertIsNone(user)
631
-
632
- # validate - expected LDAP methods were called
633
- self.assertEqual(
634
- self.ldapobj.methods_called(with_args=True),
635
- [self.call_initialize, self.call_set_option, self.call_bind_admin],
636
- )
637
-
638
- def test__direct_bind__unregistered__single_role(self):
639
- """
640
- LDAP: test login flow for - direct bind - unregistered user - single role mapping
641
- """
642
- self.app.config["AUTH_ROLES_MAPPING"] = {
643
- "cn=staff,ou=groups,o=test": ["User"],
644
- "cn=admins,ou=groups,o=test": ["Admin"],
645
- }
646
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
647
- self.app.config["AUTH_LDAP_USERNAME_FORMAT"] = "uid=%s,ou=users,o=test"
648
- self.app.config["AUTH_USER_REGISTRATION"] = True
649
- self.app.config["AUTH_USER_REGISTRATION_ROLE"] = "Public"
650
- self.appbuilder = AppBuilder(self.app, self.db.session)
651
- sm = self.appbuilder.sm
652
-
653
- # add User role
654
- sm.add_role("User")
655
-
656
- # validate - no users are registered
657
- self.assertEqual(sm.get_all_users(), [])
658
-
659
- # attempt login
660
- user = sm.auth_user_ldap("alice", "alice_password")
661
-
662
- # validate - user was allowed to log in
663
- self.assertIsInstance(user, sm.user_model)
664
-
665
- # validate - user was registered
666
- self.assertEqual(len(sm.get_all_users()), 1)
667
-
668
- # validate - user was given the correct roles
669
- self.assertListEqual(user.roles, [sm.find_role("Public"), sm.find_role("User")])
670
-
671
- # validate - user was given the correct attributes (read from LDAP)
672
- self.assertEqual(user.first_name, "Alice")
673
- self.assertEqual(user.last_name, "Doe")
674
- self.assertEqual(user.email, "alice@example.com")
675
-
676
- # validate - expected LDAP methods were called
677
- self.assertEqual(
678
- self.ldapobj.methods_called(with_args=True),
679
- [
680
- self.call_initialize,
681
- self.call_set_option,
682
- self.call_bind_alice,
683
- self.call_search_alice_memberof,
684
- ],
685
- )
686
-
687
- def test__direct_bind__unregistered__multi_role(self):
688
- """
689
- LDAP: test login flow for - direct bind - unregistered user - multi role mapping
690
- """
691
- self.app.config["AUTH_ROLES_MAPPING"] = {
692
- "cn=staff,ou=groups,o=test": ["Admin", "User"]
693
- }
694
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
695
- self.app.config["AUTH_LDAP_USERNAME_FORMAT"] = "uid=%s,ou=users,o=test"
696
- self.app.config["AUTH_USER_REGISTRATION"] = True
697
- self.app.config["AUTH_USER_REGISTRATION_ROLE"] = "Public"
698
- self.appbuilder = AppBuilder(self.app, self.db.session)
699
- sm = self.appbuilder.sm
700
-
701
- # add User role
702
- sm.add_role("User")
703
-
704
- # validate - no users are registered
705
- self.assertEqual(sm.get_all_users(), [])
706
-
707
- # attempt login
708
- user = sm.auth_user_ldap("alice", "alice_password")
709
-
710
- # validate - user was allowed to log in
711
- self.assertIsInstance(user, sm.user_model)
712
-
713
- # validate - user was registered
714
- self.assertEqual(len(sm.get_all_users()), 1)
715
-
716
- # validate - user was given the correct roles
717
- self.assertListEqual(
718
- user.roles,
719
- [sm.find_role("Admin"), sm.find_role("Public"), sm.find_role("User")],
720
- )
721
-
722
- # validate - user was given the correct attributes (read from LDAP)
723
- self.assertEqual(user.first_name, "Alice")
724
- self.assertEqual(user.last_name, "Doe")
725
- self.assertEqual(user.email, "alice@example.com")
726
-
727
- # validate - expected LDAP methods were called
728
- self.assertEqual(
729
- self.ldapobj.methods_called(with_args=True),
730
- [
731
- self.call_initialize,
732
- self.call_set_option,
733
- self.call_bind_alice,
734
- self.call_search_alice_memberof,
735
- ],
736
- )
737
-
738
- def test__direct_bind__registered__multi_role__no_role_sync(self):
739
- """
740
- LDAP: test login flow for - direct bind - registered user - multi role mapping - no login role-sync
741
- """ # noqa
742
- self.app.config["AUTH_ROLES_MAPPING"] = {
743
- "cn=staff,ou=groups,o=test": ["Admin", "User"]
744
- }
745
- self.app.config["AUTH_ROLES_SYNC_AT_LOGIN"] = False
746
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
747
- self.app.config["AUTH_LDAP_USERNAME_FORMAT"] = "uid=%s,ou=users,o=test"
748
- self.appbuilder = AppBuilder(self.app, self.db.session)
749
- sm = self.appbuilder.sm
750
-
751
- # add User role
752
- sm.add_role("User")
753
-
754
- # validate - no users are registered
755
- self.assertEqual(sm.get_all_users(), [])
756
-
757
- # register a user
758
- new_user = sm.add_user( # noqa
759
- username="alice",
760
- first_name="Alice",
761
- last_name="Doe",
762
- email="alice@example.com",
763
- role=[],
764
- )
765
-
766
- # validate - user was registered
767
- self.assertEqual(len(sm.get_all_users()), 1)
768
-
769
- # attempt login
770
- user = sm.auth_user_ldap("alice", "alice_password")
771
-
772
- # validate - user was allowed to log in
773
- self.assertIsInstance(user, sm.user_model)
774
-
775
- # validate - user was given no roles
776
- self.assertListEqual(user.roles, [])
777
-
778
- # validate - expected LDAP methods were called
779
- self.assertEqual(
780
- self.ldapobj.methods_called(with_args=True),
781
- [
782
- self.call_initialize,
783
- self.call_set_option,
784
- self.call_bind_alice,
785
- self.call_search_alice_memberof,
786
- ],
787
- )
788
-
789
- def test__direct_bind__registered__multi_role__with_role_sync(self):
790
- """
791
- LDAP: test login flow for - direct bind - registered user - multi role mapping - with login role-sync
792
- """ # noqa
793
- self.app.config["AUTH_ROLES_MAPPING"] = {
794
- "cn=staff,ou=groups,o=test": ["Admin", "User"]
795
- }
796
- self.app.config["AUTH_ROLES_SYNC_AT_LOGIN"] = True
797
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
798
- self.app.config["AUTH_LDAP_USERNAME_FORMAT"] = "uid=%s,ou=users,o=test"
799
- self.appbuilder = AppBuilder(self.app, self.db.session)
800
- sm = self.appbuilder.sm
801
-
802
- # add User role
803
- sm.add_role("User")
804
-
805
- # validate - no users are registered
806
- self.assertEqual(sm.get_all_users(), [])
807
-
808
- # register a user
809
- new_user = sm.add_user( # noqa
810
- username="alice",
811
- first_name="Alice",
812
- last_name="Doe",
813
- email="alice@example.com",
814
- role=[],
815
- )
816
-
817
- # validate - user was registered
818
- self.assertEqual(len(sm.get_all_users()), 1)
819
-
820
- # attempt login
821
- user = sm.auth_user_ldap("alice", "alice_password")
822
-
823
- # validate - user was allowed to log in
824
- self.assertIsInstance(user, sm.user_model)
825
-
826
- # validate - user was given the correct roles
827
- self.assertListEqual(user.roles, [sm.find_role("Admin"), sm.find_role("User")])
828
-
829
- # validate - expected LDAP methods were called
830
- self.assertEqual(
831
- self.ldapobj.methods_called(with_args=True),
832
- [
833
- self.call_initialize,
834
- self.call_set_option,
835
- self.call_bind_alice,
836
- self.call_search_alice_memberof,
837
- ],
838
- )
839
-
840
- def test__indirect_bind__unregistered__single_role(self):
841
- """
842
- LDAP: test login flow for - indirect bind - unregistered user - single role mapping
843
- """ # noqa
844
- self.app.config["AUTH_ROLES_MAPPING"] = {"cn=staff,ou=groups,o=test": ["User"]}
845
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
846
- self.app.config["AUTH_LDAP_BIND_USER"] = "uid=admin,ou=users,o=test"
847
- self.app.config["AUTH_LDAP_BIND_PASSWORD"] = "admin_password"
848
- self.app.config["AUTH_USER_REGISTRATION"] = True
849
- self.app.config["AUTH_USER_REGISTRATION_ROLE"] = "Public"
850
- self.appbuilder = AppBuilder(self.app, self.db.session)
851
- sm = self.appbuilder.sm
852
-
853
- # add User role
854
- sm.add_role("User")
855
-
856
- # validate - no users are registered
857
- self.assertEqual(sm.get_all_users(), [])
858
-
859
- # attempt login
860
- user = sm.auth_user_ldap("alice", "alice_password")
861
-
862
- # validate - user was allowed to log in
863
- self.assertIsInstance(user, sm.user_model)
864
-
865
- # validate - user was registered
866
- self.assertEqual(len(sm.get_all_users()), 1)
867
-
868
- # validate - user was given the correct roles
869
- self.assertListEqual(user.roles, [sm.find_role("Public"), sm.find_role("User")])
870
-
871
- # validate - user was given the correct attributes (read from LDAP)
872
- self.assertEqual(user.first_name, "Alice")
873
- self.assertEqual(user.last_name, "Doe")
874
- self.assertEqual(user.email, "alice@example.com")
875
-
876
- # validate - expected LDAP methods were called
877
- self.assertEqual(
878
- self.ldapobj.methods_called(with_args=True),
879
- [
880
- self.call_initialize,
881
- self.call_set_option,
882
- self.call_bind_admin,
883
- self.call_search_alice_memberof,
884
- self.call_bind_alice,
885
- ],
886
- )
887
-
888
- def test__indirect_bind__unregistered__multi_role(self):
889
- """
890
- LDAP: test login flow for - indirect bind - unregistered user - multi role mapping
891
- """
892
- self.app.config["AUTH_ROLES_MAPPING"] = {
893
- "cn=staff,ou=groups,o=test": ["Admin", "User"]
894
- }
895
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
896
- self.app.config["AUTH_LDAP_BIND_USER"] = "uid=admin,ou=users,o=test"
897
- self.app.config["AUTH_LDAP_BIND_PASSWORD"] = "admin_password"
898
- self.app.config["AUTH_USER_REGISTRATION"] = True
899
- self.app.config["AUTH_USER_REGISTRATION_ROLE"] = "Public"
900
- self.appbuilder = AppBuilder(self.app, self.db.session)
901
- sm = self.appbuilder.sm
902
-
903
- # add User role
904
- sm.add_role("User")
905
-
906
- # validate - no users are registered
907
- self.assertEqual(sm.get_all_users(), [])
908
-
909
- # attempt login
910
- user = sm.auth_user_ldap("alice", "alice_password")
911
-
912
- # validate - user was allowed to log in
913
- self.assertIsInstance(user, sm.user_model)
914
-
915
- # validate - user was registered
916
- self.assertEqual(len(sm.get_all_users()), 1)
917
-
918
- # validate - user was given the correct roles
919
- self.assertListEqual(
920
- user.roles,
921
- [sm.find_role("Admin"), sm.find_role("Public"), sm.find_role("User")],
922
- )
923
-
924
- # validate - user was given the correct attributes (read from LDAP)
925
- self.assertEqual(user.first_name, "Alice")
926
- self.assertEqual(user.last_name, "Doe")
927
- self.assertEqual(user.email, "alice@example.com")
928
-
929
- # validate - expected LDAP methods were called
930
- self.assertEqual(
931
- self.ldapobj.methods_called(with_args=True),
932
- [
933
- self.call_initialize,
934
- self.call_set_option,
935
- self.call_bind_admin,
936
- self.call_search_alice_memberof,
937
- self.call_bind_alice,
938
- ],
939
- )
940
-
941
- def test__indirect_bind__registered__multi_role__no_role_sync(self):
942
- """
943
- LDAP: test login flow for - indirect bind - registered user - multi role mapping - no login role-sync
944
- """ # noqa
945
- self.app.config["AUTH_ROLES_MAPPING"] = {
946
- "cn=staff,ou=groups,o=test": ["Admin", "User"]
947
- }
948
- self.app.config["AUTH_ROLES_SYNC_AT_LOGIN"] = False
949
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
950
- self.app.config["AUTH_LDAP_BIND_USER"] = "uid=admin,ou=users,o=test"
951
- self.app.config["AUTH_LDAP_BIND_PASSWORD"] = "admin_password"
952
- self.appbuilder = AppBuilder(self.app, self.db.session)
953
- sm = self.appbuilder.sm
954
-
955
- # add User role
956
- sm.add_role("User")
957
-
958
- # validate - no users are registered
959
- self.assertEqual(sm.get_all_users(), [])
960
-
961
- # register a user
962
- new_user = sm.add_user( # noqa
963
- username="alice",
964
- first_name="Alice",
965
- last_name="Doe",
966
- email="alice@example.com",
967
- role=[],
968
- )
969
-
970
- # validate - user was registered
971
- self.assertEqual(len(sm.get_all_users()), 1)
972
-
973
- # attempt login
974
- user = sm.auth_user_ldap("alice", "alice_password")
975
-
976
- # validate - user was allowed to log in
977
- self.assertIsInstance(user, sm.user_model)
978
-
979
- # validate - user was given no roles
980
- self.assertListEqual(user.roles, [])
981
-
982
- # validate - expected LDAP methods were called
983
- self.assertEqual(
984
- self.ldapobj.methods_called(with_args=True),
985
- [
986
- self.call_initialize,
987
- self.call_set_option,
988
- self.call_bind_admin,
989
- self.call_search_alice_memberof,
990
- self.call_bind_alice,
991
- ],
992
- )
993
-
994
- def test__indirect_bind__registered__multi_role__with_role_sync(self):
995
- """
996
- LDAP: test login flow for - indirect bind - registered user - multi role mapping - with login role-sync
997
- """ # noqa
998
- self.app.config["AUTH_ROLES_MAPPING"] = {
999
- "cn=staff,ou=groups,o=test": ["Admin", "User"]
1000
- }
1001
- self.app.config["AUTH_ROLES_SYNC_AT_LOGIN"] = True
1002
- self.app.config["AUTH_LDAP_SEARCH"] = "ou=users,o=test"
1003
- self.app.config["AUTH_LDAP_BIND_USER"] = "uid=admin,ou=users,o=test"
1004
- self.app.config["AUTH_LDAP_BIND_PASSWORD"] = "admin_password"
1005
- self.appbuilder = AppBuilder(self.app, self.db.session)
1006
- sm = self.appbuilder.sm
1007
-
1008
- # add User role
1009
- sm.add_role("User")
1010
-
1011
- # validate - no users are registered
1012
- self.assertEqual(sm.get_all_users(), [])
1013
-
1014
- # register a user
1015
- new_user = sm.add_user( # noqa
1016
- username="alice",
1017
- first_name="Alice",
1018
- last_name="Doe",
1019
- email="alice@example.com",
1020
- role=[],
1021
- )
1022
-
1023
- # validate - user was registered
1024
- self.assertEqual(len(sm.get_all_users()), 1)
1025
-
1026
- # attempt login
1027
- user = sm.auth_user_ldap("alice", "alice_password")
1028
-
1029
- # validate - user was allowed to log in
1030
- self.assertIsInstance(user, sm.user_model)
1031
-
1032
- # validate - user was given the correct roles
1033
- self.assertListEqual(user.roles, [sm.find_role("Admin"), sm.find_role("User")])
1034
-
1035
- # validate - expected LDAP methods were called
1036
- self.assertEqual(
1037
- self.ldapobj.methods_called(with_args=True),
1038
- [
1039
- self.call_initialize,
1040
- self.call_set_option,
1041
- self.call_bind_admin,
1042
- self.call_search_alice_memberof,
1043
- self.call_bind_alice,
1044
- ],
1045
- )