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    
018    package org.apache.geronimo.security.deployment;
019    
020    import java.security.Principal;
021    import java.util.HashMap;
022    import java.util.HashSet;
023    import java.util.Map;
024    import java.util.Set;
025    import java.util.Collections;
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.NamespaceDrivenBuilder;
032    import org.apache.geronimo.deployment.service.SingleGBeanBuilder;
033    import org.apache.geronimo.deployment.xbeans.PatternType;
034    import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
035    import org.apache.geronimo.gbean.AbstractName;
036    import org.apache.geronimo.gbean.GBeanData;
037    import org.apache.geronimo.gbean.GBeanInfo;
038    import org.apache.geronimo.gbean.GBeanInfoBuilder;
039    import org.apache.geronimo.gbean.AbstractNameQuery;
040    import org.apache.geronimo.gbean.GBeanLifecycle;
041    import org.apache.geronimo.j2ee.deployment.EARContext;
042    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
043    import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
044    import org.apache.geronimo.kernel.Naming;
045    import org.apache.geronimo.kernel.repository.Environment;
046    import org.apache.geronimo.security.deploy.LoginDomainPrincipalInfo;
047    import org.apache.geronimo.security.deploy.PrincipalInfo;
048    import org.apache.geronimo.security.deploy.RealmPrincipalInfo;
049    import org.apache.geronimo.security.deploy.Role;
050    import org.apache.geronimo.security.deploy.Security;
051    import org.apache.geronimo.security.deploy.SubjectInfo;
052    import org.apache.geronimo.security.jacc.ApplicationPolicyConfigurationManager;
053    import org.apache.geronimo.security.jacc.mappingprovider.ApplicationPrincipalRoleConfigurationManager;
054    import org.apache.geronimo.security.util.ConfigurationUtil;
055    import org.apache.geronimo.security.credentialstore.CredentialStore;
056    import org.apache.geronimo.xbeans.geronimo.security.GerLoginDomainPrincipalType;
057    import org.apache.geronimo.xbeans.geronimo.security.GerPrincipalType;
058    import org.apache.geronimo.xbeans.geronimo.security.GerRealmPrincipalType;
059    import org.apache.geronimo.xbeans.geronimo.security.GerRoleMappingsType;
060    import org.apache.geronimo.xbeans.geronimo.security.GerRoleType;
061    import org.apache.geronimo.xbeans.geronimo.security.GerSecurityDocument;
062    import org.apache.geronimo.xbeans.geronimo.security.GerSecurityType;
063    import org.apache.geronimo.xbeans.geronimo.security.GerSubjectInfoType;
064    import org.apache.xmlbeans.QNameSet;
065    import org.apache.xmlbeans.XmlException;
066    import org.apache.xmlbeans.XmlObject;
067    
068    /**
069     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
070     */
071    public class GeronimoSecurityBuilderImpl implements NamespaceDrivenBuilder, GBeanLifecycle {
072        private static final QName SECURITY_QNAME = GerSecurityDocument.type.getDocumentElementName();
073        private static final QNameSet SECURITY_QNAME_SET = QNameSet.singleton(SECURITY_QNAME);
074        private static final Map<String, String> NAMESPACE_UPDATES = new HashMap<String, String>();
075        static {
076            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/loginconfig", "http://geronimo.apache.org/xml/ns/loginconfig-2.0");
077            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/loginconfig-1.1", "http://geronimo.apache.org/xml/ns/loginconfig-2.0");
078            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/loginconfig-1.2", "http://geronimo.apache.org/xml/ns/loginconfig-2.0");
079            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/security", "http://geronimo.apache.org/xml/ns/security-1.2");
080            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/security-1.1", "http://geronimo.apache.org/xml/ns/security-2.0");
081            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/security-1.2", "http://geronimo.apache.org/xml/ns/security-2.0");
082        }
083    
084        private final AbstractNameQuery credentialStoreName;
085    
086        public GeronimoSecurityBuilderImpl(AbstractNameQuery credentialStoreName) {
087            this.credentialStoreName = credentialStoreName;
088        }
089    
090        public void doStart() {
091            XmlBeansUtil.registerNamespaceUpdates(NAMESPACE_UPDATES);
092        }
093    
094        public void doStop() {
095            XmlBeansUtil.unregisterNamespaceUpdates(NAMESPACE_UPDATES);
096        }
097    
098        public void doFail() {
099            doStop();
100        }
101    
102        public void buildEnvironment(XmlObject container, Environment environment) throws DeploymentException {
103        }
104    
105        public void build(XmlObject container, DeploymentContext applicationContext, DeploymentContext moduleContext) throws DeploymentException {
106            EARContext earContext = (EARContext) applicationContext;
107            XmlObject[] items = container.selectChildren(SECURITY_QNAME_SET);
108            if (items.length > 1) {
109                throw new DeploymentException("Unexpected count of security elements in geronimo plan " + items.length + " qnameset: " + SECURITY_QNAME_SET);
110            }
111            if (items.length == 1) {
112                GerSecurityType securityType;
113                try {
114                    securityType = (GerSecurityType) XmlBeansUtil.typedCopy(items[0], GerSecurityType.type);
115                } catch (XmlException e) {
116                    throw new DeploymentException("Could not validate security element", e);
117                }
118                Security security = buildSecurityConfig(securityType);
119                ClassLoader classLoader = applicationContext.getClassLoader();
120                SecurityConfiguration securityConfiguration = buildSecurityConfiguration(security, classLoader);
121                earContext.setSecurityConfiguration(securityConfiguration);
122                
123                Naming naming = earContext.getNaming();
124                GBeanData roleMapperData = configureRoleMapper(naming, earContext.getModuleName(), securityConfiguration);
125                try {
126                    earContext.addGBean(roleMapperData);
127                } catch (GBeanAlreadyExistsException e) {
128                    throw new DeploymentException("Role mapper gbean already present", e);
129                }
130                AbstractNameQuery credentialStoreName;
131                if (securityType.isSetCredentialStoreRef()) {
132                    PatternType credentialStoreType = securityType.getCredentialStoreRef();
133                    credentialStoreName = SingleGBeanBuilder.buildAbstractNameQuery(credentialStoreType, NameFactory.GERONIMO_SERVICE, Collections.singleton(CredentialStore.class.getName()));
134                } else {
135                    credentialStoreName = this.credentialStoreName;
136                }
137                GBeanData jaccBeanData = configureApplicationPolicyManager(naming, earContext.getModuleName(), earContext.getContextIDToPermissionsMap(), securityConfiguration, credentialStoreName);
138                jaccBeanData.setReferencePattern("PrincipalRoleMapper", roleMapperData.getAbstractName());
139                try {
140                    earContext.addGBean(jaccBeanData);
141                } catch (GBeanAlreadyExistsException e) {
142                    throw new DeploymentException("JACC manager gbean already present", e);
143                }
144                earContext.setJaccManagerName(jaccBeanData.getAbstractName());
145            }
146        }
147    
148        private static SecurityConfiguration buildSecurityConfiguration(Security security, ClassLoader classLoader) {
149            Map<String, SubjectInfo> roleDesignates = security.getRoleSubjectMappings();
150            Map<Principal, Set<String>> principalRoleMap = new HashMap<Principal, Set<String>>();
151            Map<String, Set<Principal>> roleToPrincipalMap = new HashMap<String, Set<Principal>>();
152            buildRolePrincipalMap(security, roleToPrincipalMap, classLoader);
153            invertMap(roleToPrincipalMap, principalRoleMap);
154            return new SecurityConfiguration(principalRoleMap, roleDesignates, security.getDefaultSubjectInfo(), security.getDefaultRole(), security.isDoAsCurrentCaller(), security.isUseContextHandler());
155        }
156    
157        private static Map invertMap(Map<String, Set<Principal>> roleToPrincipalMap, Map<Principal, Set<String>> principalRoleMapping) {
158            for (Map.Entry<String, Set<java.security.Principal>> entry : roleToPrincipalMap.entrySet()) {
159                String role = entry.getKey();
160                Set<Principal> principals = entry.getValue();
161                for (Principal principal : principals) {
162    
163                    Set<String> roleSet = principalRoleMapping.get(principal);
164                    if (roleSet == null) {
165                        roleSet = new HashSet<String>();
166                        principalRoleMapping.put(principal, roleSet);
167                    }
168                    roleSet.add(role);
169                }
170            }
171            return principalRoleMapping;
172        }
173    
174        /**
175         * non-interface, used in some jetty/tomcat tests
176         *
177         * @param security Security object holding security info as it is extracted
178         * @param roleToPrincipalMap role to set of Principals mapping
179         * @param classLoader application classloader in case we need to load some principal classes.
180         */
181        public static void buildRolePrincipalMap(Security security, Map<String, Set<Principal>> roleToPrincipalMap, ClassLoader classLoader) {
182    
183            for (Object o : security.getRoleMappings().values()) {
184                Role role = (Role) o;
185    
186                String roleName = role.getRoleName();
187                Set<Principal> principalSet = new HashSet<Principal>();
188    
189                for (Object o1 : role.getRealmPrincipals()) {
190                    RealmPrincipalInfo realmPrincipal = (RealmPrincipalInfo) o1;
191                    Principal principal = ConfigurationUtil.generateRealmPrincipal(realmPrincipal.getRealm(), realmPrincipal.getDomain(), realmPrincipal, classLoader);
192    
193                    principalSet.add(principal);
194                }
195    
196                for (Object o2 : role.getLoginDomainPrincipals()) {
197                    LoginDomainPrincipalInfo domainPrincipal = (LoginDomainPrincipalInfo) o2;
198                    Principal principal = ConfigurationUtil.generateDomainPrincipal(domainPrincipal.getDomain(), domainPrincipal, classLoader);
199    
200                    principalSet.add(principal);
201                }
202    
203                for (Object o3 : role.getPrincipals()) {
204                    PrincipalInfo plainPrincipalInfo = (PrincipalInfo) o3;
205                    Principal principal = ConfigurationUtil.generatePrincipal(plainPrincipalInfo, classLoader);
206    
207                    principalSet.add(principal);
208                }
209    
210                Set<Principal> roleMapping = roleToPrincipalMap.get(roleName);
211                if (roleMapping == null) {
212                    roleMapping = new HashSet<Principal>();
213                    roleToPrincipalMap.put(roleName, roleMapping);
214                }
215                roleMapping.addAll(principalSet);
216    
217            }
218        }
219    
220        private Security buildSecurityConfig(GerSecurityType securityType) {
221            Security security;
222    
223            if (securityType == null) {
224                return null;
225            }
226            security = new Security();
227    
228            security.setDoAsCurrentCaller(securityType.getDoasCurrentCaller());
229            security.setUseContextHandler(securityType.getUseContextHandler());
230            if (securityType.isSetDefaultRole()) {
231                security.setDefaultRole(securityType.getDefaultRole().trim());
232            }
233    
234            if (securityType.isSetRoleMappings()) {
235                GerRoleMappingsType roleMappingsType = securityType.getRoleMappings();
236                for (int i = 0; i < roleMappingsType.sizeOfRoleArray(); i++) {
237                    GerRoleType roleType = roleMappingsType.getRoleArray(i);
238                    Role role = new Role();
239    
240                    String roleName = roleType.getRoleName().trim();
241                    role.setRoleName(roleName);
242    
243                    if (roleType.isSetRunAsSubject()) {
244                        SubjectInfo subjectInfo = buildSubjectInfo(roleType.getRunAsSubject());
245                        security.getRoleSubjectMappings().put(roleName, subjectInfo);
246                    }
247    
248                    for (int j = 0; j < roleType.sizeOfRealmPrincipalArray(); j++) {
249                        role.getRealmPrincipals().add(GeronimoSecurityBuilderImpl.buildRealmPrincipal(roleType.getRealmPrincipalArray(j)));
250                    }
251    
252                    for (int j = 0; j < roleType.sizeOfLoginDomainPrincipalArray(); j++) {
253                        role.getLoginDomainPrincipals().add(GeronimoSecurityBuilderImpl.buildDomainPrincipal(roleType.getLoginDomainPrincipalArray(j)));
254                    }
255    
256                    for (int j = 0; j < roleType.sizeOfPrincipalArray(); j++) {
257                        role.getPrincipals().add(buildPrincipal(roleType.getPrincipalArray(j)));
258                    }
259    
260                    security.getRoleMappings().put(roleName, role);
261                }
262            }
263    
264            security.setDefaultSubjectInfo(buildSubjectInfo(securityType.getDefaultSubject()));
265    
266            return security;
267        }
268    
269        private SubjectInfo buildSubjectInfo(GerSubjectInfoType defaultSubject) {
270            if (defaultSubject == null) {
271                return null;
272            }
273            String realmName = defaultSubject.getRealm().trim();
274            String id = defaultSubject.getId().trim();
275            return new SubjectInfo(realmName, id);
276        }
277    
278        private static RealmPrincipalInfo buildRealmPrincipal(GerRealmPrincipalType realmPrincipalType) {
279            return new RealmPrincipalInfo(realmPrincipalType.getRealmName().trim(), realmPrincipalType.getDomainName().trim(), realmPrincipalType.getClass1().trim(), realmPrincipalType.getName().trim());
280        }
281    
282        private static LoginDomainPrincipalInfo buildDomainPrincipal(GerLoginDomainPrincipalType domainPrincipalType) {
283            return new LoginDomainPrincipalInfo(domainPrincipalType.getDomainName().trim(), domainPrincipalType.getClass1().trim(), domainPrincipalType.getName().trim());
284        }
285    
286        //used from TSSConfigEditor
287        public PrincipalInfo buildPrincipal(XmlObject xmlObject) {
288            GerPrincipalType principalType = (GerPrincipalType) xmlObject;
289            return new PrincipalInfo(principalType.getClass1().trim(), principalType.getName().trim());
290        }
291    
292        protected GBeanData configureRoleMapper(Naming naming, AbstractName moduleName, SecurityConfiguration securityConfiguration) {
293            AbstractName roleMapperName = naming.createChildName(moduleName, "RoleMapper", "RoleMapper");
294            GBeanData roleMapperData = new GBeanData(roleMapperName, ApplicationPrincipalRoleConfigurationManager.GBEAN_INFO);
295            roleMapperData.setAttribute("principalRoleMap", securityConfiguration.getPrincipalRoleMap());
296            return roleMapperData;
297        }
298    
299        protected GBeanData configureApplicationPolicyManager(Naming naming, AbstractName moduleName, Map contextIDToPermissionsMap, SecurityConfiguration securityConfiguration, AbstractNameQuery credentialStoreName) {
300            AbstractName jaccBeanName = naming.createChildName(moduleName, NameFactory.JACC_MANAGER, NameFactory.JACC_MANAGER);
301            GBeanData jaccBeanData = new GBeanData(jaccBeanName, ApplicationPolicyConfigurationManager.GBEAN_INFO);
302            jaccBeanData.setAttribute("contextIdToPermissionsMap", contextIDToPermissionsMap);
303            Map<String, SubjectInfo> roleDesignates = securityConfiguration.getRoleDesignates();
304            jaccBeanData.setAttribute("roleDesignates", roleDesignates);
305            jaccBeanData.setAttribute("defaultSubjectInfo", securityConfiguration.getDefaultSubjectInfo());
306            if ((roleDesignates != null && !roleDesignates.isEmpty()) || securityConfiguration.getDefaultSubjectInfo() != null) {
307                jaccBeanData.setReferencePattern("CredentialStore", credentialStoreName);
308            }
309            return jaccBeanData;
310        }
311    
312        public QNameSet getSpecQNameSet() {
313            return QNameSet.EMPTY;
314        }
315    
316        public QNameSet getPlanQNameSet() {
317            return SECURITY_QNAME_SET;
318        }
319    
320        public static final GBeanInfo GBEAN_INFO;
321    
322        static {
323            GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(GeronimoSecurityBuilderImpl.class, NameFactory.MODULE_BUILDER);
324    
325            infoFactory.addAttribute("credentialStoreName", AbstractNameQuery.class, true, true);
326            infoFactory.setConstructor(new String[] {"credentialStoreName"});
327    
328            GBEAN_INFO = infoFactory.getBeanInfo();
329        }
330    
331        public static GBeanInfo getGBeanInfo() {
332            return GBEAN_INFO;
333        }
334    
335    }