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: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 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 }