View Javadoc
1   package org.argeo.cms.ui.widgets.auth;
2   
3   import java.io.IOException;
4   import java.util.Arrays;
5   
6   import javax.security.auth.callback.Callback;
7   import javax.security.auth.callback.CallbackHandler;
8   import javax.security.auth.callback.NameCallback;
9   import javax.security.auth.callback.PasswordCallback;
10  import javax.security.auth.callback.TextOutputCallback;
11  import javax.security.auth.callback.UnsupportedCallbackException;
12  
13  import org.eclipse.swt.SWT;
14  import org.eclipse.swt.events.KeyEvent;
15  import org.eclipse.swt.events.KeyListener;
16  import org.eclipse.swt.events.ModifyEvent;
17  import org.eclipse.swt.events.ModifyListener;
18  import org.eclipse.swt.events.SelectionEvent;
19  import org.eclipse.swt.events.SelectionListener;
20  import org.eclipse.swt.layout.GridData;
21  import org.eclipse.swt.widgets.Combo;
22  import org.eclipse.swt.widgets.Composite;
23  import org.eclipse.swt.widgets.Control;
24  import org.eclipse.swt.widgets.Label;
25  import org.eclipse.swt.widgets.Text;
26  
27  /**
28   * A composite that can populate itself based on {@link Callback}s. It can be
29   * used directly as a {@link CallbackHandler} or be used by one by calling the
30   * {@link #createCallbackHandlers(Callback[])}. Supported standard
31   * {@link Callback}s are:<br>
32   * <ul>
33   * <li>{@link PasswordCallback}</li>
34   * <li>{@link NameCallback}</li>
35   * <li>{@link TextOutputCallback}</li>
36   * </ul>
37   * Supported Argeo {@link Callback}s are:<br>
38   * <ul>
39   * <li>{@link LocaleChoice}</li>
40   * </ul>
41   */
42  public class CompositeCallbackHandler extends Composite implements CallbackHandler {
43  	private static final long serialVersionUID = -928223893722723777L;
44  
45  	private boolean wasUsedAlready = false;
46  	private boolean isSubmitted = false;
47  	private boolean isCanceled = false;
48  
49  	public CompositeCallbackHandler(Composite parent, int style) {
50  		super(parent, style);
51  	}
52  
53  	@Override
54  	public synchronized void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {
55  		// reset
56  		if (wasUsedAlready && !isSubmitted() && !isCanceled()) {
57  			cancel();
58  			for (Control control : getChildren())
59  				control.dispose();
60  			isSubmitted = false;
61  			isCanceled = false;
62  		}
63  
64  		for (Callback callback : callbacks)
65  			checkCallbackSupported(callback);
66  		// create controls synchronously in the UI thread
67  		getDisplay().syncExec(new Runnable() {
68  
69  			@Override
70  			public void run() {
71  				createCallbackHandlers(callbacks);
72  			}
73  		});
74  
75  		if (!wasUsedAlready)
76  			wasUsedAlready = true;
77  
78  		// while (!isSubmitted() && !isCanceled()) {
79  		// try {
80  		// wait(1000l);
81  		// } catch (InterruptedException e) {
82  		// // silent
83  		// }
84  		// }
85  
86  		// cleanCallbacksAfterCancel(callbacks);
87  	}
88  
89  	public void checkCallbackSupported(Callback callback) throws UnsupportedCallbackException {
90  		if (callback instanceof TextOutputCallback || callback instanceof NameCallback
91  				|| callback instanceof PasswordCallback || callback instanceof LocaleChoice) {
92  			return;
93  		} else {
94  			throw new UnsupportedCallbackException(callback);
95  		}
96  	}
97  
98  	/**
99  	 * Set writable callbacks to null if the handle is canceled (check is done
100 	 * by the method)
101 	 */
102 	public void cleanCallbacksAfterCancel(Callback[] callbacks) {
103 		if (isCanceled()) {
104 			for (Callback callback : callbacks) {
105 				if (callback instanceof NameCallback) {
106 					((NameCallback) callback).setName(null);
107 				} else if (callback instanceof PasswordCallback) {
108 					PasswordCallback pCallback = (PasswordCallback) callback;
109 					char[] arr = pCallback.getPassword();
110 					if (arr != null) {
111 						Arrays.fill(arr, '*');
112 						pCallback.setPassword(null);
113 					}
114 				}
115 			}
116 		}
117 	}
118 
119 	public void createCallbackHandlers(Callback[] callbacks) {
120 		Composite composite = this;
121 		for (int i = 0; i < callbacks.length; i++) {
122 			Callback callback = callbacks[i];
123 			if (callback instanceof TextOutputCallback) {
124 				createLabelTextoutputHandler(composite, (TextOutputCallback) callback);
125 			} else if (callback instanceof NameCallback) {
126 				createNameHandler(composite, (NameCallback) callback);
127 			} else if (callback instanceof PasswordCallback) {
128 				createPasswordHandler(composite, (PasswordCallback) callback);
129 			} else if (callback instanceof LocaleChoice) {
130 				createLocaleHandler(composite, (LocaleChoice) callback);
131 			}
132 		}
133 	}
134 
135 	protected Text createNameHandler(Composite composite, final NameCallback callback) {
136 		Label label = new Label(composite, SWT.NONE);
137 		label.setText(callback.getPrompt());
138 		final Text text = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
139 		if (callback.getDefaultName() != null) {
140 			// set default value, if provided
141 			text.setText(callback.getDefaultName());
142 			callback.setName(callback.getDefaultName());
143 		}
144 		text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
145 		text.addModifyListener(new ModifyListener() {
146 			private static final long serialVersionUID = 7300032545287292973L;
147 
148 			public void modifyText(ModifyEvent event) {
149 				callback.setName(text.getText());
150 			}
151 		});
152 		text.addSelectionListener(new SelectionListener() {
153 			private static final long serialVersionUID = 1820530045857665111L;
154 
155 			@Override
156 			public void widgetSelected(SelectionEvent e) {
157 			}
158 
159 			@Override
160 			public void widgetDefaultSelected(SelectionEvent e) {
161 				submit();
162 			}
163 		});
164 
165 		text.addKeyListener(new KeyListener() {
166 			private static final long serialVersionUID = -8698107785092095713L;
167 
168 			@Override
169 			public void keyReleased(KeyEvent e) {
170 			}
171 
172 			@Override
173 			public void keyPressed(KeyEvent e) {
174 			}
175 		});
176 		return text;
177 	}
178 
179 	protected Text createPasswordHandler(Composite composite, final PasswordCallback callback) {
180 		Label label = new Label(composite, SWT.NONE);
181 		label.setText(callback.getPrompt());
182 		final Text passwordText = new Text(composite, SWT.SINGLE | SWT.LEAD | SWT.PASSWORD | SWT.BORDER);
183 		passwordText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
184 		passwordText.addModifyListener(new ModifyListener() {
185 			private static final long serialVersionUID = -7099363995047686732L;
186 
187 			public void modifyText(ModifyEvent event) {
188 				callback.setPassword(passwordText.getTextChars());
189 			}
190 		});
191 		passwordText.addSelectionListener(new SelectionListener() {
192 			private static final long serialVersionUID = 1820530045857665111L;
193 
194 			@Override
195 			public void widgetSelected(SelectionEvent e) {
196 			}
197 
198 			@Override
199 			public void widgetDefaultSelected(SelectionEvent e) {
200 				submit();
201 			}
202 		});
203 		return passwordText;
204 	}
205 
206 	protected Combo createLocaleHandler(Composite composite, final LocaleChoice callback) {
207 		String[] labels = callback.getSupportedLocalesLabels();
208 		if (labels.length == 0)
209 			return null;
210 		Label label = new Label(composite, SWT.NONE);
211 		label.setText("Language");
212 
213 		final Combo combo = new Combo(composite, SWT.READ_ONLY);
214 		combo.setItems(labels);
215 		combo.select(callback.getDefaultIndex());
216 		combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
217 		combo.addSelectionListener(new SelectionListener() {
218 			private static final long serialVersionUID = 38678989091946277L;
219 
220 			@Override
221 			public void widgetSelected(SelectionEvent e) {
222 				callback.setSelectedIndex(combo.getSelectionIndex());
223 			}
224 
225 			@Override
226 			public void widgetDefaultSelected(SelectionEvent e) {
227 			}
228 		});
229 		return combo;
230 	}
231 
232 	protected Label createLabelTextoutputHandler(Composite composite, final TextOutputCallback callback) {
233 		Label label = new Label(composite, SWT.NONE);
234 		label.setText(callback.getMessage());
235 		GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
236 		data.horizontalSpan = 2;
237 		label.setLayoutData(data);
238 		return label;
239 		// TODO: find a way to pass this information
240 		// int messageType = callback.getMessageType();
241 		// int dialogMessageType = IMessageProvider.NONE;
242 		// switch (messageType) {
243 		// case TextOutputCallback.INFORMATION:
244 		// dialogMessageType = IMessageProvider.INFORMATION;
245 		// break;
246 		// case TextOutputCallback.WARNING:
247 		// dialogMessageType = IMessageProvider.WARNING;
248 		// break;
249 		// case TextOutputCallback.ERROR:
250 		// dialogMessageType = IMessageProvider.ERROR;
251 		// break;
252 		// }
253 		// setMessage(callback.getMessage(), dialogMessageType);
254 	}
255 
256 	synchronized boolean isSubmitted() {
257 		return isSubmitted;
258 	}
259 
260 	synchronized boolean isCanceled() {
261 		return isCanceled;
262 	}
263 
264 	protected synchronized void submit() {
265 		isSubmitted = true;
266 		notifyAll();
267 	}
268 
269 	protected synchronized void cancel() {
270 		isCanceled = true;
271 		notifyAll();
272 	}
273 }