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.catalina.realm;
019:
020: import java.security.Principal;
021: import java.util.ArrayList;
022: import java.util.Iterator;
023: import java.util.List;
024:
025: import javax.naming.Context;
026:
027: import org.apache.catalina.Group;
028: import org.apache.catalina.LifecycleException;
029: import org.apache.catalina.Role;
030: import org.apache.catalina.ServerFactory;
031: import org.apache.catalina.User;
032: import org.apache.catalina.UserDatabase;
033: import org.apache.catalina.core.StandardServer;
034: import org.apache.catalina.util.StringManager;
035:
036: /**
037: * <p>Implementation of {@link org.apache.catalina.Realm} that is based on an implementation of
038: * {@link UserDatabase} made available through the global JNDI resources
039: * configured for this instance of Catalina. Set the <code>resourceName</code>
040: * parameter to the global JNDI resources name for the configured instance
041: * of <code>UserDatabase</code> that we should consult.</p>
042: *
043: * @author Craig R. McClanahan
044: * @version $Revision: 543691 $ $Date: 2007-06-02 03:37:08 +0200 (sam., 02 juin 2007) $
045: * @since 4.1
046: */
047:
048: public class UserDatabaseRealm extends RealmBase {
049:
050: // ----------------------------------------------------- Instance Variables
051:
052: /**
053: * The <code>UserDatabase</code> we will use to authenticate users
054: * and identify associated roles.
055: */
056: protected UserDatabase database = null;
057:
058: /**
059: * Descriptive information about this Realm implementation.
060: */
061: protected final String info = "org.apache.catalina.realm.UserDatabaseRealm/1.0";
062:
063: /**
064: * Descriptive information about this Realm implementation.
065: */
066: protected static final String name = "UserDatabaseRealm";
067:
068: /**
069: * The global JNDI name of the <code>UserDatabase</code> resource
070: * we will be utilizing.
071: */
072: protected String resourceName = "UserDatabase";
073:
074: /**
075: * The string manager for this package.
076: */
077: private static StringManager sm = StringManager
078: .getManager(Constants.Package);
079:
080: // ------------------------------------------------------------- Properties
081:
082: /**
083: * Return descriptive information about this Realm implementation and
084: * the corresponding version number, in the format
085: * <code><description>/<version></code>.
086: */
087: public String getInfo() {
088:
089: return info;
090:
091: }
092:
093: /**
094: * Return the global JNDI name of the <code>UserDatabase</code> resource
095: * we will be using.
096: */
097: public String getResourceName() {
098:
099: return resourceName;
100:
101: }
102:
103: /**
104: * Set the global JNDI name of the <code>UserDatabase</code> resource
105: * we will be using.
106: *
107: * @param resourceName The new global JNDI name
108: */
109: public void setResourceName(String resourceName) {
110:
111: this .resourceName = resourceName;
112:
113: }
114:
115: // --------------------------------------------------------- Public Methods
116:
117: /**
118: * Return <code>true</code> if the specified Principal has the specified
119: * security role, within the context of this Realm; otherwise return
120: * <code>false</code>. This implementation returns <code>true</code>
121: * if the <code>User</code> has the role, or if any <code>Group</code>
122: * that the <code>User</code> is a member of has the role.
123: *
124: * @param principal Principal for whom the role is to be checked
125: * @param role Security role to be checked
126: */
127: public boolean hasRole(Principal principal, String role) {
128: if (principal instanceof GenericPrincipal) {
129: GenericPrincipal gp = (GenericPrincipal) principal;
130: if (gp.getUserPrincipal() instanceof User) {
131: principal = gp.getUserPrincipal();
132: }
133: }
134: if (!(principal instanceof User)) {
135: //Play nice with SSO and mixed Realms
136: return super .hasRole(principal, role);
137: }
138: if ("*".equals(role)) {
139: return true;
140: } else if (role == null) {
141: return false;
142: }
143: User user = (User) principal;
144: Role dbrole = database.findRole(role);
145: if (dbrole == null) {
146: return false;
147: }
148: if (user.isInRole(dbrole)) {
149: return true;
150: }
151: Iterator groups = user.getGroups();
152: while (groups.hasNext()) {
153: Group group = (Group) groups.next();
154: if (group.isInRole(dbrole)) {
155: return true;
156: }
157: }
158: return false;
159: }
160:
161: // ------------------------------------------------------ Protected Methods
162:
163: /**
164: * Return a short name for this Realm implementation.
165: */
166: protected String getName() {
167:
168: return (name);
169:
170: }
171:
172: /**
173: * Return the password associated with the given principal's user name.
174: */
175: protected String getPassword(String username) {
176:
177: User user = database.findUser(username);
178:
179: if (user == null) {
180: return null;
181: }
182:
183: return (user.getPassword());
184:
185: }
186:
187: /**
188: * Return the Principal associated with the given user name.
189: */
190: protected Principal getPrincipal(String username) {
191:
192: User user = database.findUser(username);
193: if (user == null) {
194: return null;
195: }
196:
197: List<String> roles = new ArrayList<String>();
198: Iterator uroles = user.getRoles();
199: while (uroles.hasNext()) {
200: Role role = (Role) uroles.next();
201: roles.add(role.getName());
202: }
203: Iterator groups = user.getGroups();
204: while (groups.hasNext()) {
205: Group group = (Group) groups.next();
206: uroles = group.getRoles();
207: while (uroles.hasNext()) {
208: Role role = (Role) uroles.next();
209: roles.add(role.getName());
210: }
211: }
212: return new GenericPrincipal(this , username, user.getPassword(),
213: roles, user);
214: }
215:
216: // ------------------------------------------------------ Lifecycle Methods
217:
218: /**
219: * Prepare for active use of the public methods of this Component.
220: *
221: * @exception LifecycleException if this component detects a fatal error
222: * that prevents it from being started
223: */
224: public synchronized void start() throws LifecycleException {
225:
226: // Perform normal superclass initialization
227: super .start();
228:
229: try {
230: StandardServer server = (StandardServer) ServerFactory
231: .getServer();
232: Context context = server.getGlobalNamingContext();
233: database = (UserDatabase) context.lookup(resourceName);
234: } catch (Throwable e) {
235: containerLog.error(sm.getString("userDatabaseRealm.lookup",
236: resourceName), e);
237: database = null;
238: }
239: if (database == null) {
240: throw new LifecycleException(sm.getString(
241: "userDatabaseRealm.noDatabase", resourceName));
242: }
243:
244: }
245:
246: /**
247: * Gracefully shut down active use of the public methods of this Component.
248: *
249: * @exception LifecycleException if this component detects a fatal error
250: * that needs to be reported
251: */
252: public synchronized void stop() throws LifecycleException {
253:
254: // Perform normal superclass finalization
255: super .stop();
256:
257: // Release reference to our user database
258: database = null;
259:
260: }
261:
262: }
|