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