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
@@ -2,10 +2,10 @@ __author__ = "Daniel Gaspar"
2
2
 
3
3
  import logging
4
4
 
5
- from flask import flash, redirect, request, session, url_for
5
+ from flask import current_app, flash, redirect, request, url_for
6
6
  from flask_babel import lazy_gettext
7
7
 
8
- from .forms import LoginForm_oid, RegisterUserDBForm, RegisterUserOIDForm
8
+ from .forms import RegisterUserDBForm
9
9
  from .. import const as c
10
10
  from .._compat import as_unicode
11
11
  from ..validators import Unique
@@ -24,29 +24,29 @@ def get_first_last_name(fullname):
24
24
 
25
25
  class BaseRegisterUser(PublicFormView):
26
26
  """
27
- Make your own user registration view and inherit from this class if you
28
- want to implement a completely different registration process. If not,
29
- just inherit from RegisterUserDBView or RegisterUserOIDView depending on
30
- your authentication method.
31
- then override SecurityManager property that defines the class to use::
27
+ Make your own user registration view and inherit from this class if you
28
+ want to implement a completely different registration process. If not,
29
+ just inherit from RegisterUserDBView or RegisterUserOAuthView depending on
30
+ your authentication method.
31
+ then override SecurityManager property that defines the class to use::
32
32
 
33
- from flask_appbuilder.security.registerviews import RegisterUserDBView
33
+ from flask_appbuilder.security.registerviews import RegisterUserDBView
34
34
 
35
- class MyRegisterUserDBView(BaseRegisterUser):
36
- email_template = 'register_mail.html'
37
- ...
35
+ class MyRegisterUserDBView(BaseRegisterUser):
36
+ email_template = 'register_mail.html'
37
+ ...
38
38
 
39
39
 
40
- class MySecurityManager(SecurityManager):
41
- registeruserdbview = MyRegisterUserDBView
40
+ class MySecurityManager(SecurityManager):
41
+ registeruserdbview = MyRegisterUserDBView
42
42
 
43
- When instantiating AppBuilder set your own SecurityManager class::
43
+ When instantiating AppBuilder set your own SecurityManager class::
44
44
 
45
- appbuilder = AppBuilder(
46
- app,
47
- db.session,
48
- security_manager_class=MySecurityManager
49
- )
45
+ appbuilder = AppBuilder(
46
+ app,
47
+ db.session,
48
+ security_manager_class=MySecurityManager
49
+ )
50
50
  """
51
51
 
52
52
  route_base = "/register"
@@ -69,14 +69,14 @@ class BaseRegisterUser(PublicFormView):
69
69
 
70
70
  def send_email(self, register_user):
71
71
  """
72
- Method for sending the registration Email to the user
72
+ Method for sending the registration Email to the user
73
73
  """
74
74
  try:
75
75
  from flask_mail import Mail, Message
76
76
  except Exception:
77
77
  log.error("Install Flask-Mail to use User registration")
78
78
  return False
79
- mail = Mail(self.appbuilder.get_app)
79
+ mail = Mail(current_app)
80
80
  msg = Message()
81
81
  msg.subject = self.email_subject
82
82
  url = url_for(
@@ -95,7 +95,7 @@ class BaseRegisterUser(PublicFormView):
95
95
  try:
96
96
  mail.send(msg)
97
97
  except Exception as e:
98
- log.error("Send email exception: {0}".format(str(e)))
98
+ log.error("Send email exception: %s", e)
99
99
  return False
100
100
  return True
101
101
 
@@ -120,13 +120,13 @@ class BaseRegisterUser(PublicFormView):
120
120
  @expose("/activation/<string:activation_hash>")
121
121
  def activation(self, activation_hash):
122
122
  """
123
- Endpoint to expose an activation url, this url
124
- is sent to the user by email, when accessed the user is inserted
125
- and activated
123
+ Endpoint to expose an activation url, this url
124
+ is sent to the user by email, when accessed the user is inserted
125
+ and activated
126
126
  """
127
127
  reg = self.appbuilder.sm.find_register_user(activation_hash)
128
128
  if not reg:
129
- log.error(c.LOGMSG_ERR_SEC_NO_REGISTER_HASH.format(activation_hash))
129
+ log.error(c.LOGMSG_ERR_SEC_NO_REGISTER_HASH, activation_hash)
130
130
  flash(as_unicode(self.false_error_message), "danger")
131
131
  return redirect(self.appbuilder.get_url_for_index)
132
132
  if not self.appbuilder.sm.add_user(
@@ -164,7 +164,7 @@ class BaseRegisterUser(PublicFormView):
164
164
 
165
165
  class RegisterUserDBView(BaseRegisterUser):
166
166
  """
167
- View for Registering a new user, auth db mode
167
+ View for Registering a new user, auth db mode
168
168
  """
169
169
 
170
170
  form = RegisterUserDBForm
@@ -185,95 +185,12 @@ class RegisterUserDBView(BaseRegisterUser):
185
185
  )
186
186
 
187
187
 
188
- class RegisterUserOIDView(BaseRegisterUser):
189
- """
190
- View for Registering a new user, auth OID mode
191
- """
192
-
193
- route_base = "/register"
194
-
195
- form = RegisterUserOIDForm
196
- default_view = "form_oid_post"
197
-
198
- @expose("/formoidone", methods=["GET", "POST"])
199
- def form_oid_post(self, flag=True):
200
- if flag:
201
- self.oid_login_handler(self.form_oid_post, self.appbuilder.sm.oid)
202
- form = LoginForm_oid()
203
- if form.validate_on_submit():
204
- session["remember_me"] = form.remember_me.data
205
- return self.appbuilder.sm.oid.try_login(
206
- form.openid.data, ask_for=["email", "fullname"]
207
- )
208
- resp = session.pop("oid_resp", None)
209
- if resp:
210
- self._init_vars()
211
- form = self.form.refresh()
212
- self.form_get(form)
213
- form.username.data = resp.email
214
- first_name, last_name = get_first_last_name(resp.fullname)
215
- form.first_name.data = first_name
216
- form.last_name.data = last_name
217
- form.email.data = resp.email
218
- widgets = self._get_edit_widget(form=form)
219
- # self.update_redirect()
220
- return self.render_template(
221
- self.form_template,
222
- title=self.form_title,
223
- widgets=widgets,
224
- form_action="form",
225
- appbuilder=self.appbuilder,
226
- )
227
- else:
228
- flash(as_unicode(self.error_message), "warning")
229
- return redirect(self.get_redirect())
230
-
231
- def oid_login_handler(self, f, oid):
232
- """
233
- Hackish method to make use of oid.login_handler decorator.
234
- """
235
- from flask_openid import OpenIDResponse, SessionWrapper
236
- from openid.consumer.consumer import CANCEL, Consumer, SUCCESS
237
-
238
- if request.args.get("openid_complete") != u"yes":
239
- return f(False)
240
- consumer = Consumer(SessionWrapper(self), oid.store_factory())
241
- openid_response = consumer.complete(
242
- request.args.to_dict(), oid.get_current_url()
243
- )
244
- if openid_response.status == SUCCESS:
245
- return self.after_login(OpenIDResponse(openid_response, []))
246
- elif openid_response.status == CANCEL:
247
- oid.signal_error(u"The request was cancelled")
248
- return redirect(oid.get_current_url())
249
- oid.signal_error(u"OpenID authentication error")
250
- return redirect(oid.get_current_url())
251
-
252
- def after_login(self, resp):
253
- """
254
- Method that adds the return OpenID response object on the session
255
- this session key will be deleted
256
- """
257
- session["oid_resp"] = resp
258
-
259
- def form_get(self, form):
260
- self.add_form_unique_validations(form)
261
-
262
- def form_post(self, form):
263
- self.add_registration(
264
- username=form.username.data,
265
- first_name=form.first_name.data,
266
- last_name=form.last_name.data,
267
- email=form.email.data,
268
- )
269
-
270
-
271
188
  class RegisterUserOAuthView(BaseRegisterUser):
272
189
  """
273
- View for Registering a new user, auth OID mode
190
+ View for Registering a new user, auth OAuth mode
274
191
  """
275
192
 
276
- form = RegisterUserOIDForm
193
+ form = RegisterUserDBForm
277
194
 
278
195
  def form_get(self, form):
279
196
  self.add_form_unique_validations(form)
@@ -0,0 +1,43 @@
1
+ from typing import Union
2
+
3
+ from flask import current_app
4
+ from flask_appbuilder.const import (
5
+ API_SECURITY_PROVIDER_DB,
6
+ API_SECURITY_PROVIDER_LDAP,
7
+ AUTH_DB,
8
+ AUTH_LDAP,
9
+ )
10
+ from marshmallow import fields, Schema, ValidationError
11
+ from marshmallow.validate import Length, OneOf
12
+
13
+
14
+ provider_to_auth_type = {"db": AUTH_DB, "ldap": AUTH_LDAP}
15
+
16
+
17
+ def validate_password(value: Union[bytes, bytearray, str]) -> None:
18
+ if value and sum(value.encode()) == 0:
19
+ raise ValidationError("Password null is not allowed")
20
+
21
+
22
+ def validate_provider(value: Union[bytes, bytearray, str]) -> None:
23
+ if not current_app.appbuilder.sm.api_login_allow_multiple_providers:
24
+ provider_name = current_app.appbuilder.sm.auth_type_provider_name
25
+ if provider_name and provider_name != value:
26
+ raise ValidationError("Alternative authentication provider is not allowed")
27
+
28
+
29
+ class LoginPost(Schema):
30
+ username = fields.String(required=True, allow_none=False, validate=Length(min=1))
31
+ password = fields.String(
32
+ validate=[Length(min=1), validate_password], required=True, allow_none=False
33
+ )
34
+ provider = fields.String(
35
+ validate=[
36
+ OneOf([API_SECURITY_PROVIDER_DB, API_SECURITY_PROVIDER_LDAP]),
37
+ validate_provider,
38
+ ]
39
+ )
40
+ refresh = fields.Boolean(required=False)
41
+
42
+
43
+ login_post = LoginPost()
@@ -0,0 +1,8 @@
1
+ from flask_appbuilder.security.sqla.apis.group import GroupApi # noqa: F401
2
+ from flask_appbuilder.security.sqla.apis.permission import PermissionApi # noqa: F401
3
+ from flask_appbuilder.security.sqla.apis.permission_view_menu import ( # noqa: F401
4
+ PermissionViewMenuApi,
5
+ )
6
+ from flask_appbuilder.security.sqla.apis.role import RoleApi # noqa: F401
7
+ from flask_appbuilder.security.sqla.apis.user import UserApi # noqa: F401
8
+ from flask_appbuilder.security.sqla.apis.view_menu import ViewMenuApi # noqa: F401
@@ -0,0 +1 @@
1
+ from .api import GroupApi # noqa: F401
@@ -0,0 +1,227 @@
1
+ from flask import request
2
+ from flask_appbuilder import ModelRestApi
3
+ from flask_appbuilder.api import expose, safe
4
+ from flask_appbuilder.const import API_RESULT_RES_KEY
5
+ from flask_appbuilder.models.sqla.interface import SQLAInterface
6
+ from flask_appbuilder.security.decorators import permission_name, protect
7
+ from flask_appbuilder.security.sqla.apis.group.schema import (
8
+ GroupPostSchema,
9
+ GroupPutSchema,
10
+ )
11
+ from flask_appbuilder.security.sqla.models import Group, Role, User
12
+ from marshmallow import ValidationError
13
+ from sqlalchemy.exc import IntegrityError
14
+
15
+
16
+ class GroupApi(ModelRestApi):
17
+ resource_name = "security/groups"
18
+ openapi_spec_tag = "Security Groups"
19
+ class_permission_name = "Group"
20
+ datamodel = SQLAInterface(Group)
21
+ allow_browser_login = True
22
+
23
+ list_columns = [
24
+ "id",
25
+ "name",
26
+ "label",
27
+ "description",
28
+ "roles.name",
29
+ "roles.id",
30
+ "users.id",
31
+ "users.username",
32
+ ]
33
+ show_columns = list_columns
34
+ edit_columns = ["name", "label", "description", "users", "roles"]
35
+ add_columns = edit_columns
36
+ search_columns = list_columns
37
+
38
+ add_model_schema = GroupPostSchema()
39
+ edit_model_schema = GroupPutSchema()
40
+
41
+ openapi_spec_component_schemas = (
42
+ GroupPostSchema,
43
+ GroupPutSchema,
44
+ )
45
+
46
+ @expose("/", methods=["POST"])
47
+ @protect()
48
+ @safe
49
+ @permission_name("post")
50
+ def post(self):
51
+ """Create new group
52
+ ---
53
+ post:
54
+ requestBody:
55
+ description: Model schema
56
+ required: true
57
+ content:
58
+ application/json:
59
+ schema:
60
+ $ref: '#/components/schemas/GroupPostSchema'
61
+ responses:
62
+ 201:
63
+ description: Group created
64
+ content:
65
+ application/json:
66
+ schema:
67
+ type: object
68
+ properties:
69
+ result:
70
+ $ref: '#/components/schemas/GroupPostSchema'
71
+ 400:
72
+ $ref: '#/components/responses/400'
73
+ 401:
74
+ $ref: '#/components/responses/401'
75
+ 422:
76
+ $ref: '#/components/responses/422'
77
+ 500:
78
+ $ref: '#/components/responses/500'
79
+ """
80
+ try:
81
+ item = self.add_model_schema.load(request.json)
82
+ model = Group()
83
+ roles = []
84
+ users = []
85
+
86
+ for key, value in item.items():
87
+ if key == "roles":
88
+ roles = self._fetch_entities(Role, value)
89
+ missing_role_ids = set(item["roles"]) - {r.id for r in roles}
90
+ if missing_role_ids:
91
+ return self.response_400(
92
+ message={
93
+ "roles": [
94
+ (
95
+ f"Role(s) with ID(s) {sorted(missing_role_ids)} "
96
+ "do not exist."
97
+ )
98
+ ]
99
+ }
100
+ )
101
+ elif key == "users":
102
+ users = self._fetch_entities(User, value)
103
+ missing_user_ids = set(item["users"]) - {u.id for u in users}
104
+ if missing_user_ids:
105
+ return self.response_400(
106
+ message={
107
+ "users": [
108
+ (
109
+ f"User(s) with ID(s) {sorted(missing_user_ids)} "
110
+ "do not exist."
111
+ )
112
+ ]
113
+ }
114
+ )
115
+ else:
116
+ setattr(model, key, value)
117
+
118
+ model.roles = roles
119
+ model.users = users
120
+
121
+ self.pre_add(model)
122
+ self.datamodel.add(model)
123
+
124
+ return self.response(201, id=model.id)
125
+
126
+ except ValidationError as error:
127
+ return self.response_400(message=error.messages)
128
+ except IntegrityError as e:
129
+ return self.response_422(message=str(e.orig))
130
+
131
+ @expose("/<pk>", methods=["PUT"])
132
+ @protect()
133
+ @safe
134
+ @permission_name("put")
135
+ def put(self, pk):
136
+ """Edit group
137
+ ---
138
+ put:
139
+ parameters:
140
+ - in: path
141
+ schema:
142
+ type: integer
143
+ name: pk
144
+ requestBody:
145
+ description: Model schema
146
+ required: true
147
+ content:
148
+ application/json:
149
+ schema:
150
+ $ref: '#/components/schemas/GroupPutSchema'
151
+ responses:
152
+ 200:
153
+ description: Group updated
154
+ content:
155
+ application/json:
156
+ schema:
157
+ type: object
158
+ properties:
159
+ result:
160
+ $ref: '#/components/schemas/GroupPutSchema'
161
+ 400:
162
+ $ref: '#/components/responses/400'
163
+ 401:
164
+ $ref: '#/components/responses/401'
165
+ 404:
166
+ $ref: '#/components/responses/404'
167
+ 422:
168
+ $ref: '#/components/responses/422'
169
+ 500:
170
+ $ref: '#/components/responses/500'
171
+ """
172
+ try:
173
+ item = self.edit_model_schema.load(request.json)
174
+ model = self.datamodel.get(pk, self._base_filters)
175
+ if not model:
176
+ return self.response_404()
177
+
178
+ roles = []
179
+ users = []
180
+
181
+ for key, value in item.items():
182
+ if key == "roles":
183
+ roles = self._fetch_entities(Role, value)
184
+ missing_role_ids = set(value) - {r.id for r in roles}
185
+ if missing_role_ids:
186
+ return self.response_400(
187
+ message={
188
+ "roles": [
189
+ (
190
+ f"Role(s) with ID(s) {sorted(missing_role_ids)} "
191
+ "do not exist."
192
+ )
193
+ ]
194
+ }
195
+ )
196
+ elif key == "users":
197
+ users = self._fetch_entities(User, value)
198
+ missing_user_ids = set(value) - {u.id for u in users}
199
+ if missing_user_ids:
200
+ return self.response_400(
201
+ message={
202
+ "users": [
203
+ (
204
+ f"User(s) with ID(s) {sorted(missing_user_ids)} "
205
+ "do not exist."
206
+ )
207
+ ]
208
+ }
209
+ )
210
+ else:
211
+ setattr(model, key, value)
212
+
213
+ if "roles" in item.keys():
214
+ model.roles = roles
215
+ if "users" in item.keys():
216
+ model.users = users
217
+ self.pre_update(model)
218
+ self.datamodel.edit(model)
219
+ return self.response(
220
+ 200,
221
+ **{API_RESULT_RES_KEY: self.edit_model_schema.dump(item, many=False)},
222
+ )
223
+
224
+ except ValidationError as e:
225
+ return self.response_400(message=e.messages)
226
+ except IntegrityError as e:
227
+ return self.response_422(message=str(e.orig))
@@ -0,0 +1,73 @@
1
+ from flask_appbuilder.security.sqla.models import Group
2
+ from marshmallow import fields, Schema
3
+ from marshmallow.validate import Length
4
+
5
+ name_description = "Group name"
6
+ label_description = "Group label"
7
+ description_description = "Group description"
8
+ roles_description = "Group roles"
9
+ users_description = "Group users"
10
+
11
+
12
+ class GroupPostSchema(Schema):
13
+ model_cls = Group
14
+
15
+ name = fields.String(
16
+ required=True,
17
+ validate=[Length(1, 100)],
18
+ metadata={"description": name_description},
19
+ )
20
+ label = fields.String(
21
+ required=False,
22
+ allow_none=True,
23
+ validate=[Length(0, 150)],
24
+ metadata={"description": label_description},
25
+ )
26
+ description = fields.String(
27
+ required=False,
28
+ allow_none=True,
29
+ validate=[Length(0, 512)],
30
+ metadata={"description": description_description},
31
+ )
32
+ roles = fields.List(
33
+ fields.Integer,
34
+ required=False,
35
+ metadata={"description": roles_description},
36
+ )
37
+ users = fields.List(
38
+ fields.Integer,
39
+ required=False,
40
+ metadata={"description": users_description},
41
+ )
42
+
43
+
44
+ class GroupPutSchema(Schema):
45
+ model_cls = Group
46
+
47
+ name = fields.String(
48
+ required=False,
49
+ validate=[Length(1, 100)],
50
+ metadata={"description": name_description},
51
+ )
52
+ label = fields.String(
53
+ required=False,
54
+ allow_none=True,
55
+ validate=[Length(0, 150)],
56
+ metadata={"description": label_description},
57
+ )
58
+ description = fields.String(
59
+ required=False,
60
+ allow_none=True,
61
+ validate=[Length(0, 512)],
62
+ metadata={"description": description_description},
63
+ )
64
+ roles = fields.List(
65
+ fields.Integer,
66
+ required=False,
67
+ metadata={"description": roles_description},
68
+ )
69
+ users = fields.List(
70
+ fields.Integer,
71
+ required=False,
72
+ metadata={"description": users_description},
73
+ )
@@ -0,0 +1 @@
1
+ from .api import PermissionApi # noqa: F401
@@ -0,0 +1,19 @@
1
+ from flask_appbuilder import ModelRestApi
2
+ from flask_appbuilder.models.sqla.interface import SQLAInterface
3
+ from flask_appbuilder.security.sqla.models import Permission
4
+
5
+
6
+ class PermissionApi(ModelRestApi):
7
+ resource_name = "security/permissions"
8
+ openapi_spec_tag = "Security Permissions"
9
+
10
+ class_permission_name = "Permission"
11
+ datamodel = SQLAInterface(Permission)
12
+ allow_browser_login = True
13
+ include_route_methods = {"info", "get", "get_list"}
14
+
15
+ list_columns = ["id", "name"]
16
+ show_columns = list_columns
17
+ add_columns = ["name"]
18
+ edit_columns = add_columns
19
+ search_columns = list_columns
@@ -0,0 +1 @@
1
+ from .api import PermissionViewMenuApi # noqa: F401
@@ -0,0 +1,16 @@
1
+ from flask_appbuilder import ModelRestApi
2
+ from flask_appbuilder.models.sqla.interface import SQLAInterface
3
+ from flask_appbuilder.security.sqla.models import PermissionView
4
+
5
+
6
+ class PermissionViewMenuApi(ModelRestApi):
7
+ resource_name = "security/permissions-resources"
8
+ openapi_spec_tag = "Security Permissions on Resources (View Menus)"
9
+ class_permission_name = "PermissionViewMenu"
10
+ datamodel = SQLAInterface(PermissionView)
11
+ allow_browser_login = True
12
+
13
+ list_columns = ["id", "permission.name", "view_menu.name"]
14
+ show_columns = list_columns
15
+ add_columns = ["permission_id", "view_menu_id"]
16
+ edit_columns = add_columns
@@ -0,0 +1 @@
1
+ from .api import RoleApi # noqa: F401