001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999-2005 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: JonasSecurityServiceImpl.java 7499 2005-10-13 11:58:30Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas.security;
025:
026: import java.io.File;
027: import java.io.FileNotFoundException;
028: import java.io.FileReader;
029: import java.io.Reader;
030: import java.io.StringReader;
031: import java.security.NoSuchAlgorithmException;
032:
033: import javax.management.MBeanServer;
034: import javax.naming.Context;
035: import javax.naming.InitialContext;
036: import javax.naming.NamingException;
037:
038: import org.objectweb.jonas.common.Log;
039: import org.objectweb.jonas.jmx.JmxService;
040: import org.objectweb.jonas.jmx.JonasObjectName;
041: import org.objectweb.jonas.security.lib.wrapper.JResourceManagerWrapper;
042: import org.objectweb.jonas.security.realm.factory.JResource;
043: import org.objectweb.jonas.security.realm.factory.JResourceDS;
044: import org.objectweb.jonas.security.realm.factory.JResourceLDAP;
045: import org.objectweb.jonas.security.realm.factory.JResourceMemory;
046: import org.objectweb.jonas.security.realm.factory.JResourceRemote;
047: import org.objectweb.jonas.security.realm.factory.JResourceRemoteImpl;
048: import org.objectweb.jonas.security.realm.lib.HashHelper;
049: import org.objectweb.jonas.service.AbsServiceImpl;
050: import org.objectweb.jonas.service.ServiceException;
051: import org.objectweb.jonas.service.ServiceManager;
052:
053: import org.objectweb.util.monolog.api.BasicLevel;
054: import org.objectweb.util.monolog.api.Logger;
055:
056: /**
057: * Security Service implementation
058: * @author Jeff Mesnil,Philippe Coq, John Ellis, Joe Gittings for old security
059: * service
060: * @author Florent Benoit - JOnAS 3.x (Add JResources) - JOnAS 4.x (remove
061: * MethodGuard, RoleGuard no more used with JACC)
062: */
063:
064: public class JonasSecurityServiceImpl extends AbsServiceImpl implements
065: SecurityService, JonasSecurityServiceImplMBean {
066:
067: /**
068: * Logger which is used
069: */
070: private static Logger logger = null;
071:
072: /**
073: * Security service configuration properties
074: */
075: public static final String CLASS = "jonas.service.security.class";
076:
077: /**
078: * Name of resource
079: */
080: public static final String REMOTE_RESOUCE = "_remoteres";
081:
082: /**
083: * Relative path of the realm configuration file
084: */
085: protected static final String CONFIG_FILE = "conf" + File.separator
086: + "jonas-realm.xml";
087:
088: /**
089: * Bind the resources into JNDI ?
090: */
091: protected static final String BIND_RESOURCES_INTO_JNDI = "jonas.service.security.realm.jndi.registration";
092:
093: /**
094: * Reference to a MBean server.
095: */
096: private MBeanServer mbeanServer = null;
097:
098: /**
099: * JResources list
100: */
101: private JResources jResources;
102:
103: /**
104: * Initial Context for Naming
105: */
106: private Context ictx = null;
107:
108: /**
109: * Bind resource in JNDI
110: */
111: private boolean bindResourcesIntoJndi = false;
112:
113: /**
114: * Init the Service. Configuration information is passed thru a Context
115: * object.
116: * @param ctx the configuration of the Security service.
117: * @throws ServiceException if the initialization failed.
118: */
119: public void doInit(Context ctx) throws ServiceException {
120: if (logger == null) {
121: logger = Log.getLogger(Log.JONAS_SECURITY_PREFIX);
122: }
123:
124: // JResources
125: jResources = new JResources(this );
126:
127: // Get the initial Context
128: try {
129: ictx = new InitialContext();
130: } catch (NamingException e) {
131: logger
132: .log(
133: BasicLevel.ERROR,
134: "Cannot create initial context during the mail service initializing",
135: e);
136: throw new ServiceException(
137: "Cannot create initial context during the mail service initializing",
138: e);
139: }
140:
141: // Register the security remote object (name is based on JOnAS server
142: // name).
143: JResourceRemote jResourceRemote = null;
144: try {
145: jResourceRemote = new JResourceRemoteImpl();
146: ictx.rebind(getJonasServerName() + REMOTE_RESOUCE,
147: jResourceRemote);
148: } catch (Exception e) {
149: logger.log(BasicLevel.ERROR,
150: "Cannot bind remote resource for security access",
151: e);
152: throw new ServiceException(
153: "Cannot bind remote resource for security access",
154: e);
155: }
156:
157: // Get the JMX Server via JMX Service
158: try {
159: mbeanServer = ((JmxService) ServiceManager.getInstance()
160: .getJmxService()).getJmxServer();
161: } catch (Exception e) {
162: // the JMX service may not be started
163: mbeanServer = null;
164: }
165:
166: try {
167: String s = (String) ctx.lookup(BIND_RESOURCES_INTO_JNDI);
168: bindResourcesIntoJndi = new Boolean(s).booleanValue();
169: } catch (Exception e) {
170: if (logger.isLoggable(BasicLevel.DEBUG)) {
171: logger
172: .log(
173: BasicLevel.DEBUG,
174: "Property '"
175: + BIND_RESOURCES_INTO_JNDI
176: + "' not available, set it to false by default",
177: e);
178: }
179: }
180:
181: if (logger.isLoggable(BasicLevel.DEBUG)) {
182: logger.log(BasicLevel.DEBUG,
183: "JonasSecurityService initialized");
184: }
185: }
186:
187: /**
188: * Remove the Resource (memory, ldap, datasource,...)
189: * @param resourceName name of the resource
190: * @throws Exception if the resource name does not exist
191: */
192: public void removeJResource(String resourceName) throws Exception {
193:
194: // remove the given resource of the list
195: JResource jResource = jResources.remove(resourceName);
196:
197: // remove the resource into the jndi
198: if (bindResourcesIntoJndi) {
199: try {
200: ictx.unbind(resourceName);
201: if (logger.isLoggable(BasicLevel.DEBUG)) {
202: logger.log(BasicLevel.DEBUG, "jResource "
203: + resourceName
204: + " remove from the registry.");
205: }
206: } catch (NamingException e) {
207: logger.log(BasicLevel.ERROR,
208: "Cannot unbind the resource '" + resourceName
209: + "' into JNDI", e);
210: }
211: }
212:
213: try {
214: // Remove mbeans of the resources
215: jResource.removeMBeans();
216:
217: // register security factory mbean
218: if (jResource instanceof JResourceMemory) {
219: mbeanServer.unregisterMBean(JonasObjectName
220: .securityMemoryFactory(resourceName));
221: } else if (jResource instanceof JResourceDS) {
222: mbeanServer.unregisterMBean(JonasObjectName
223: .securityDatasourceFactory(resourceName));
224: } else if (jResource instanceof JResourceLDAP) {
225: mbeanServer.unregisterMBean(JonasObjectName
226: .securityLdapFactory(resourceName));
227: }
228: } catch (ServiceException se) {
229: logger.log(BasicLevel.ERROR, "JMX service not available",
230: se);
231: } catch (Exception e) {
232: logger.log(BasicLevel.ERROR,
233: "Can not unregister the MBean for the resource "
234: + resourceName + " : " + e.getMessage());
235: throw new ServiceException(
236: "Can not unregister the MBean for the resource "
237: + resourceName + " : " + e.getMessage());
238: }
239:
240: }
241:
242: /**
243: * Start the Service Initialization of the service is already done.
244: * @throws ServiceException if the stop failed.
245: */
246: public void doStart() throws ServiceException {
247: try {
248: // register security service mbean
249: mbeanServer.registerMBean(this , JonasObjectName
250: .securityService());
251: } catch (ServiceException se) {
252: logger.log(BasicLevel.ERROR, "JMX service not available",
253: se);
254: } catch (Exception e) {
255: logger.log(BasicLevel.ERROR,
256: "SecurityService: Cannot start the Security service:\n"
257: + e);
258: throw new ServiceException(
259: "SecurityService: Cannot start the Security service",
260: e);
261: }
262:
263: createRealm();
264: }
265:
266: /**
267: * Stop the Service
268: */
269: public void doStop() {
270:
271: // Unregister MBean
272: try {
273: // Unregister Security Service MBean : SecurityServiceImplMBean
274: ((JmxService) ServiceManager.getInstance().getJmxService())
275: .getJmxServer().unregisterMBean(
276: JonasObjectName.securityService());
277: } catch (ServiceException se) {
278: logger.log(BasicLevel.ERROR, "JMX service not available",
279: se);
280: } catch (Exception e) {
281: logger.log(BasicLevel.ERROR,
282: "Cannot stop the security service:\n" + e);
283: throw new ServiceException(
284: "Cannot stop the security service", e);
285: }
286:
287: // Unregister the security remote object (name is based on JOnAS server
288: // name).
289: try {
290: ictx.unbind(getJonasServerName() + REMOTE_RESOUCE);
291: } catch (Exception e) {
292: logger
293: .log(
294: BasicLevel.ERROR,
295: "Cannot unbind remote resource for security access",
296: e);
297: throw new ServiceException(
298: "Cannot unbind remote resource for security access",
299: e);
300: }
301: }
302:
303: /**
304: * Return a resource by giving its name
305: * @param name the wanted Resource
306: * @return a JResouce
307: */
308: public JResource getJResource(String name) {
309: return jResources.getJResource(name);
310: }
311:
312: /**
313: * Parse the xml file and create all the JResource
314: * @throws ServiceException if a JResource can't be created
315: */
316: private void createRealm() throws ServiceException {
317:
318: // Execute the digester for the parsing of the jonas-realm.xml file.
319: File configFile = null;
320: Reader reader = null;
321: try {
322: configFile = getConfigFile();
323: reader = new FileReader(configFile);
324: } catch (FileNotFoundException e) {
325: logger.log(BasicLevel.ERROR, "Cannot find config file "
326: + configFile);
327: throw new ServiceException(e.getMessage(), e);
328: }
329:
330: try {
331: JResourceManagerWrapper.addResources(jResources, reader,
332: configFile.getPath());
333: } catch (Exception e1) {
334: String err = "Cannot add security resource from '"
335: + configFile + "'";
336: logger.log(BasicLevel.ERROR, err);
337: throw new ServiceException(err, e1);
338: }
339: }
340:
341: /**
342: * Return a File object representing the jonas-realm.xml configuration file.
343: * @return a File object representing the jonas-realm.xml configuration
344: * file.
345: * @throws FileNotFoundException if the configuration file is not found.
346: */
347: protected File getConfigFile() throws FileNotFoundException {
348: String fileName = System.getProperty("jonas.base");
349: fileName = fileName + File.separator + CONFIG_FILE;
350: File file = new File(fileName);
351: if (!file.exists()) {
352: String err = "Can't find configuration file : " + fileName;
353: throw new FileNotFoundException(err);
354: }
355: return (file);
356: }
357:
358: /**
359: * String representation of the JOnAS realm
360: * @return the xml representation of the JOnAS realm
361: */
362: public String toXML() {
363: return jResources.toXML();
364: }
365:
366: /**
367: * Encrypt a string with an algorithm
368: * @param string the string to encode
369: * @param algo algorithm to apply on the given string
370: * @return the encoded string
371: * @throws NoSuchAlgorithmException One reason could be a bad algorithm
372: */
373: public String encryptPassword(String string, String algo)
374: throws NoSuchAlgorithmException {
375: String encrypt = HashHelper.hashPassword(string, algo);
376: // Prefix with algorithm
377: return "{" + algo.toUpperCase() + "}" + encrypt;
378: }
379:
380: /**
381: * Check if the given algorithm is a valid algorithm
382: * @param algo algorithm to apply on the given string
383: * @return true if it is a valid algorithm
384: */
385: public boolean isValidAlgorithm(String algo) {
386: boolean b = true;
387: try {
388: encryptPassword("test", algo);
389: } catch (NoSuchAlgorithmException nsae) {
390: b = false;
391: }
392: return b;
393: }
394:
395: /**
396: * Add JResources with a given xml configuration
397: * @param xml xml representation of the resources to add
398: * @throws Exception if the resources can't be added
399: */
400: public void addResources(String xml) throws Exception {
401:
402: try {
403: JResourceManagerWrapper.addResources(jResources,
404: new StringReader(xml), "");
405: } catch (Exception e1) {
406: String err = "Cannot add security resource from xml '"
407: + xml + "'";
408: logger.log(BasicLevel.ERROR, err);
409: throw new ServiceException(err, e1);
410: }
411: }
412:
413: /**
414: * Add a Memory resource
415: * @param name the name of the JResourceMemory to create
416: * @throws Exception if the resource can't be added
417: */
418: public void addJResourceMemory(String name) throws Exception {
419:
420: // Build a new JResourceMemory
421: JResourceMemory jResourceMemory = new JResourceMemory();
422: jResourceMemory.setName(name);
423:
424: // Build xml
425: StringBuffer xml = new StringBuffer(JResources.HEADER_XML);
426: xml.append("<jonas-realm>");
427: xml.append("<jonas-memoryrealm>");
428: xml.append(jResourceMemory.toXML());
429: xml.append("</jonas-memoryrealm>");
430: xml.append("</jonas-realm>");
431:
432: // Add the resource
433: addResources(xml.toString());
434:
435: }
436:
437: /**
438: * Add a DS resource
439: * @param name the name of the JResourceDS to create
440: * @param dsName Name of the datasource resource to use.
441: * @param userTable Name of table which have the username/password
442: * @param userTableUsernameCol Column of the username of the user table
443: * @param userTablePasswordCol Column of the password of the user table
444: * @param roleTable Name of table which have the username/role
445: * @param roleTableUsernameCol Column of the username of the role table
446: * @param roleTableRolenameCol Column of the role of the role table
447: * @param algorithm Default algorithm. If specified, the default is not
448: * 'clear' password
449: * @throws Exception if the resource can't be added
450: */
451: public void addJResourceDS(String name, String dsName,
452: String userTable, String userTableUsernameCol,
453: String userTablePasswordCol, String roleTable,
454: String roleTableUsernameCol, String roleTableRolenameCol,
455: String algorithm) throws Exception {
456:
457: // Build a new JResourceDS
458: JResourceDS jResourceDS = new JResourceDS();
459: jResourceDS.setName(name);
460: jResourceDS.setDsName(dsName);
461: jResourceDS.setUserTable(userTable);
462: jResourceDS.setUserTableUsernameCol(userTableUsernameCol);
463: jResourceDS.setUserTablePasswordCol(userTablePasswordCol);
464: jResourceDS.setRoleTable(roleTable);
465: jResourceDS.setRoleTableUsernameCol(roleTableUsernameCol);
466: jResourceDS.setRoleTableRolenameCol(roleTableRolenameCol);
467: jResourceDS.setAlgorithm(algorithm);
468:
469: // Build xml
470: StringBuffer xml = new StringBuffer(JResources.HEADER_XML);
471: xml.append("<jonas-realm>");
472: xml.append("<jonas-dsrealm>");
473: xml.append(jResourceDS.toXML());
474: xml.append("</jonas-dsrealm>");
475: xml.append("</jonas-realm>");
476:
477: // Add the resource
478: addResources(xml.toString());
479:
480: }
481:
482: /**
483: * Add a LDAP resource
484: * @param name the name of the JResourceLDAP to create
485: * @param initialContextFactory Initial context factory for the LDAp server
486: * @param providerUrl Url of the ldap server
487: * @param securityAuthentication Type of the authentication used during the
488: * authentication to the LDAP server
489: * @param securityPrincipal DN of the Principal(username). He can retrieve
490: * the information from the user
491: * @param securityCredentials Credential(password) of the principal
492: * @param securityProtocol Constant that holds the name of the environment
493: * property for specifying the security protocol to use.
494: * @param language Constant that holds the name of the environment property
495: * for specifying the preferred language to use with the service.
496: * @param referral Constant that holds the name of the environment property
497: * for specifying how referrals encountered by the service provider
498: * are to be processed.
499: * @param stateFactories Constant that holds the name of the environment
500: * property for specifying the list of state factories to use.
501: * @param authenticationMode Mode for validate the authentication
502: * (BIND_AUTHENTICATION_MODE or COMPARE_AUTHENTICATION_MODE)
503: * @param userPasswordAttribute Attribute in order to get the password from
504: * the ldap server
505: * @param userRolesAttribute Attribute in order to get the user role from
506: * the ldap server
507: * @param roleNameAttribute Attribute for the role name when performing a
508: * lookup on a role
509: * @param baseDN DN used for the lookup
510: * @param userDN DN used when searching the user DN. Override the baseDN if
511: * it is defined
512: * @param userSearchFilter Filter used when searching the user
513: * @param roleDN DN used when searching the role DN. Override the baseDN if
514: * it is defined
515: * @param roleSearchFilter Filter used when searching the role
516: * @param algorithm Default algorithm. If specified, the default is not
517: * 'clear' password
518: * @throws Exception if the resource can't be added
519: */
520: public void addJResourceLDAP(String name,
521: String initialContextFactory, String providerUrl,
522: String securityAuthentication, String securityPrincipal,
523: String securityCredentials, String securityProtocol,
524: String language, String referral, String stateFactories,
525: String authenticationMode, String userPasswordAttribute,
526: String userRolesAttribute, String roleNameAttribute,
527: String baseDN, String userDN, String userSearchFilter,
528: String roleDN, String roleSearchFilter, String algorithm)
529: throws Exception {
530:
531: // Build a new JResourceLDAP
532: JResourceLDAP jResourceLDAP = new JResourceLDAP();
533: jResourceLDAP.setName(name);
534: jResourceLDAP.setInitialContextFactory(initialContextFactory);
535: jResourceLDAP.setProviderUrl(providerUrl);
536: jResourceLDAP.setSecurityAuthentication(securityAuthentication);
537: jResourceLDAP.setSecurityPrincipal(securityPrincipal);
538: jResourceLDAP.setSecurityCredentials(securityCredentials);
539: jResourceLDAP.setSecurityProtocol(securityProtocol);
540: jResourceLDAP.setLanguage(language);
541: jResourceLDAP.setReferral(referral);
542: jResourceLDAP.setStateFactories(stateFactories);
543: jResourceLDAP.setAuthenticationMode(authenticationMode);
544: jResourceLDAP.setUserPasswordAttribute(userPasswordAttribute);
545: jResourceLDAP.setUserRolesAttribute(userRolesAttribute);
546: jResourceLDAP.setRoleNameAttribute(roleNameAttribute);
547: jResourceLDAP.setBaseDN(baseDN);
548: jResourceLDAP.setUserDN(userDN);
549: jResourceLDAP.setUserSearchFilter(userSearchFilter);
550: jResourceLDAP.setRoleDN(roleDN);
551: jResourceLDAP.setRoleSearchFilter(roleSearchFilter);
552: jResourceLDAP.setAlgorithm(algorithm);
553:
554: // Build xml
555: StringBuffer xml = new StringBuffer(JResources.HEADER_XML);
556: xml.append("<jonas-realm>");
557: xml.append("<jonas-ldaprealm>");
558: xml.append(jResourceLDAP.toXML());
559: xml.append("</jonas-ldaprealm>");
560: xml.append("</jonas-realm>");
561:
562: // Add the resource
563: addResources(xml.toString());
564:
565: }
566:
567: /**
568: * Bind the given resource with the given name and register with a new
569: * MBean.
570: * @param name resource name
571: * @param jResource resource
572: */
573: public void bindResource(String name, JResource jResource) {
574: // bind the resource into the jndi
575: if (bindResourcesIntoJndi) {
576: try {
577: ictx.rebind(jResource.getName(), jResource);
578: if (logger.isLoggable(BasicLevel.DEBUG)) {
579: logger.log(BasicLevel.DEBUG, "jResource "
580: + jResource.getName()
581: + " bound into the registry.");
582: }
583: } catch (NamingException e) {
584: logger.log(BasicLevel.ERROR,
585: "Cannot bind the resource '"
586: + jResource.getName() + "' into JNDI",
587: e);
588: }
589: }
590:
591: try {
592: // register security factory mbean
593: if (jResource instanceof JResourceMemory) {
594: mbeanServer.registerMBean(jResource, JonasObjectName
595: .securityMemoryFactory(jResource.getName()));
596: } else if (jResource instanceof JResourceDS) {
597: mbeanServer
598: .registerMBean(jResource, JonasObjectName
599: .securityDatasourceFactory(jResource
600: .getName()));
601: } else if (jResource instanceof JResourceLDAP) {
602: mbeanServer.registerMBean(jResource, JonasObjectName
603: .securityLdapFactory(jResource.getName()));
604: }
605: } catch (ServiceException se) {
606: logger.log(BasicLevel.ERROR, "JMX service not available",
607: se);
608: } catch (Exception e) {
609: logger.log(BasicLevel.ERROR,
610: "Can not register the MBean for the resource "
611: + jResource.getName() + " : "
612: + e.getMessage());
613: throw new ServiceException(
614: "Can not register the MBean for the resource "
615: + jResource.getName() + " : "
616: + e.getMessage());
617: }
618:
619: }
620: }
|