1 // This may look like C code, but it's really -*- C++ -*-
2 /*
3  * Copyright (C) 2011 Emweb bv, Herent, Belgium.
4  *
5  * See the LICENSE file for terms of use.
6  */
7 #ifndef WT_AUTH_AUTH_WIDGET_H_
8 #define WT_AUTH_AUTH_WIDGET_H_
9 
10 #include <Wt/WTemplateFormView.h>
11 #include <Wt/Auth/AuthModel.h>
12 #include <Wt/Auth/OAuthService.h>
13 #include <Wt/Auth/RegistrationModel.h>
14 #include <Wt/WDialog.h>
15 #include <Wt/WMessageBox.h>
16 
17 namespace Wt {
18   namespace Auth {
19 
20 class AbstractUserDatabase;
21 class AuthModel;
22 class Login;
23 class User;
24 
25 /*! \class AuthWidget Wt/Auth/AuthWidget.h
26  *  \brief An authentication widget.
27  *
28  * The authentication widget is a widget that provides a login or
29  * logout function (depending on whether the user is currently logged
30  * in). You can use it for either or both purposes.
31  *
32  * Login or logout events are signalled to a Login object on which
33  * this widget acts.
34  *
35  * The widget also processes environmental information related to
36  * authentication:
37  *
38  * - email tokens, which are indicated in an internal path. The widget
39  *   uses dialogs (by default) to interact with the user to act on the token.
40  * - authentication tokens, which are stored in browser cookies, to implement
41  *   remember-me functionality.
42  *
43  * The processEnvironment() method initiates this process, and should
44  * typically be called only at application startup time.
45  *
46  * The authentication widget is implemented as a View for an
47  * AuthModel, which can be set using setModel(). The login logic (at
48  * this moment only for password-based authentication) is handled by
49  * this model.
50  *
51  * It is very likely that the off-the shelf authentication widget does
52  * not satisfy entirely to your taste or functional requirements. The
53  * widget uses three methods to allow customization:
54  *
55  * - as a WTemplateFormView, you may change the layout and styling of
56  *   to your liking.
57  * - the authentication logic is delegated to an AuthModel and can
58  *   can be specialized or can be used with a custom view altogether.
59  * - the views are created using virtual methods, which may be specialized
60  *   to create a customized view or to apply changes to the default view.
61  *
62  * \ingroup auth
63  */
64 class WT_API AuthWidget : public WTemplateFormView
65 {
66 public:
67   /*! \brief Constructor
68    *
69    * Creates a new authentication widget. This creates an AuthModel
70    * using the given authentication service \p baseAuth and user
71    * database \p users.
72    *
73    * The result of authentication changes is propagated to the rest of
74    * the application using a \p login object.
75    *
76    * Authentication services need to be configured in the model().
77    */
78   AuthWidget(const AuthService& baseAuth, AbstractUserDatabase& users,
79 	     Login& login);
80 
81   /*! \brief Constructor.
82    *
83    * Creates a new authentication widget.
84    *
85    * The result of authentication changes is propagated to the rest of
86    * the application using a \p login object.
87    *
88    * You need to call setModel() to configure a model for this view.
89    */
90   AuthWidget(Login& login);
91 
92   ~AuthWidget();
93 
94   /*! \brief Sets a model.
95    *
96    * This sets a model to be used for authentication.
97    */
98   void setModel(std::unique_ptr<AuthModel> model);
99 
100   /*! \brief Returns the model.
101    *
102    * The model is used only for the login function.
103    *
104    * \sa setModel()
105    */
model()106   AuthModel *model() const { return model_.get(); }
107 
108   /*! \brief Returns the login object.
109    *
110    * This login object is used to keep track of the user currently
111    * authenticated.
112    */
login()113   Login& login() { return login_; }
114 
115   /*! \brief Sets an internal path for authentication services.
116    *
117    * Only the registration function is made available through an
118    * internal path (so that one can redirect a user to the
119    * registration page). Other internal paths involved in
120    * authentication are configured in the service classes:
121    * - AuthService::setEmailRedirectInternalPath(): email tokens
122    * - OAuthService::redirectInternalPath(): an internal path used during
123    *   the oauth process.
124    */
125   void setInternalBasePath(const std::string& path);
126 
127   /*! \brief Returns the internal path.
128    *
129    * \sa setInternalBasePath()
130    */
internalBasePath()131   std::string internalBasePath() const { return basePath_; }
132 
133   /*! \brief Configures registration capabilities.
134    *
135    * Although the AuthWidget itself does not implement a registration
136    * view, it may offer a button/link to do so, and calls
137    * registerNewUser() when a user wishes to register.
138    *
139    * Even if registration is not enabled, the result of an
140    * OAuthService login process may be that a new user is
141    * identified. Then the createRegistrationView() is also used to
142    * present this new user with a registration view, passing the
143    * information obtained through OAuth.
144    */
145   void setRegistrationEnabled(bool enabled);
146 
147   /*! \brief Starts a new registration process.
148    *
149    * This calls \p registerNewUser(0).
150    */
151   void registerNewUser();
152 
153   /*! \brief Starts a new registration process.
154    *
155    * This starts a new registration process, and may be called in
156    * response to a user action, an internal path change, or an
157    * OAuthService login procedure which identified a new user. In the
158    * latter case, the OAuth-provided information is passed as
159    * parameter \p oauth.
160    *
161    * The default implementation creates a view using
162    * createRegistrationView(), and shows it in a dialog using
163    * showDialog().
164    */
165   virtual void registerNewUser(const Identity& oauth);
166 
167   /*! \brief Processes the (initial) environment.
168    *
169    * This method process environmental information that may be
170    * relevant to authentication:
171    *
172    * - email tokens, which are indicated through an internal path. The
173    *   widget uses dialogs (by default) to interact with the user to
174    *   act on the token.
175    *
176    * - authentication tokens, which are stored in browser cookies, to
177    *   implement remember-me functionality. When logging in using an
178    *   authentication token, the login is considered "weak" (since a
179    *   user may have inadvertently forgotten to logout from a public
180    *   computer). You should let the user authenticate using another,
181    *   primary method before doing sensitive operations. The
182    *   createPasswordPromptDialog() method may be useful for this.
183    *
184    * \sa letUpdatePassword()
185    */
186   virtual void processEnvironment();
187 
188   /*! \brief Lets the user update his password.
189    *
190    * This creates a view to let the user enter his new password.
191    *
192    * The default implementation creates a new view using
193    * createUpdatePasswordView() and shows it in a dialog using
194    * showDialog().
195    */
196   virtual void letUpdatePassword(const User& user, bool promptPassword);
197 
198   /*! \brief Lets the user "recover" a lost password.
199    *
200    * This creates a view to let the user enter his email address, used
201    * to send an email containing instructions to enter a new password.
202    *
203    * The default implementation creates a new view using
204    * createLostPasswordView() and shows it in a dialog using
205    * showDialog().
206    */
207   virtual void handleLostPassword();
208 
209   /*! \brief Creates a lost password view.
210    *
211    * When email verification has been enabled, the user may indicate
212    * that he has lost his password -- then proof of controlling the same
213    * email address that had associated with his account is sufficient to
214    * allow him to enter a new password.
215    *
216    * This creates the widget used to let the user enter his email
217    * address. The default implementation creates a new
218    * LostPasswordWidget.
219    *
220    * \sa handleLostPassword()
221    */
222   virtual std::unique_ptr<WWidget> createLostPasswordView();
223 
224   /*! \brief Creates a registration view.
225    *
226    * This creates a registration view, optionally using information
227    * already obtained from a third party identification service (such as
228    * an OAuth provider).
229    *
230    * The default implementation creates a new RegistrationWidget
231    * with a model created using createRegistrationModel().
232    *
233    * \sa registerNewUser()
234    */
235   virtual std::unique_ptr<WWidget> createRegistrationView(const Identity& id);
236 
237   /*! \brief Creates a view to update a user's password.
238    *
239    * If \p promptPassword is \c true, the user has to enter his current
240    * password in addition to a new password.
241    *
242    * This creates the widget used to let the user chose a new
243    * password. The default implementation instantiates an
244    * UpdatePasswordWidget.
245    *
246    * \sa letUpdatePassword()
247    */
248   virtual std::unique_ptr<WWidget>
249     createUpdatePasswordView(const User& user, bool promptPassword);
250 
251   /*! \brief Creates a password prompt dialog.
252    *
253    * This creates a dialog
254    * password. The user is taken from the \p login object, which also
255    * signals an eventual success using its Login::changed() signal.
256    *
257    * The default implementation instantiates a PasswordPromptDialog.
258    */
259   virtual std::unique_ptr<WDialog> createPasswordPromptDialog(Login& login);
260 
261   void attemptPasswordLogin();
262 
263   /*! \brief Displays the error message.
264    *
265    * This method display an dialog showing the error
266    */
267   virtual void displayError(const WString& error);
268 
269   /*! \brief Displays the info message.
270    *
271    * This method display an dialog showing the info
272    */
273   virtual void displayInfo(const WString& message);
274 
275 protected:
276   /*! \brief Creates the user-interface.
277    *
278    * This method is called just before an initial rendering, and creates
279    * the initial view.
280    *
281    * The default implementation calls createLoginView() or
282    * createLoggedInView() depending on whether a user is currently
283    * logged in.
284    */
285   virtual void create();
286 
287   /*! \brief Creates the login view.
288    *
289    * This creates a view that allows the user to login, and is shown when
290    * no user is current logged in.
291    *
292    * The default implementation renders the
293    * <tt>"Wt.Auth.template.login"</tt> template, and binds fields
294    * using createPasswordLoginView() and createOAuthLoginView().
295    */
296   virtual void createLoginView();
297 
298   /*! \brief Creates the view shown when the user is logged in.
299    *
300    * The default implementation renders the
301    * <tt>"Wt.Auth.template.logged-in"</tt> template.
302    */
303   virtual void createLoggedInView();
304 
305   /*! \brief Creates a password login view.
306    *
307    * This is used by the default implementation of createLoginView()
308    * to prompt for the information needed for logging in using a
309    * username and password. The default implementation implements a view
310    * guided by the model().
311    *
312    * \sa createLoginView()
313    */
314   virtual void createPasswordLoginView();
315 
316   /*! \brief Creates a widget to login using OAuth.
317    *
318    * The default implementation adds an icon for each OAuth service
319    * provider available. The icon that will be used for each service
320    * is a PNG file with a path based on the
321    * \link OAuthService::name name \endlink of the service. If the
322    * name is is "myService", then the icon path will be "css/oauth-myService.png".
323    * Wt does not bundle any icons by default, so you should make sure that
324    * the icon is in place.
325    *
326    * There's a lot to say about making a usable login mechanism for
327    * OAuth (and federated login services in general), see
328    * https://sites.google.com/site/oauthgoog/UXFedLogin.
329    *
330    * \sa createLoginView()
331    */
332   virtual void createOAuthLoginView();
333 
334 #ifdef WT_HAS_SAML
335   /*! \brief Creates a widget to login using SAML.
336    *
337    * The default implementation adds an icon for each SAML service
338    * provider available. The icon that will be used for each service
339    * is a PNG file with a path based on the
340    * \link Saml::Service::name name \endlink of the service. If the
341    * name is "myService", then the icon path will be "css/saml-myService.png".
342    * Wt does not bundle any icons by default, so you should make sure that
343    * the icon is in place.
344    *
345    * \sa createLoginView()
346    */
347   virtual void createSamlLoginView();
348 #endif // WT_HAS_SAML
349 
350   /*! \brief Shows a dialog.
351    *
352    * This shows a dialog. The default method creates a standard WDialog,
353    * with the given \p title and \p contents as central widget.
354    *
355    * When the central widget is deleted, it deletes the dialog.
356    */
357   virtual WDialog *showDialog(const WString& title,
358 			      std::unique_ptr<WWidget> contents);
359 
360   /*! \brief Creates a registration model.
361    *
362    * This method creates a registration model. The default
363    * implementation creates a RegistrationModel() but you may want to
364    * reimplement this function to return a specialized registration
365    * model (complementing a specialized registration view).
366    *
367    * \sa registerNewUser()
368    */
369   virtual std::unique_ptr<RegistrationModel> createRegistrationModel();
370 
371   virtual std::unique_ptr<WWidget> createFormWidget(AuthModel::Field field)
372     override;
373 
374   virtual void render(WFlags<RenderFlag> flags) override;
375 
376 private:
377   std::shared_ptr<AuthModel> model_;
378   std::unique_ptr<RegistrationModel> registrationModel_;
379   Login& login_;
380   std::string basePath_;
381   bool registrationEnabled_;
382 
383   bool created_;
384   std::unique_ptr<WDialog> dialog_;
385   std::unique_ptr<WMessageBox> messageBox_;
386 
387   void init();
388   void logout();
389   void loginThrottle(int delay);
390   void closeDialog();
391   void onLoginChange();
392   void onPathChange(const std::string& path);
393   bool handleRegistrationPath(const std::string& path);
394 
395   void oAuthStateChange(OAuthProcess *process);
396   void oAuthDone(OAuthProcess *process, const Identity& identity);
397 #ifdef WT_HAS_SAML
398   void samlDone(Saml::Process *process, const Identity& identity);
399 #endif // WT_HAS_SAML
400   void updatePasswordLoginView();
401 };
402 
403   }
404 }
405 
406 #endif // WT_AUTH_AUTH_WIDGET_H_
407