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