1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.geronimo.security.deployment; 18 19 import java.util.ArrayList; 20 import java.util.Collection; 21 import java.util.HashMap; 22 import java.util.HashSet; 23 import java.util.List; 24 import java.util.Map; 25 import java.util.Set; 26 27 import javax.xml.namespace.QName; 28 29 import org.apache.geronimo.common.DeploymentException; 30 import org.apache.geronimo.deployment.DeploymentContext; 31 import org.apache.geronimo.deployment.service.SingleGBeanBuilder; 32 import org.apache.geronimo.deployment.service.XmlAttributeBuilder; 33 import org.apache.geronimo.deployment.service.XmlReferenceBuilder; 34 import org.apache.geronimo.deployment.xbeans.PatternType; 35 import org.apache.geronimo.deployment.xbeans.XmlAttributeType; 36 import org.apache.geronimo.gbean.AbstractName; 37 import org.apache.geronimo.gbean.AbstractNameQuery; 38 import org.apache.geronimo.gbean.GBeanData; 39 import org.apache.geronimo.gbean.GBeanInfo; 40 import org.apache.geronimo.gbean.GBeanInfoBuilder; 41 import org.apache.geronimo.gbean.GReferenceInfo; 42 import org.apache.geronimo.gbean.ReferenceMap; 43 import org.apache.geronimo.gbean.ReferencePatterns; 44 import org.apache.geronimo.kernel.GBeanAlreadyExistsException; 45 import org.apache.geronimo.kernel.Kernel; 46 import org.apache.geronimo.kernel.Naming; 47 import org.apache.geronimo.security.SecurityNames; 48 import org.apache.geronimo.security.jaas.JaasLoginModuleUse; 49 import org.apache.geronimo.security.jaas.LoginModuleControlFlag; 50 import org.apache.geronimo.security.jaas.LoginModuleControlFlagEditor; 51 import org.apache.geronimo.security.jaas.LoginModuleGBean; 52 import org.apache.geronimo.xbeans.geronimo.loginconfig.GerAbstractLoginModuleType; 53 import org.apache.geronimo.xbeans.geronimo.loginconfig.GerLoginConfigDocument; 54 import org.apache.geronimo.xbeans.geronimo.loginconfig.GerLoginConfigType; 55 import org.apache.geronimo.xbeans.geronimo.loginconfig.GerLoginModuleRefType; 56 import org.apache.geronimo.xbeans.geronimo.loginconfig.GerLoginModuleType; 57 import org.apache.geronimo.xbeans.geronimo.loginconfig.GerOptionType; 58 import org.apache.xmlbeans.XmlCursor; 59 import org.apache.xmlbeans.XmlObject; 60 import org.apache.xmlbeans.XmlOptions; 61 62 63 /** 64 * @version $Rev: 698441 $ $Date: 2008-09-24 00:10:08 -0700 (Wed, 24 Sep 2008) $ 65 */ 66 public class LoginConfigBuilder implements XmlReferenceBuilder { 67 public static final String LOGIN_CONFIG_NAMESPACE = GerLoginConfigDocument.type.getDocumentElementName().getNamespaceURI(); 68 private static final QName LOGIN_MODULE_QNAME = new QName(LOGIN_CONFIG_NAMESPACE, "login-module"); 69 private static final QName SERVER_SIDE_QNAME = new QName(null, "server-side"); 70 71 private final Naming naming; 72 private final Map xmlAttributeBuilderMap; 73 74 public LoginConfigBuilder(Kernel kernel, Collection xmlAttributeBuilderMap) { 75 this(kernel.getNaming(), xmlAttributeBuilderMap); 76 } 77 78 public LoginConfigBuilder(Naming naming, Collection xmlAttributeBuilders) { 79 this.naming = naming; 80 if (xmlAttributeBuilders != null) { 81 ReferenceMap.Key key = new ReferenceMap.Key() { 82 83 public Object getKey(Object object) { 84 return ((XmlAttributeBuilder) object).getNamespace(); 85 } 86 }; 87 xmlAttributeBuilderMap = new ReferenceMap(xmlAttributeBuilders, new HashMap(), key); 88 } else { 89 xmlAttributeBuilderMap = new HashMap(); 90 } 91 } 92 93 public String getNamespace() { 94 return LOGIN_CONFIG_NAMESPACE; 95 } 96 97 public ReferencePatterns getReferences(XmlObject xmlObject, DeploymentContext context, AbstractName parentName, ClassLoader classLoader) throws DeploymentException { 98 List<GBeanData> uses = new ArrayList<GBeanData>(); 99 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, SecurityNames.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 }