1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package edu.internet2.middleware.shibboleth.common.attribute;
18
19 import jargs.gnu.CmdLineParser;
20
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.io.PrintStream;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.Map;
28
29 import org.opensaml.Configuration;
30 import org.opensaml.common.SAMLObject;
31 import org.opensaml.saml2.metadata.provider.MetadataProvider;
32 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
33 import org.opensaml.util.resource.FilesystemResource;
34 import org.opensaml.util.resource.Resource;
35 import org.opensaml.util.resource.ResourceException;
36 import org.opensaml.xml.io.Marshaller;
37 import org.opensaml.xml.io.MarshallingException;
38 import org.opensaml.xml.util.XMLHelper;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41 import org.springframework.context.ApplicationContext;
42 import org.springframework.context.support.GenericApplicationContext;
43 import org.w3c.dom.Element;
44
45 import ch.qos.logback.classic.LoggerContext;
46 import ch.qos.logback.classic.joran.JoranConfigurator;
47 import ch.qos.logback.core.joran.spi.JoranException;
48 import ch.qos.logback.core.status.ErrorStatus;
49 import ch.qos.logback.core.status.InfoStatus;
50 import ch.qos.logback.core.status.StatusManager;
51
52 import edu.internet2.middleware.shibboleth.common.attribute.provider.SAML1AttributeAuthority;
53 import edu.internet2.middleware.shibboleth.common.attribute.provider.SAML2AttributeAuthority;
54 import edu.internet2.middleware.shibboleth.common.config.SpringConfigurationUtils;
55 import edu.internet2.middleware.shibboleth.common.profile.provider.BaseSAMLProfileRequestContext;
56 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
57 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.SAMLMDRelyingPartyConfigurationManager;
58
59
60
61
62
63
64
65
66
67 public class AttributeAuthorityCLI {
68
69
70 private static Logger log = LoggerFactory.getLogger(AttributeAuthorityCLI.class);
71
72
73 private static String[] aacliConfigs = { "/internal.xml", "/service.xml", };
74
75
76 private static SAML1AttributeAuthority saml1AA;
77
78
79 private static SAML2AttributeAuthority saml2AA;
80
81
82
83
84
85
86
87
88 public static void main(String[] args) throws Exception {
89 CmdLineParser parser = parseCommandArguments(args);
90 ApplicationContext appCtx = loadConfigurations((String) parser.getOptionValue(CLIParserBuilder.CONFIG_DIR_ARG));
91
92 saml1AA = (SAML1AttributeAuthority) appCtx.getBean("shibboleth.SAML1AttributeAuthority");
93 saml2AA = (SAML2AttributeAuthority) appCtx.getBean("shibboleth.SAML2AttributeAuthority");
94
95 SAMLObject attributeStatement;
96 Boolean saml1 = (Boolean) parser.getOptionValue(CLIParserBuilder.SAML1_ARG, Boolean.FALSE);
97 if (saml1.booleanValue()) {
98 attributeStatement = performSAML1AttributeResolution(parser, appCtx);
99 } else {
100 attributeStatement = performSAML2AttributeResolution(parser, appCtx);
101 }
102
103 printAttributeStatement(attributeStatement);
104 }
105
106
107
108
109
110
111
112
113
114
115 private static CmdLineParser parseCommandArguments(String[] args) throws Exception {
116 if (args.length < 2) {
117 printHelp(System.out);
118 System.out.flush();
119 System.exit(0);
120 }
121
122 CmdLineParser parser = CLIParserBuilder.buildParser();
123
124 try {
125 parser.parse(args);
126 } catch (CmdLineParser.OptionException e) {
127 errorAndExit(e.getMessage(), e);
128 }
129
130 Boolean helpEnabled = (Boolean) parser.getOptionValue(CLIParserBuilder.HELP_ARG);
131 if (helpEnabled != null) {
132 printHelp(System.out);
133 System.out.flush();
134 System.exit(0);
135 }
136
137 return parser;
138 }
139
140
141
142
143
144
145
146
147
148
149
150 private static ApplicationContext loadConfigurations(String configDir) throws IOException, ResourceException {
151 File configDirectory;
152
153 if (configDir != null) {
154 configDirectory = new File(configDir);
155 } else {
156 configDirectory = new File(System.getenv("IDP_HOME") + "/conf");
157 }
158
159 if (!configDirectory.exists() || !configDirectory.isDirectory() || !configDirectory.canRead()) {
160 errorAndExit("Configuration directory " + configDir
161 + " does not exist, is not a directory, or is not readable", null);
162 }
163
164 loadLoggingConfiguration(configDirectory.getAbsolutePath());
165
166 List<Resource> configs = new ArrayList<Resource>();
167
168 File config;
169 for (int i = 0; i < aacliConfigs.length; i++) {
170 config = new File(configDirectory.getPath() + aacliConfigs[i]);
171 if (config.isDirectory() || !config.canRead()) {
172 errorAndExit("Configuration file " + config.getAbsolutePath() + " is a directory or is not readable",
173 null);
174 }
175 configs.add(new FilesystemResource(config.getPath()));
176 }
177
178 GenericApplicationContext gContext = new GenericApplicationContext();
179 SpringConfigurationUtils.populateRegistry(gContext, configs);
180 gContext.refresh();
181 return gContext;
182 }
183
184
185
186
187
188
189 private static void loadLoggingConfiguration(String configDir) {
190 String loggingConfig = configDir + File.separator + "logging.xml";
191
192 LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
193 StatusManager statusManager = loggerContext.getStatusManager();
194 statusManager.add(new InfoStatus("Loading logging configuration file: " + loggingConfig, null));
195 try {
196
197 loggerContext.reset();
198 JoranConfigurator configurator = new JoranConfigurator();
199 configurator.setContext(loggerContext);
200 configurator.doConfigure(new FileInputStream(loggingConfig));
201 loggerContext.start();
202 } catch (JoranException e) {
203 statusManager.add(new ErrorStatus("Error loading logging configuration file: " + configDir, null, e));
204 } catch (IOException e) {
205 statusManager.add(new ErrorStatus("Error loading logging configuration file: " + configDir, null, e));
206 }
207 }
208
209
210
211
212
213
214
215
216
217 private static SAMLObject performSAML1AttributeResolution(CmdLineParser parser, ApplicationContext appCtx) {
218 BaseSAMLProfileRequestContext requestCtx = buildAttributeRequestContext(parser, appCtx);
219
220 try {
221 Map<String, BaseAttribute> attributes = saml1AA.getAttributes(requestCtx);
222 return saml1AA.buildAttributeStatement(null, attributes.values());
223 } catch (AttributeRequestException e) {
224 errorAndExit("Error encountered during attribute resolution and filtering", e);
225 }
226
227 return null;
228 }
229
230
231
232
233
234
235
236
237
238 private static SAMLObject performSAML2AttributeResolution(CmdLineParser parser, ApplicationContext appCtx) {
239 BaseSAMLProfileRequestContext requestCtx = buildAttributeRequestContext(parser, appCtx);
240
241 try {
242 Map<String, BaseAttribute> attributes = saml2AA.getAttributes(requestCtx);
243 return saml2AA.buildAttributeStatement(null, attributes.values());
244 } catch (AttributeRequestException e) {
245 errorAndExit("Error encountered during attribute resolution and filtering", e);
246 }
247
248 return null;
249 }
250
251
252
253
254
255
256
257
258
259 private static BaseSAMLProfileRequestContext buildAttributeRequestContext(CmdLineParser parser,
260 ApplicationContext appCtx) {
261 BaseSAMLProfileRequestContext requestContext = new BaseSAMLProfileRequestContext();
262
263 String[] rpConfigManagerNames = appCtx.getBeanNamesForType(SAMLMDRelyingPartyConfigurationManager.class);
264 SAMLMDRelyingPartyConfigurationManager rpConfigManager = (SAMLMDRelyingPartyConfigurationManager) appCtx
265 .getBean(rpConfigManagerNames[0]);
266
267 requestContext.setMetadataProvider(rpConfigManager.getMetadataProvider());
268
269 String requester = (String) parser.getOptionValue(CLIParserBuilder.REQUESTER_ARG);
270 if (requester != null) {
271 requestContext.setRelyingPartyConfiguration(rpConfigManager.getRelyingPartyConfiguration(requester));
272 } else {
273 requester = rpConfigManager.getAnonymousRelyingConfiguration().getRelyingPartyId();
274 requestContext.setRelyingPartyConfiguration(rpConfigManager.getAnonymousRelyingConfiguration());
275 }
276
277 try {
278 requestContext.setInboundMessageIssuer(requester);
279 requestContext.setPeerEntityId(requester);
280 requestContext.setPeerEntityMetadata(requestContext.getMetadataProvider().getEntityDescriptor(requester));
281 } catch (MetadataProviderException e) {
282 errorAndExit("Unable to query for metadata for requester " + requester, e);
283 }
284
285 try {
286 String issuer = requestContext.getRelyingPartyConfiguration().getProviderId();
287 requestContext.setOutboundMessageIssuer(issuer);
288 requestContext.setLocalEntityId(issuer);
289 requestContext.setLocalEntityMetadata(requestContext.getMetadataProvider().getEntityDescriptor(issuer));
290 } catch (MetadataProviderException e) {
291 errorAndExit("Unable to query for metadata for issuer " + requester, e);
292 }
293
294 String principal = (String) parser.getOptionValue(CLIParserBuilder.PRINCIPAL_ARG);
295 requestContext.setPrincipalName(principal);
296
297 String authnMethod = (String) parser.getOptionValue(CLIParserBuilder.AUTHN_METHOD_ARG);
298 requestContext.setPrincipalAuthenticationMethod(authnMethod);
299
300 return requestContext;
301 }
302
303
304
305
306
307
308 private static void printAttributeStatement(SAMLObject attributeStatement) {
309 if (attributeStatement == null) {
310 System.out.println("No attribute statement.");
311 return;
312 }
313
314 Marshaller statementMarshaller = Configuration.getMarshallerFactory().getMarshaller(attributeStatement);
315
316 try {
317 Element statement = statementMarshaller.marshall(attributeStatement);
318 System.out.println();
319 System.out.println(XMLHelper.prettyPrintXML(statement));
320 } catch (MarshallingException e) {
321 errorAndExit("Unable to marshall attribute statement", e);
322 }
323 }
324
325
326
327
328
329
330 private static void printHelp(PrintStream out) {
331 out.println("Attribute Authority, Command Line Interface");
332 out.println(" This tools provides a command line interface to the Shibboleth Attribute Authority,");
333 out.println(" providing deployers a means to test their attribute resolution and configurations.");
334 out.println();
335 out.println("usage:");
336 out.println(" On Unix systems: ./aacli.sh <PARAMETERS>");
337 out.println(" On Windows systems: .\\aacli.bat <PARAMETERS>");
338 out.println();
339 out.println("Required Parameters:");
340 out.println(String.format(" --%-16s %s", CLIParserBuilder.CONFIG_DIR,
341 "Directory containing attribute authority configuration files"));
342 out.println(String.format(" --%-16s %s", CLIParserBuilder.PRINCIPAL,
343 "Principal name (user id) of the person whose attributes will be retrieved"));
344
345 out.println();
346
347 out.println("Optional Parameters:");
348 out.println(String.format(" --%-16s %s", CLIParserBuilder.HELP, "Print this message"));
349 out.println(String.format(" --%-16s %s", CLIParserBuilder.REQUESTER,
350 "SAML entity ID of the relying party requesting the attributes. For example, the SPs entity ID. "
351 + "If not provided, requester is treated as anonymous."));
352 out.println(String
353 .format(" --%-16s %s", CLIParserBuilder.AUTHN_METHOD, "Method used to authenticate the user"));
354 out.println(String.format(" --%-16s %s", CLIParserBuilder.SAML1,
355 "No-value parameter indicating the attribute "
356 + "authority should answer as if it received a SAML 1 request"));
357
358 out.println();
359 }
360
361
362
363
364
365
366
367 private static void errorAndExit(String errorMessage, Exception e) {
368 if (e == null) {
369 log.error(errorMessage);
370 } else {
371 log.error(errorMessage, e);
372 }
373
374 System.out.flush();
375 System.exit(1);
376 }
377
378
379
380
381 private static class CLIParserBuilder {
382
383
384 public static final String HELP = "help";
385
386 public static final String CONFIG_DIR = "configDir";
387
388 public static final String REQUESTER = "requester";
389
390 public static final String ISSUER = "issuer";
391
392 public static final String PRINCIPAL = "principal";
393
394 public static final String AUTHN_METHOD = "authnMethod";
395
396 public static final String SAML1 = "saml1";
397
398
399 public static CmdLineParser.Option HELP_ARG;
400
401 public static CmdLineParser.Option CONFIG_DIR_ARG;
402
403 public static CmdLineParser.Option REQUESTER_ARG;
404
405
406 public static CmdLineParser.Option ISSUER_ARG;
407
408 public static CmdLineParser.Option PRINCIPAL_ARG;
409
410 public static CmdLineParser.Option AUTHN_METHOD_ARG;
411
412 public static CmdLineParser.Option SAML1_ARG;
413
414
415
416
417
418
419 public static CmdLineParser buildParser() {
420 CmdLineParser parser = new CmdLineParser();
421
422 HELP_ARG = parser.addBooleanOption(HELP);
423 CONFIG_DIR_ARG = parser.addStringOption(CONFIG_DIR);
424 REQUESTER_ARG = parser.addStringOption(REQUESTER);
425 ISSUER_ARG = parser.addStringOption(ISSUER);
426 PRINCIPAL_ARG = parser.addStringOption(PRINCIPAL);
427 AUTHN_METHOD_ARG = parser.addStringOption(AUTHN_METHOD);
428 SAML1_ARG = parser.addBooleanOption(SAML1);
429
430 return parser;
431 }
432 }
433 }