001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.geronimo.security.deployment;
018
019 import java.util.ArrayList;
020 import java.util.Collection;
021 import java.util.HashMap;
022 import java.util.HashSet;
023 import java.util.List;
024 import java.util.Map;
025 import java.util.Set;
026
027 import javax.xml.namespace.QName;
028
029 import org.apache.geronimo.common.DeploymentException;
030 import org.apache.geronimo.deployment.DeploymentContext;
031 import org.apache.geronimo.deployment.service.SingleGBeanBuilder;
032 import org.apache.geronimo.deployment.service.XmlAttributeBuilder;
033 import org.apache.geronimo.deployment.service.XmlReferenceBuilder;
034 import org.apache.geronimo.deployment.xbeans.PatternType;
035 import org.apache.geronimo.deployment.xbeans.XmlAttributeType;
036 import org.apache.geronimo.gbean.AbstractName;
037 import org.apache.geronimo.gbean.AbstractNameQuery;
038 import org.apache.geronimo.gbean.GBeanData;
039 import org.apache.geronimo.gbean.GBeanInfo;
040 import org.apache.geronimo.gbean.GBeanInfoBuilder;
041 import org.apache.geronimo.gbean.GReferenceInfo;
042 import org.apache.geronimo.gbean.ReferenceMap;
043 import org.apache.geronimo.gbean.ReferencePatterns;
044 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
045 import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
046 import org.apache.geronimo.kernel.Kernel;
047 import org.apache.geronimo.kernel.Naming;
048 import org.apache.geronimo.security.jaas.JaasLoginModuleUse;
049 import org.apache.geronimo.security.jaas.LoginModuleControlFlag;
050 import org.apache.geronimo.security.jaas.LoginModuleControlFlagEditor;
051 import org.apache.geronimo.security.jaas.LoginModuleGBean;
052 import org.apache.geronimo.xbeans.geronimo.loginconfig.GerAbstractLoginModuleType;
053 import org.apache.geronimo.xbeans.geronimo.loginconfig.GerLoginConfigDocument;
054 import org.apache.geronimo.xbeans.geronimo.loginconfig.GerLoginConfigType;
055 import org.apache.geronimo.xbeans.geronimo.loginconfig.GerLoginModuleRefType;
056 import org.apache.geronimo.xbeans.geronimo.loginconfig.GerLoginModuleType;
057 import org.apache.geronimo.xbeans.geronimo.loginconfig.GerOptionType;
058 import org.apache.xmlbeans.XmlCursor;
059 import org.apache.xmlbeans.XmlObject;
060 import org.apache.xmlbeans.XmlOptions;
061
062
063 /**
064 * @version $Rev: 555771 $ $Date: 2007-07-12 17:22:48 -0400 (Thu, 12 Jul 2007) $
065 */
066 public class LoginConfigBuilder implements XmlReferenceBuilder {
067 public static final String LOGIN_CONFIG_NAMESPACE = GerLoginConfigDocument.type.getDocumentElementName().getNamespaceURI();
068 private static final QName LOGIN_MODULE_QNAME = new QName(LOGIN_CONFIG_NAMESPACE, "login-module");
069 private static final QName SERVER_SIDE_QNAME = new QName(null, "server-side");
070
071 private final Naming naming;
072 private final Map xmlAttributeBuilderMap;
073
074 public LoginConfigBuilder(Kernel kernel, Collection xmlAttributeBuilderMap) {
075 this(kernel.getNaming(), xmlAttributeBuilderMap);
076 }
077
078 public LoginConfigBuilder(Naming naming, Collection xmlAttributeBuilders) {
079 this.naming = naming;
080 if (xmlAttributeBuilders != null) {
081 ReferenceMap.Key key = new ReferenceMap.Key() {
082
083 public Object getKey(Object object) {
084 return ((XmlAttributeBuilder) object).getNamespace();
085 }
086 };
087 xmlAttributeBuilderMap = new ReferenceMap(xmlAttributeBuilders, new HashMap(), key);
088 } else {
089 xmlAttributeBuilderMap = new HashMap();
090 }
091 }
092
093 public String getNamespace() {
094 return LOGIN_CONFIG_NAMESPACE;
095 }
096
097 public ReferencePatterns getReferences(XmlObject xmlObject, DeploymentContext context, AbstractName parentName, ClassLoader classLoader) throws DeploymentException {
098 List<GBeanData> uses = new ArrayList<GBeanData>();
099 GerLoginConfigType loginConfig = (GerLoginConfigType) xmlObject.copy().changeType(GerLoginConfigType.type);
100 XmlCursor xmlCursor = loginConfig.newCursor();
101 xmlCursor.push();
102 try {
103 //munge xml
104 if (xmlCursor.toChild(LOGIN_MODULE_QNAME)) {
105 do {
106 xmlCursor.removeAttribute(SERVER_SIDE_QNAME);
107 } while (xmlCursor.toNextSibling(LOGIN_MODULE_QNAME));
108 }
109 xmlCursor.pop();
110 //validate
111 XmlOptions xmlOptions = new XmlOptions();
112 xmlOptions.setLoadLineNumbers();
113 Collection errors = new ArrayList();
114 xmlOptions.setErrorListener(errors);
115 if (!loginConfig.validate(xmlOptions)) {
116 throw new DeploymentException("Invalid login configuration:\n" + errors + "\nDescriptor: " + loginConfig.toString());
117 }
118 //find the login modules
119 Set<String> loginModuleNames = new HashSet<String>();
120 boolean atStart = true;
121 while ((atStart && xmlCursor.toFirstChild()) || (!atStart && xmlCursor.toNextSibling())) {
122 atStart = false;
123 XmlObject child = xmlCursor.getObject();
124 GerAbstractLoginModuleType abstractLoginModule = (GerAbstractLoginModuleType) child;
125 String controlFlag = abstractLoginModule.getControlFlag().toString();
126 boolean wrapPrincipals = (abstractLoginModule.isSetWrapPrincipals() && abstractLoginModule.getWrapPrincipals());
127 ReferencePatterns loginModuleReferencePatterns;
128 String name;
129 if (abstractLoginModule instanceof GerLoginModuleRefType) {
130 GerLoginModuleRefType loginModuleRef = (GerLoginModuleRefType) abstractLoginModule;
131 PatternType patternType = loginModuleRef.getPattern();
132 AbstractNameQuery loginModuleNameQuery = SingleGBeanBuilder.buildAbstractNameQuery(patternType, USE_REFERENCE_INFO);
133 loginModuleReferencePatterns = new ReferencePatterns(loginModuleNameQuery);
134 name = (String) loginModuleNameQuery.getName().get("name");
135 if (name == null) {
136 throw new DeploymentException("You must specify the name of the login module in the login module ref " + patternType);
137 }
138 //TODO configid reinstate this check for duplicate domain names
139 // try
140 // {
141 // String loginDomain = (String) context.getAttribute(loginModuleName, "loginDomainName");
142 // if (!loginModuleNames.add(loginDomain))
143 // {
144 // throw new DeploymentException("Security realm contains two login domains called '" + loginDomain + "'");
145 // }
146 // }
147 // catch (DeploymentException e)
148 // {
149 // throw e;
150 // }
151 // catch (Exception e)
152 // {
153 // throw new DeploymentException("Unable to create reference to login module " + name, e);
154 // }
155 } else if (abstractLoginModule instanceof GerLoginModuleType) {
156 //create the LoginModuleGBean also
157 AbstractName loginModuleName;
158
159 GerLoginModuleType loginModule = (GerLoginModuleType) abstractLoginModule;
160 name = trim(loginModule.getLoginDomainName());
161 if (!loginModuleNames.add(name)) {
162 throw new DeploymentException("Security realm contains two login domains called '" + name + "'");
163 }
164 String className = trim(loginModule.getLoginModuleClass());
165 Map<String, Object> options = new HashMap<String, Object>();
166 GerOptionType[] optionArray = loginModule.getOptionArray();
167 for (GerOptionType gerOptionType : optionArray) {
168 String key = gerOptionType.getName();
169 String value = trim(gerOptionType.getStringValue());
170 options.put(key, value);
171 }
172 XmlAttributeType[] xmlOptionArray = loginModule.getXmlOptionArray();
173 if (xmlOptionArray != null) {
174 for (XmlAttributeType xmlOptionType : xmlOptionArray) {
175 String key = xmlOptionType.getName().trim();
176 XmlObject[] anys = xmlOptionType.selectChildren(XmlAttributeType.type.qnameSetForWildcardElements());
177 if (anys.length != 1) {
178 throw new DeploymentException("Unexpected count of xs:any elements in xml-attribute " + anys.length + " qnameset: " + XmlAttributeType.type.qnameSetForWildcardElements());
179 }
180 String namespace = xmlObject.getDomNode().getNamespaceURI();
181 XmlAttributeBuilder builder = (XmlAttributeBuilder) xmlAttributeBuilderMap.get(namespace);
182 if (builder == null) {
183 throw new DeploymentException("No attribute builder deployed for namespace: " + namespace);
184 }
185 Object value = builder.getValue(xmlObject, null, classLoader);
186 options.put(key, value);
187 }
188 }
189 loginModuleName = naming.createChildName(parentName, name, NameFactory.LOGIN_MODULE);
190 loginModuleReferencePatterns = new ReferencePatterns(loginModuleName);
191 GBeanData loginModuleGBeanData = new GBeanData(loginModuleName, LoginModuleGBean.GBEAN_INFO);
192 loginModuleGBeanData.setAttribute("loginDomainName", name);
193 loginModuleGBeanData.setAttribute("loginModuleClass", className);
194 loginModuleGBeanData.setAttribute("options", options);
195 loginModuleGBeanData.setAttribute("wrapPrincipals", wrapPrincipals);
196
197 context.addGBean(loginModuleGBeanData);
198 } else {
199 throw new DeploymentException("Unknown abstract login module type: " + abstractLoginModule.getClass());
200 }
201 AbstractName thisName;
202 thisName = naming.createChildName(parentName, name, "LoginModuleUse");
203 GBeanData loginModuleUseGBeanData = new GBeanData(thisName, JaasLoginModuleUse.GBEAN_INFO);
204 loginModuleUseGBeanData.setAttribute("controlFlag", getControlFlag(controlFlag));
205 loginModuleUseGBeanData.setReferencePatterns("LoginModule", loginModuleReferencePatterns);
206 uses.add(loginModuleUseGBeanData);
207 }
208 for (int i = uses.size() - 1; i >= 0; i--) {
209 GBeanData data = uses.get(i);
210 if (i > 0) {
211 uses.get(i - 1).setReferencePattern("Next", data.getAbstractName());
212 }
213 context.addGBean(data);
214 }
215 } catch (GBeanAlreadyExistsException e) {
216 throw new DeploymentException(e);
217 } finally {
218 xmlCursor.dispose();
219 }
220 return uses.size() == 0 ? null : new ReferencePatterns(uses.get(0).getAbstractName());
221 }
222
223 private LoginModuleControlFlag getControlFlag(String controlFlag) {
224 LoginModuleControlFlagEditor editor = new LoginModuleControlFlagEditor();
225 editor.setAsText(controlFlag);
226 return (LoginModuleControlFlag) editor.getValue();
227 }
228
229 private String trim(String string) {
230 return string == null ? null : string.trim();
231 }
232
233 public static final GBeanInfo GBEAN_INFO;
234
235 private static final GReferenceInfo USE_REFERENCE_INFO;
236
237 static {
238 GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(LoginConfigBuilder.class, "XmlReferenceBuilder");
239 infoBuilder.addAttribute("kernel", Kernel.class, false, false);
240 infoBuilder.addReference("xmlAttributeBuilders", XmlAttributeBuilder.class, "XmlAttributeBuilder");
241 infoBuilder.setConstructor(new String[]{"kernel", "xmlAttributeBuilders"});
242 infoBuilder.addInterface(XmlReferenceBuilder.class);
243 GBEAN_INFO = infoBuilder.getBeanInfo();
244
245 Set<GReferenceInfo> referenceInfos = JaasLoginModuleUse.GBEAN_INFO.getReferences();
246 GReferenceInfo found = null;
247 for (GReferenceInfo testReferenceInfo : referenceInfos) {
248 String testRefName = testReferenceInfo.getName();
249 if (testRefName.equals("LoginModule")) {
250 found = testReferenceInfo;
251 break;
252 }
253 }
254 if (found == null) {
255 throw new RuntimeException("Someone changed the gbeaninfo on JaasLoginModuleUse");
256 }
257 USE_REFERENCE_INFO = found;
258
259 }
260
261 public static GBeanInfo getGBeanInfo() {
262 return GBEAN_INFO;
263 }
264 }