1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.client.ui.commons;
20
21 import com.googlecode.wicket.kendo.ui.widget.notification.Notification;
22 import java.security.AccessControlException;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.Locale;
27 import java.util.stream.Collectors;
28 import javax.ws.rs.core.HttpHeaders;
29 import org.apache.commons.lang3.StringUtils;
30 import org.apache.syncope.client.ui.commons.panels.NotificationPanel;
31 import org.apache.syncope.common.keymaster.client.api.DomainOps;
32 import org.apache.syncope.common.keymaster.client.api.model.Domain;
33 import org.apache.syncope.common.lib.SyncopeConstants;
34 import org.apache.wicket.Component;
35 import org.apache.wicket.ajax.AjaxRequestTarget;
36 import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
37 import org.apache.wicket.ajax.markup.html.form.AjaxButton;
38 import org.apache.wicket.markup.head.IHeaderResponse;
39 import org.apache.wicket.markup.head.OnLoadHeaderItem;
40 import org.apache.wicket.markup.html.WebPage;
41 import org.apache.wicket.markup.html.basic.Label;
42 import org.apache.wicket.markup.html.form.ChoiceRenderer;
43 import org.apache.wicket.markup.html.form.DropDownChoice;
44 import org.apache.wicket.markup.html.form.PasswordTextField;
45 import org.apache.wicket.markup.html.form.StatelessForm;
46 import org.apache.wicket.markup.html.form.TextField;
47 import org.apache.wicket.markup.html.list.ListItem;
48 import org.apache.wicket.markup.html.list.ListView;
49 import org.apache.wicket.markup.html.panel.Panel;
50 import org.apache.wicket.model.IModel;
51 import org.apache.wicket.model.LoadableDetachableModel;
52 import org.apache.wicket.model.Model;
53 import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
54 import org.apache.wicket.request.cycle.RequestCycle;
55 import org.apache.wicket.request.mapper.parameter.PageParameters;
56 import org.apache.wicket.spring.injection.annot.SpringBean;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 public abstract class BaseLogin extends WebPage {
61
62 private static final long serialVersionUID = 5889157642852559004L;
63
64 protected static final Logger LOG = LoggerFactory.getLogger(BaseLogin.class);
65
66 @SpringBean
67 protected DomainOps domainOps;
68
69 protected final NotificationPanel notificationPanel;
70
71 protected final StatelessForm<Void> form;
72
73 protected final TextField<String> usernameField;
74
75 protected final TextField<String> passwordField;
76
77 protected String notificationMessage;
78
79 protected String notificationLevel;
80
81 protected final LoadableDetachableModel<List<String>> domains = new LoadableDetachableModel<>() {
82
83 private static final long serialVersionUID = 4659376149825914247L;
84
85 @Override
86 protected List<String> load() {
87 List<String> current = new ArrayList<>();
88 current.addAll(domainOps.list().stream().map(Domain::getKey).sorted().collect(Collectors.toList()));
89 current.add(0, SyncopeConstants.MASTER_DOMAIN);
90 return current;
91 }
92 };
93
94 public BaseLogin(final PageParameters parameters) {
95 super(parameters);
96 setStatelessHint(true);
97
98 notificationPanel = new NotificationPanel(Constants.FEEDBACK);
99 add(notificationPanel);
100
101 if (!parameters.get("notificationMessage").isNull()) {
102 notificationMessage = parameters.get(Constants.NOTIFICATION_MSG_PARAM).toString();
103 notificationLevel = parameters.get(Constants.NOTIFICATION_LEVEL_PARAM).isEmpty()
104 ? Notification.SUCCESS
105 : parameters.get(Constants.NOTIFICATION_LEVEL_PARAM).toString();
106 }
107
108 Label exceptionMessage = new Label("exceptionMessage");
109 exceptionMessage.setOutputMarkupPlaceholderTag(true);
110 exceptionMessage.setVisible(false);
111 if (!parameters.get("errorMessage").isNull()) {
112 exceptionMessage.setVisible(true);
113 exceptionMessage.setDefaultModel(Model.of(parameters.get("errorMessage")));
114 }
115 add(exceptionMessage);
116
117 form = new StatelessForm<>("login");
118
119 usernameField = new TextField<>("username", new Model<>());
120 usernameField.setMarkupId("username");
121 form.add(usernameField);
122
123 passwordField = new PasswordTextField("password", new Model<>());
124 passwordField.setMarkupId("password");
125 form.add(passwordField);
126
127 LocaleDropDown languageSelect = new LocaleDropDown("language");
128 languageSelect.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_BLUR) {
129
130 private static final long serialVersionUID = -1107858522700306810L;
131
132 @Override
133 protected void onUpdate(final AjaxRequestTarget target) {
134 getLanguageOnChangeComponents().forEach(target::add);
135 }
136 }).add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
137
138 private static final long serialVersionUID = -1107858522700306810L;
139
140 @Override
141 protected void onUpdate(final AjaxRequestTarget target) {
142 getLanguageOnChangeComponents().forEach(target::add);
143 }
144 });
145 form.add(languageSelect.setOutputMarkupId(true));
146
147 DomainDropDown domainSelect = new DomainDropDown("domain", domains);
148 domainSelect.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_BLUR) {
149
150 private static final long serialVersionUID = -1107858522700306810L;
151
152 @Override
153 protected void onUpdate(final AjaxRequestTarget target) {
154 getDomainOnChangeComponents().forEach(target::add);
155 }
156 }).add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
157
158 private static final long serialVersionUID = -1107858522700306810L;
159
160 @Override
161 protected void onUpdate(final AjaxRequestTarget target) {
162 getDomainOnChangeComponents().forEach(target::add);
163 }
164 });
165 form.add(domainSelect.setOutputMarkupId(true));
166
167 AjaxButton submitButton = new AjaxButton("submit", new Model<>(getString("submit"))) {
168
169 private static final long serialVersionUID = 429178684321093953L;
170
171 @Override
172 protected void onSubmit(final AjaxRequestTarget target) {
173 authenticate(usernameField.getRawInput(), passwordField.getRawInput(), target);
174 }
175
176 };
177 submitButton.setDefaultFormProcessing(false);
178 form.add(submitButton);
179 form.setDefaultButton(submitButton);
180
181 List<Panel> ssoLoginFormPanels = getSSOLoginFormPanels();
182 ListView<Panel> ssoLogins = new ListView<>("ssoLogins", ssoLoginFormPanels) {
183
184 private static final long serialVersionUID = -9180479401817023838L;
185
186 @Override
187 protected void populateItem(final ListItem<Panel> item) {
188 item.add(item.getModelObject());
189 }
190 };
191 form.add(ssoLogins);
192
193 add(form);
194 }
195
196 protected Collection<Component> getLanguageOnChangeComponents() {
197 return List.of(form);
198 }
199
200 protected Collection<Component> getDomainOnChangeComponents() {
201 return List.of(form);
202 }
203
204 @Override
205 public void renderHead(final IHeaderResponse response) {
206 super.renderHead(response);
207 if (StringUtils.isNotBlank(notificationMessage)) {
208 response.render(OnLoadHeaderItem.forScript(StyledNotificationBehavior.jQueryShow(notificationMessage,
209 String.format("jQuery('#%s').data('kendoNotification')",
210 notificationPanel.getNotificationMarkupId()), notificationLevel)));
211 }
212 }
213
214 protected abstract BaseSession getBaseSession();
215
216 protected abstract List<Panel> getSSOLoginFormPanels();
217
218 protected abstract void sendError(String error);
219
220 protected abstract void authenticate(
221 String username,
222 String password,
223 AjaxRequestTarget target)
224 throws AccessControlException;
225
226
227
228
229 protected class LocaleDropDown extends DropDownChoice<Locale> {
230
231 private static final long serialVersionUID = 2349382679992357202L;
232
233 protected class LocaleRenderer extends ChoiceRenderer<Locale> {
234
235 private static final long serialVersionUID = -3657529581555164741L;
236
237 @Override
238 public String getDisplayValue(final Locale locale) {
239 return locale.getDisplayName(getLocale());
240 }
241 }
242
243 protected LocaleDropDown(final String id) {
244 super(id, getBaseSession().getSupportedLocales());
245
246 setChoiceRenderer(new LocaleRenderer());
247 setModel(new IModel<>() {
248
249 private static final long serialVersionUID = -6985170095629312963L;
250
251 @Override
252 public Locale getObject() {
253 return getSession().getLocale();
254 }
255
256 @Override
257 public void setObject(final Locale object) {
258 getSession().setLocale(object);
259 }
260
261 @Override
262 public void detach() {
263
264 }
265 });
266
267
268 List<Locale> filtered = List.of();
269
270 String acceptLanguage = ((ServletWebRequest) RequestCycle.get().getRequest()).
271 getHeader(HttpHeaders.ACCEPT_LANGUAGE);
272 if (StringUtils.isNotBlank(acceptLanguage)) {
273 try {
274 filtered = Locale.filter(
275 Locale.LanguageRange.parse(acceptLanguage),
276 getBaseSession().getSupportedLocales());
277 } catch (Exception e) {
278 LOG.debug("Could not parse {} HTTP header value '{}'",
279 HttpHeaders.ACCEPT_LANGUAGE, acceptLanguage, e);
280 }
281 }
282
283 getModel().setObject(filtered.isEmpty()
284 ? Locale.ENGLISH
285 : filtered.get(0));
286 }
287 }
288 }