1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package edu.internet2.middleware.shibboleth.common.config.resource;
18
19 import java.io.File;
20 import java.util.ArrayList;
21
22 import javax.xml.namespace.QName;
23
24 import org.opensaml.xml.util.DatatypeHelper;
25 import org.opensaml.xml.util.XMLHelper;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28 import org.springframework.beans.factory.BeanCreationException;
29 import org.springframework.beans.factory.support.AbstractBeanDefinition;
30 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
31 import org.springframework.beans.factory.xml.ParserContext;
32 import org.tmatesoft.svn.core.SVNException;
33 import org.tmatesoft.svn.core.SVNURL;
34 import org.tmatesoft.svn.core.auth.SVNAuthentication;
35 import org.tmatesoft.svn.core.auth.SVNPasswordAuthentication;
36 import org.tmatesoft.svn.core.auth.SVNUserNameAuthentication;
37 import org.tmatesoft.svn.core.wc.SVNClientManager;
38 import org.w3c.dom.Element;
39
40 import edu.internet2.middleware.shibboleth.common.config.SpringConfigurationUtils;
41 import edu.internet2.middleware.shibboleth.common.resource.SVNBasicAuthenticationManager;
42 import edu.internet2.middleware.shibboleth.common.resource.SVNResource;
43
44
45 public class SVNResourceBeanDefinitionParser extends AbstractResourceBeanDefinitionParser {
46
47
48 public static final QName SCHEMA_TYPE = new QName(ResourceNamespaceHandler.NAMESPACE, "SVNResource");
49
50
51 public static final String REPOSITORY_URL_ATTRIB_NAME = "repositoryURL";
52
53
54 public static final String CTX_TIMEOUT_ATTRIB_NAME = "connectionTimeout";
55
56
57 public static final String READ_TIMEOUT_ATTRIB_NAME = "readTimeout";
58
59
60 public static final String WORKING_COPY_DIR_ATTRIB_NAME = "workingCopyDirectory";
61
62
63 public static final String REVISION_ATTRIB_NAME = "revision";
64
65
66
67
68
69 public static final String RESOURCE_FILE_ATTRIB_NAME = "resourceFile";
70
71
72 public static final String USERNAME_ATTRIB_NAME = "username";
73
74
75 public static final String PASSWORD_ATTRIB_NAME = "password";
76
77
78
79
80
81 public static final String PROXY_HOST_ATTRIB_NAME = "proxyHost";
82
83
84
85
86
87 public static final String PROXY_PORT_ATTRIB_NAME = "proxyPort";
88
89
90
91
92
93 public static final String PROXY_USERNAME_ATTRIB_NAME = "proxyUsername";
94
95
96
97
98
99 public static final String PROXY_PASSWORD_ATTRIB_NAME = "proxyPassword";
100
101
102 public static final int DEFAULT_CTX_TIMEOUT = 3000;
103
104
105 public static final int DEFAULT_READ_TIMEOUT = 5000;
106
107
108 public static final int DEFAULT_PROXY_PORT = 8080;
109
110
111 private final Logger log = LoggerFactory.getLogger(SVNResourceBeanDefinitionParser.class);
112
113
114 protected Class getBeanClass(Element arg0) {
115 return SVNResource.class;
116 }
117
118
119 protected String resolveId(Element configElement, AbstractBeanDefinition beanDefinition, ParserContext parserContext) {
120 return SVNResource.class.getName() + ":"
121 + DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null, REPOSITORY_URL_ATTRIB_NAME));
122 }
123
124
125 protected void doParse(Element configElement, ParserContext parserContext, BeanDefinitionBuilder builder)
126 throws BeanCreationException {
127 super.doParse(configElement, parserContext, builder);
128
129 builder.addConstructorArgValue(buildClientManager(configElement));
130
131 builder.addConstructorArgValue(getRespositoryUrl(configElement));
132
133 builder.addConstructorArgValue(getWorkingCopyDirectory(configElement));
134
135 builder.addConstructorArgValue(getRevision(configElement));
136
137 builder.addConstructorArgValue(getResourceFile(configElement));
138
139 addResourceFilter(configElement, parserContext, builder);
140 }
141
142
143
144
145
146
147
148
149 protected SVNClientManager buildClientManager(Element configElement) {
150 ArrayList<SVNAuthentication> authnMethods = new ArrayList<SVNAuthentication>();
151 String username = getUsername(configElement);
152 if (username != null) {
153 authnMethods.add(new SVNUserNameAuthentication(username, false));
154
155 String password = getPassword(configElement);
156 if (password != null) {
157 authnMethods.add(new SVNPasswordAuthentication(username, password, false));
158 }
159 }
160
161 String proxyHost = getProxyHost(configElement);
162 int proxyPort = getProxyPort(configElement);
163 String proxyUser = getProxyUsername(configElement);
164 String proxyPassword = getPassword(configElement);
165
166 SVNBasicAuthenticationManager authnManager;
167 if (proxyHost == null) {
168 authnManager = new SVNBasicAuthenticationManager(authnMethods);
169 } else {
170 authnManager = new SVNBasicAuthenticationManager(authnMethods, proxyHost, proxyPort, proxyUser,
171 proxyPassword);
172 }
173 authnManager.setConnectionTimeout(getConnectionTimeout(configElement));
174 authnManager.setReadTimeout(getReadTimeout(configElement));
175
176 SVNClientManager clientManager = SVNClientManager.newInstance();
177 clientManager.setAuthenticationManager(authnManager);
178 return clientManager;
179 }
180
181
182
183
184
185
186
187
188
189
190 protected SVNURL getRespositoryUrl(Element configElement) throws BeanCreationException {
191 if (!configElement.hasAttributeNS(null, REPOSITORY_URL_ATTRIB_NAME)) {
192 log.error("SVN resource definition missing required '" + REPOSITORY_URL_ATTRIB_NAME + "' attribute");
193 throw new BeanCreationException("SVN resource definition missing required '" + REPOSITORY_URL_ATTRIB_NAME
194 + "' attribute");
195 }
196
197 String repositoryUrl = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
198 REPOSITORY_URL_ATTRIB_NAME));
199 try {
200 return SVNURL.parseURIDecoded(repositoryUrl);
201 } catch (SVNException e) {
202 log.error("SVN remote repository URL " + repositoryUrl + " is not valid", e);
203 throw new BeanCreationException("SVN remote repository URL " + repositoryUrl + " is not valid", e);
204 }
205 }
206
207
208
209
210
211
212
213
214
215
216 protected int getConnectionTimeout(Element configElement) throws BeanCreationException {
217 if (!configElement.hasAttributeNS(null, CTX_TIMEOUT_ATTRIB_NAME)) {
218 return DEFAULT_CTX_TIMEOUT;
219 }
220
221 return (int) SpringConfigurationUtils.parseDurationToMillis(CTX_TIMEOUT_ATTRIB_NAME + " on SVN resource",
222 configElement.getAttributeNS(null, CTX_TIMEOUT_ATTRIB_NAME), 0);
223 }
224
225
226
227
228
229
230
231
232
233
234 protected int getReadTimeout(Element configElement) throws BeanCreationException {
235 if (!configElement.hasAttributeNS(null, READ_TIMEOUT_ATTRIB_NAME)) {
236 return DEFAULT_READ_TIMEOUT;
237 }
238
239 return (int) SpringConfigurationUtils.parseDurationToMillis(READ_TIMEOUT_ATTRIB_NAME + " on SVN resource",
240 configElement.getAttributeNS(null, CTX_TIMEOUT_ATTRIB_NAME), 0);
241 }
242
243
244
245
246
247
248
249
250
251
252 protected File getWorkingCopyDirectory(Element configElement) throws BeanCreationException {
253 if (!configElement.hasAttributeNS(null, WORKING_COPY_DIR_ATTRIB_NAME)) {
254 log.error("SVN resource definition missing required '" + WORKING_COPY_DIR_ATTRIB_NAME + "' attribute");
255 throw new BeanCreationException("SVN resource definition missing required '" + WORKING_COPY_DIR_ATTRIB_NAME
256 + "' attribute");
257 }
258
259 File directory = new File(DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
260 WORKING_COPY_DIR_ATTRIB_NAME)));
261 if (directory == null) {
262 log.error("SVN working copy directory may not be null");
263 throw new BeanCreationException("SVN working copy directory may not be null");
264 }
265
266 if (!directory.exists()) {
267 boolean created = directory.mkdirs();
268 if (!created) {
269 log.error("SVN working copy direction " + directory.getAbsolutePath()
270 + " does not exist and could not be created");
271 throw new BeanCreationException("SVN working copy direction " + directory.getAbsolutePath()
272 + " does not exist and could not be created");
273 }
274 }
275
276 if (!directory.isDirectory()) {
277 log.error("SVN working copy location " + directory.getAbsolutePath() + " is not a directory");
278 throw new BeanCreationException("SVN working copy location " + directory.getAbsolutePath()
279 + " is not a directory");
280 }
281
282 if (!directory.canRead()) {
283 log.error("SVN working copy directory " + directory.getAbsolutePath() + " can not be read by this process");
284 throw new BeanCreationException("SVN working copy directory " + directory.getAbsolutePath()
285 + " can not be read by this process");
286 }
287
288 if (!directory.canWrite()) {
289 log.error("SVN working copy directory " + directory.getAbsolutePath()
290 + " can not be written to by this process");
291 throw new BeanCreationException("SVN working copy directory " + directory.getAbsolutePath()
292 + " can not be written to by this process");
293 }
294
295 return directory;
296 }
297
298
299
300
301
302
303
304
305
306
307 protected long getRevision(Element configElement) throws BeanCreationException {
308 if (!configElement.hasAttributeNS(null, REVISION_ATTRIB_NAME)) {
309 return -1;
310 } else {
311 try {
312 return Long.parseLong(DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
313 WORKING_COPY_DIR_ATTRIB_NAME)));
314 } catch (NumberFormatException e) {
315 log
316 .error("SVN resource definition attribute '" + REVISION_ATTRIB_NAME
317 + "' contains an invalid number");
318 throw new BeanCreationException("SVN resource definition attribute '" + REVISION_ATTRIB_NAME
319 + "' contains an invalid number");
320 }
321 }
322 }
323
324
325
326
327
328
329
330
331
332
333 protected String getResourceFile(Element configElement) throws BeanCreationException {
334 if (!configElement.hasAttributeNS(null, RESOURCE_FILE_ATTRIB_NAME)) {
335 log.error("SVN resource definition missing required '" + RESOURCE_FILE_ATTRIB_NAME + "' attribute");
336 throw new BeanCreationException("SVN resource definition missing required '" + RESOURCE_FILE_ATTRIB_NAME
337 + "' attribute");
338 }
339
340 String filename = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
341 RESOURCE_FILE_ATTRIB_NAME));
342 if (filename == null) {
343 log.error("SVN resource definition attribute '" + RESOURCE_FILE_ATTRIB_NAME
344 + "' may not be an empty string");
345 throw new BeanCreationException("SVN resource definition attribute '" + RESOURCE_FILE_ATTRIB_NAME
346 + "' may not be an empty string");
347 }
348
349 return filename;
350 }
351
352
353
354
355
356
357
358
359
360
361 protected String getUsername(Element configElement) throws BeanCreationException {
362 if (configElement.hasAttributeNS(null, USERNAME_ATTRIB_NAME)) {
363 String username = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
364 USERNAME_ATTRIB_NAME));
365 if (username == null) {
366 log
367 .error("SVN resource definition attribute '" + USERNAME_ATTRIB_NAME
368 + "' may not be an empty string");
369 throw new BeanCreationException("SVN resource definition attribute '" + USERNAME_ATTRIB_NAME
370 + "' may not be an empty string");
371 }
372 return username;
373 }
374
375 return null;
376 }
377
378
379
380
381
382
383
384
385
386
387 protected String getPassword(Element configElement) throws BeanCreationException {
388 if (configElement.hasAttributeNS(null, PASSWORD_ATTRIB_NAME)) {
389 String password = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
390 PASSWORD_ATTRIB_NAME));
391 if (password == null) {
392 log
393 .error("SVN resource definition attribute '" + PASSWORD_ATTRIB_NAME
394 + "' may not be an empty string");
395 throw new BeanCreationException("SVN resource definition attribute '" + PASSWORD_ATTRIB_NAME
396 + "' may not be an empty string");
397 }
398 return password;
399 }
400 return null;
401 }
402
403
404
405
406
407
408
409
410
411
412 protected String getProxyHost(Element configElement) throws BeanCreationException {
413 if (configElement.hasAttributeNS(null, PROXY_HOST_ATTRIB_NAME)) {
414 String host = DatatypeHelper.safeTrimOrNullString(configElement
415 .getAttributeNS(null, PROXY_HOST_ATTRIB_NAME));
416 if (host == null) {
417 log.error("SVN resource definition attribute '" + PROXY_HOST_ATTRIB_NAME
418 + "' may not be an empty string");
419 throw new BeanCreationException("SVN resource definition attribute '" + PROXY_HOST_ATTRIB_NAME
420 + "' may not be an empty string");
421 }
422 return host;
423 }
424
425 return null;
426 }
427
428
429
430
431
432
433
434
435
436
437 protected int getProxyPort(Element configElement) throws BeanCreationException {
438 if (!configElement.hasAttributeNS(null, PROXY_PORT_ATTRIB_NAME)) {
439 return DEFAULT_PROXY_PORT;
440 }
441
442 String port = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null, PROXY_PORT_ATTRIB_NAME));
443 if (port == null) {
444 log.error("SVN resource definition attribute '" + PROXY_PORT_ATTRIB_NAME + "' may not be an empty string");
445 throw new BeanCreationException("SVN resource definition attribute '" + PROXY_PORT_ATTRIB_NAME
446 + "' may not be an empty string");
447 }
448
449 try {
450 return Integer.parseInt(port);
451 } catch (NumberFormatException e) {
452 log.error("SVN resource definition attribute '" + PROXY_PORT_ATTRIB_NAME + "' contains an invalid number");
453 throw new BeanCreationException("SVN resource definition attribute '" + PROXY_PORT_ATTRIB_NAME
454 + "' contains an invalid number");
455 }
456 }
457
458
459
460
461
462
463
464
465
466
467 protected String getProxyUsername(Element configElement) throws BeanCreationException {
468 if (configElement.hasAttributeNS(null, PROXY_USERNAME_ATTRIB_NAME)) {
469 String username = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
470 PROXY_USERNAME_ATTRIB_NAME));
471 if (username == null) {
472 log.error("SVN resource definition attribute '" + PROXY_USERNAME_ATTRIB_NAME
473 + "' may not be an empty string");
474 throw new BeanCreationException("SVN resource definition attribute '" + PROXY_USERNAME_ATTRIB_NAME
475 + "' may not be an empty string");
476 }
477 return username;
478 }
479 return null;
480 }
481
482
483
484
485
486
487
488
489
490
491 protected String getProxyPassword(Element configElement) throws BeanCreationException {
492 if (configElement.hasAttributeNS(null, PROXY_PASSWORD_ATTRIB_NAME)) {
493 String password = DatatypeHelper.safeTrimOrNullString(configElement.getAttributeNS(null,
494 PROXY_PASSWORD_ATTRIB_NAME));
495 if (password == null) {
496 log.error("SVN resource definition attribute '" + PROXY_PASSWORD_ATTRIB_NAME
497 + "' may not be an empty string");
498 throw new BeanCreationException("SVN resource definition attribute '" + PROXY_PASSWORD_ATTRIB_NAME
499 + "' may not be an empty string");
500 }
501 return password;
502 }
503 return null;
504 }
505
506 }