001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.authroles.authentication.panel;
018
019import org.apache.wicket.RestartResponseException;
020import org.apache.wicket.authentication.IAuthenticationStrategy;
021import org.apache.wicket.authentication.strategy.DefaultAuthenticationStrategy;
022import org.apache.wicket.authroles.authentication.AuthenticatedWebSession;
023import org.apache.wicket.markup.html.WebMarkupContainer;
024import org.apache.wicket.markup.html.form.CheckBox;
025import org.apache.wicket.markup.html.form.PasswordTextField;
026import org.apache.wicket.markup.html.form.StatelessForm;
027import org.apache.wicket.markup.html.form.TextField;
028import org.apache.wicket.markup.html.panel.FeedbackPanel;
029import org.apache.wicket.markup.html.panel.Panel;
030import org.apache.wicket.model.CompoundPropertyModel;
031
032/**
033 * Reusable user sign in panel with username and password as well as support for persistence of the
034 * both. When the SignInPanel's form is submitted, the method signIn(String, String) is called,
035 * passing the username and password submitted. The signIn() method should authenticate the user's
036 * session.
037 * 
038 * @see IAuthenticationStrategy
039 * @see org.apache.wicket.settings.SecuritySettings#getAuthenticationStrategy()
040 * @see DefaultAuthenticationStrategy
041 *
042 * @author Jonathan Locke
043 * @author Juergen Donnerstag
044 * @author Eelco Hillenius
045 */
046public class SignInPanel extends Panel
047{
048        private static final long serialVersionUID = 1L;
049
050        private static final String SIGN_IN_FORM = "signInForm";
051
052        /** True if the panel should display a remember-me checkbox */
053        private boolean includeRememberMe = true;
054
055        /** True if the user should be remembered via form persistence (cookies) */
056        private boolean rememberMe = true;
057
058        /** password. */
059        private String password;
060
061        /** user name. */
062        private String username;
063
064        /**
065         * @see org.apache.wicket.Component#Component(String)
066         */
067        public SignInPanel(final String id)
068        {
069                this(id, true);
070        }
071
072        /**
073         * @param id
074         *            See Component constructor
075         * @param includeRememberMe
076         *            True if form should include a remember-me checkbox
077         * @see org.apache.wicket.Component#Component(String)
078         */
079        public SignInPanel(final String id, final boolean includeRememberMe)
080        {
081                super(id);
082
083                this.includeRememberMe = includeRememberMe;
084
085                // Create feedback panel and add to page
086                add(new FeedbackPanel("feedback"));
087
088                // Add sign-in form to page, passing feedback panel as
089                // validation error handler
090                add(new SignInForm(SIGN_IN_FORM));
091        }
092
093        /**
094         * 
095         * @return signin form
096         */
097        protected SignInForm getForm()
098        {
099                return (SignInForm)get(SIGN_IN_FORM);
100        }
101
102        /**
103         * Try to sign-in with remembered credentials.
104         * 
105         * @see #setRememberMe(boolean)
106         */
107        @Override
108        protected void onConfigure()
109        {
110                // logged in already?
111                if (isSignedIn() == false)
112                {
113                        IAuthenticationStrategy authenticationStrategy = getApplication().getSecuritySettings()
114                                .getAuthenticationStrategy();
115                        // get username and password from persistence store
116                        String[] data = authenticationStrategy.load();
117
118                        if ((data != null) && (data.length > 1))
119                        {
120                                // try to sign in the user
121                                if (signIn(data[0], data[1]))
122                                {
123                                        username = data[0];
124                                        password = data[1];
125
126                                        onSignInRemembered();
127                                }
128                                else
129                                {
130                                        // the loaded credentials are wrong. erase them.
131                                        authenticationStrategy.remove();
132                                }
133                        }
134                }
135
136                super.onConfigure();
137        }
138
139        /**
140         * Convenience method to access the password.
141         * 
142         * @return The password
143         */
144        public String getPassword()
145        {
146                return password;
147        }
148
149        /**
150         * Set the password
151         * 
152         * @param password
153         */
154        public void setPassword(final String password)
155        {
156                this.password = password;
157        }
158
159        /**
160         * Convenience method to access the username.
161         * 
162         * @return The user name
163         */
164        public String getUsername()
165        {
166                return username;
167        }
168
169        /**
170         * Set the username
171         * 
172         * @param username
173         */
174        public void setUsername(final String username)
175        {
176                this.username = username;
177        }
178
179        /**
180         * Get model object of the rememberMe checkbox
181         * 
182         * @return True if user should be remembered in the future
183         */
184        public boolean getRememberMe()
185        {
186                return rememberMe;
187        }
188
189        /**
190         * @param rememberMe
191         *            If true, rememberMe will be enabled (username and password will be persisted
192         *            somewhere)
193         */
194        public void setRememberMe(final boolean rememberMe)
195        {
196                this.rememberMe = rememberMe;
197        }
198
199        /**
200         * Sign in user if possible.
201         * 
202         * @param username
203         *            The username
204         * @param password
205         *            The password
206         * @return True if signin was successful
207         */
208        private boolean signIn(String username, String password)
209        {
210                return AuthenticatedWebSession.get().signIn(username, password);
211        }
212
213        /**
214         * @return true, if signed in
215         */
216        private boolean isSignedIn()
217        {
218                return AuthenticatedWebSession.get().isSignedIn();
219        }
220
221        /**
222         * Called when sign in failed
223         */
224        protected void onSignInFailed()
225        {
226                // Try the component based localizer first. If not found try the
227                // application localizer. Else use the default
228                error(getLocalizer().getString("signInFailed", this, "Sign in failed"));
229        }
230
231        /**
232         * Called when sign in was successful
233         */
234        protected void onSignInSucceeded()
235        {
236                // If login has been called because the user was not yet logged in, than continue to the
237                // original destination, otherwise to the Home page
238                continueToOriginalDestination();
239                setResponsePage(getApplication().getHomePage());
240        }
241
242        /**
243         * Called when sign-in was remembered.
244         * <p>
245         * By default tries to continue to the original destination or switches to the application's
246         * home page.
247         * <p>
248         * Note: This method will be called during rendering of this panel, thus a
249         * {@link RestartResponseException} has to be used to switch to a different page.
250         * 
251         * @see #onConfigure()
252         */
253        protected void onSignInRemembered()
254        {
255                // logon successful. Continue to the original destination
256                continueToOriginalDestination();
257
258                // Ups, no original destination. Go to the home page
259                throw new RestartResponseException(getApplication().getHomePage());
260        }
261
262        /**
263         * Sign in form.
264         */
265        public final class SignInForm extends StatelessForm<SignInPanel>
266        {
267                private static final long serialVersionUID = 1L;
268
269                /**
270                 * Constructor.
271                 * 
272                 * @param id
273                 *            id of the form component
274                 */
275                public SignInForm(final String id)
276                {
277                        super(id);
278
279                        setModel(new CompoundPropertyModel<>(SignInPanel.this));
280
281                        // Attach textfields for username and password
282                        add(new TextField<>("username").setRequired(true));
283                        add(new PasswordTextField("password"));
284
285                        // container for remember me checkbox
286                        WebMarkupContainer rememberMeContainer = new WebMarkupContainer("rememberMeContainer");
287                        add(rememberMeContainer);
288
289                        // Add rememberMe checkbox
290                        rememberMeContainer.add(new CheckBox("rememberMe"));
291
292                        // Show remember me checkbox?
293                        rememberMeContainer.setVisible(includeRememberMe);
294                }
295
296                @Override
297                public void onSubmit()
298                {
299                        IAuthenticationStrategy strategy = getApplication().getSecuritySettings()
300                                .getAuthenticationStrategy();
301
302                        if (signIn(getUsername(), getPassword()))
303                        {
304                                if (rememberMe == true)
305                                {
306                                        strategy.save(username, password);
307                                }
308                                else
309                                {
310                                        strategy.remove();
311                                }
312
313                                onSignInSucceeded();
314                        }
315                        else
316                        {
317                                onSignInFailed();
318                                strategy.remove();
319                        }
320                }
321        }
322}