registerviews.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. __author__ = "Daniel Gaspar"
  2. import logging
  3. from flask import flash, redirect, request, session, url_for
  4. from flask_babel import lazy_gettext
  5. from .forms import LoginForm_oid, RegisterUserDBForm, RegisterUserOIDForm
  6. from .. import const as c
  7. from .._compat import as_unicode
  8. from ..validators import Unique
  9. from ..views import expose, PublicFormView
  10. log = logging.getLogger(__name__)
  11. def get_first_last_name(fullname):
  12. names = fullname.split()
  13. if len(names) > 1:
  14. return names[0], " ".join(names[1:])
  15. elif names:
  16. return names[0], ""
  17. class BaseRegisterUser(PublicFormView):
  18. """
  19. Make your own user registration view and inherit from this class if you
  20. want to implement a completely different registration process. If not,
  21. just inherit from RegisterUserDBView or RegisterUserOIDView depending on
  22. your authentication method.
  23. then override SecurityManager property that defines the class to use::
  24. from flask_appbuilder.security.registerviews import RegisterUserDBView
  25. class MyRegisterUserDBView(BaseRegisterUser):
  26. email_template = 'register_mail.html'
  27. ...
  28. class MySecurityManager(SecurityManager):
  29. registeruserdbview = MyRegisterUserDBView
  30. When instantiating AppBuilder set your own SecurityManager class::
  31. appbuilder = AppBuilder(
  32. app,
  33. db.session,
  34. security_manager_class=MySecurityManager
  35. )
  36. """
  37. route_base = "/register"
  38. email_template = "appbuilder/general/security/register_mail.html"
  39. """ The template used to generate the email sent to the user """
  40. email_subject = lazy_gettext("Account activation")
  41. """ The email subject sent to the user """
  42. activation_template = "appbuilder/general/security/activation.html"
  43. """ The activation template, shown when the user is activated """
  44. message = lazy_gettext("Registration sent to your email")
  45. """ The message shown on a successful registration """
  46. error_message = lazy_gettext(
  47. "Not possible to register you at the moment, try again later"
  48. )
  49. """ The message shown on an unsuccessful registration """
  50. false_error_message = lazy_gettext("Registration not found")
  51. """ The message shown on an unsuccessful registration """
  52. form_title = lazy_gettext("Fill out the registration form")
  53. """ The form title """
  54. def send_email(self, register_user):
  55. """
  56. Method for sending the registration Email to the user
  57. """
  58. try:
  59. from flask_mail import Mail, Message
  60. except Exception:
  61. log.error("Install Flask-Mail to use User registration")
  62. return False
  63. mail = Mail(self.appbuilder.get_app)
  64. msg = Message()
  65. msg.subject = self.email_subject
  66. url = url_for(
  67. ".activation",
  68. _external=True,
  69. activation_hash=register_user.registration_hash,
  70. )
  71. msg.html = self.render_template(
  72. self.email_template,
  73. url=url,
  74. username=register_user.username,
  75. first_name=register_user.first_name,
  76. last_name=register_user.last_name,
  77. )
  78. msg.recipients = [register_user.email]
  79. try:
  80. mail.send(msg)
  81. except Exception as e:
  82. log.error("Send email exception: %s", e)
  83. return False
  84. return True
  85. def add_registration(self, username, first_name, last_name, email, password=""):
  86. """
  87. Add a registration request for the user.
  88. :rtype : RegisterUser
  89. """
  90. register_user = self.appbuilder.sm.add_register_user(
  91. username, first_name, last_name, email, password
  92. )
  93. if register_user:
  94. if self.send_email(register_user):
  95. flash(as_unicode(self.message), "info")
  96. return register_user
  97. else:
  98. flash(as_unicode(self.error_message), "danger")
  99. self.appbuilder.sm.del_register_user(register_user)
  100. return None
  101. @expose("/activation/<string:activation_hash>")
  102. def activation(self, activation_hash):
  103. """
  104. Endpoint to expose an activation url, this url
  105. is sent to the user by email, when accessed the user is inserted
  106. and activated
  107. """
  108. reg = self.appbuilder.sm.find_register_user(activation_hash)
  109. if not reg:
  110. log.error(c.LOGMSG_ERR_SEC_NO_REGISTER_HASH, activation_hash)
  111. flash(as_unicode(self.false_error_message), "danger")
  112. return redirect(self.appbuilder.get_url_for_index)
  113. if not self.appbuilder.sm.add_user(
  114. username=reg.username,
  115. email=reg.email,
  116. first_name=reg.first_name,
  117. last_name=reg.last_name,
  118. role=self.appbuilder.sm.find_role(
  119. self.appbuilder.sm.auth_user_registration_role
  120. ),
  121. hashed_password=reg.password,
  122. ):
  123. flash(as_unicode(self.error_message), "danger")
  124. return redirect(self.appbuilder.get_url_for_index)
  125. else:
  126. self.appbuilder.sm.del_register_user(reg)
  127. return self.render_template(
  128. self.activation_template,
  129. username=reg.username,
  130. first_name=reg.first_name,
  131. last_name=reg.last_name,
  132. appbuilder=self.appbuilder,
  133. )
  134. def add_form_unique_validations(self, form):
  135. datamodel_user = self.appbuilder.sm.get_user_datamodel
  136. datamodel_register_user = self.appbuilder.sm.get_register_user_datamodel
  137. if len(form.username.validators) == 1:
  138. form.username.validators.append(Unique(datamodel_user, "username"))
  139. form.username.validators.append(Unique(datamodel_register_user, "username"))
  140. if len(form.email.validators) == 2:
  141. form.email.validators.append(Unique(datamodel_user, "email"))
  142. form.email.validators.append(Unique(datamodel_register_user, "email"))
  143. class RegisterUserDBView(BaseRegisterUser):
  144. """
  145. View for Registering a new user, auth db mode
  146. """
  147. form = RegisterUserDBForm
  148. """ The WTForm form presented to the user to register himself """
  149. redirect_url = "/"
  150. def form_get(self, form):
  151. self.add_form_unique_validations(form)
  152. def form_post(self, form):
  153. self.add_form_unique_validations(form)
  154. self.add_registration(
  155. username=form.username.data,
  156. first_name=form.first_name.data,
  157. last_name=form.last_name.data,
  158. email=form.email.data,
  159. password=form.password.data,
  160. )
  161. class RegisterUserOIDView(BaseRegisterUser):
  162. """
  163. View for Registering a new user, auth OID mode
  164. """
  165. route_base = "/register"
  166. form = RegisterUserOIDForm
  167. default_view = "form_oid_post"
  168. @expose("/formoidone", methods=["GET", "POST"])
  169. def form_oid_post(self, flag=True):
  170. if flag:
  171. self.oid_login_handler(self.form_oid_post, self.appbuilder.sm.oid)
  172. form = LoginForm_oid()
  173. if form.validate_on_submit():
  174. session["remember_me"] = form.remember_me.data
  175. return self.appbuilder.sm.oid.try_login(
  176. form.openid.data, ask_for=["email", "fullname"]
  177. )
  178. resp = session.pop("oid_resp", None)
  179. if resp:
  180. self._init_vars()
  181. form = self.form.refresh()
  182. self.form_get(form)
  183. form.username.data = resp.email
  184. first_name, last_name = get_first_last_name(resp.fullname)
  185. form.first_name.data = first_name
  186. form.last_name.data = last_name
  187. form.email.data = resp.email
  188. widgets = self._get_edit_widget(form=form)
  189. # self.update_redirect()
  190. return self.render_template(
  191. self.form_template,
  192. title=self.form_title,
  193. widgets=widgets,
  194. form_action="form",
  195. appbuilder=self.appbuilder,
  196. )
  197. else:
  198. flash(as_unicode(self.error_message), "warning")
  199. return redirect(self.get_redirect())
  200. def oid_login_handler(self, f, oid):
  201. """
  202. Hackish method to make use of oid.login_handler decorator.
  203. """
  204. from flask_openid import OpenIDResponse, SessionWrapper
  205. from openid.consumer.consumer import CANCEL, Consumer, SUCCESS
  206. if request.args.get("openid_complete") != "yes":
  207. return f(False)
  208. consumer = Consumer(SessionWrapper(self), oid.store_factory())
  209. openid_response = consumer.complete(
  210. request.args.to_dict(), oid.get_current_url()
  211. )
  212. if openid_response.status == SUCCESS:
  213. return self.after_login(OpenIDResponse(openid_response, []))
  214. elif openid_response.status == CANCEL:
  215. oid.signal_error("The request was cancelled")
  216. return redirect(oid.get_current_url())
  217. oid.signal_error("OpenID authentication error")
  218. return redirect(oid.get_current_url())
  219. def after_login(self, resp):
  220. """
  221. Method that adds the return OpenID response object on the session
  222. this session key will be deleted
  223. """
  224. session["oid_resp"] = resp
  225. def form_get(self, form):
  226. self.add_form_unique_validations(form)
  227. def form_post(self, form):
  228. self.add_registration(
  229. username=form.username.data,
  230. first_name=form.first_name.data,
  231. last_name=form.last_name.data,
  232. email=form.email.data,
  233. )
  234. class RegisterUserOAuthView(BaseRegisterUser):
  235. """
  236. View for Registering a new user, auth OID mode
  237. """
  238. form = RegisterUserOIDForm
  239. def form_get(self, form):
  240. self.add_form_unique_validations(form)
  241. # fills the register form with the collected data from OAuth
  242. form.username.data = request.args.get("username", "")
  243. form.first_name.data = request.args.get("first_name", "")
  244. form.last_name.data = request.args.get("last_name", "")
  245. form.email.data = request.args.get("email", "")
  246. def form_post(self, form):
  247. log.debug("Adding Registration")
  248. self.add_registration(
  249. username=form.username.data,
  250. first_name=form.first_name.data,
  251. last_name=form.last_name.data,
  252. email=form.email.data,
  253. )