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