Java tutorial
/* * Copyright (C) 2014 Fabien Vauchelles (fabien_AT_vauchelles_DOT_com). * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3, 29 June 2007, of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ package com.vaushell.gfmongodb; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBObject; import com.mongodb.MongoClient; import com.mongodb.QueryBuilder; import com.sun.appserv.security.AppservRealm; import com.sun.enterprise.security.auth.realm.BadRealmException; import com.sun.enterprise.security.auth.realm.NoSuchRealmException; import java.net.UnknownHostException; import org.jvnet.hk2.annotations.Service; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import javax.security.auth.login.LoginException; import org.apache.commons.codec.digest.DigestUtils; /** * MongoDbUserRealm. Based on https://github.com/martijnblankestijn/glassfish-jdbc-realm. * * @author Fabien Vauchelles (fabien_AT_vauchelles_DOT_com) */ @Service(name = MongoDbUserRealm.SERVICE_NAME) public class MongoDbUserRealm extends AppservRealm { // PUBLIC public static final String SERVICE_NAME = "CustomMongoDbUserRealm"; public static final String PARAM_DB = "database-name"; public static final String PARAM_COLLECTION = "database-collection"; public static final String PARAM_USERNAME = "field-username"; public static final String PARAM_PASSWORD = "field-password"; public static final String PARAM_ADMIN = "field-admin"; public static final String PARAM_GROUP_USERS = "group-users"; public static final String PARAM_GROUP_ADMINS = "group-admins"; public MongoDbUserRealm() { super(); this.usersCollection = null; } /** * Get a descriptive string representing the type of authentication done by this realm. * * @return a descriptive string representing the type of authentication done by this realm */ @Override public String getAuthType() { return "Custom MongoDB User Realm"; } @Override public Enumeration getGroupNames(final String username) { final QueryBuilder builder = QueryBuilder.start(getProperty(PARAM_USERNAME)).is(username); final DBObject user = usersCollection.findOne(builder.get()); if (user == null) { return null; } return Collections.enumeration(getGroups(user)); } // PROTECTED @Override protected void init(final Properties parameters) throws BadRealmException, NoSuchRealmException { super.init(parameters); // Among the other custom properties, there is a property jaas-context (which is explained later in this post). // This property should be set using the call setProperty method implemented in the parent class. /// From: https://blogs.oracle.com/nithya/entry/groups_in_custom_realms checkAndSetProperty(JAAS_CONTEXT_PARAM, parameters); for (final Map.Entry<String, String> entry : OPTIONAL_PROPERTIES.entrySet()) { setOptionalProperty(entry.getKey(), parameters, entry.getValue()); } try { final MongoClient client = new MongoClient(); final DB db = client.getDB(getProperty(PARAM_DB)); usersCollection = db.getCollection(getProperty(PARAM_COLLECTION)); } catch (final UnknownHostException ex) { throw new BadRealmException(ex); } } // DEFAULT /** * Authenticate user. * * @param username Username. * @param givenPassword Password * @return List of groups. * @throws LoginException */ String[] authenticate(final String username, final char[] givenPassword) throws LoginException { if (username == null || username.length() <= 0 || givenPassword == null || givenPassword.length <= 0) { throw new LoginException("username or password is empty"); } final QueryBuilder builder = QueryBuilder.start(getProperty(PARAM_USERNAME)).is(username); final DBObject user = usersCollection.findOne(builder.get()); if (user == null) { throw new LoginException("cannot find user with username '" + username + "'"); } final String databasePassword = (String) user.get(getProperty(PARAM_PASSWORD)); if (databasePassword == null || databasePassword.length() <= 0) { throw new LoginException("cannot find nonempty password for username '" + username + "'"); } final String transformedPassword = DigestUtils.sha256Hex(new String(givenPassword)); if (!databasePassword.equals(transformedPassword)) { throw new LoginException("password is wrong for username '" + username + "'"); } final List<String> groups = getGroups(user); return groups.toArray(new String[groups.size()]); } // PRIVATE private static final String DEFAULT_DB = "mongo"; private static final String DEFAULT_COLLECTION = "users"; private static final String DEFAULT_USERNAME = "email"; private static final String DEFAULT_PASSWORD = "password"; private static final String DEFAULT_ADMIN = "admin"; private static final String DEFAULT_GROUP_USERS = "g_users"; private static final String DEFAULT_GROUP_ADMINS = "g_admins"; private static final Map<String, String> OPTIONAL_PROPERTIES = new HashMap<>(); static { OPTIONAL_PROPERTIES.put(PARAM_DB, DEFAULT_DB); OPTIONAL_PROPERTIES.put(PARAM_COLLECTION, DEFAULT_COLLECTION); OPTIONAL_PROPERTIES.put(PARAM_USERNAME, DEFAULT_USERNAME); OPTIONAL_PROPERTIES.put(PARAM_PASSWORD, DEFAULT_PASSWORD); OPTIONAL_PROPERTIES.put(PARAM_ADMIN, DEFAULT_ADMIN); OPTIONAL_PROPERTIES.put(PARAM_GROUP_USERS, DEFAULT_GROUP_USERS); OPTIONAL_PROPERTIES.put(PARAM_GROUP_ADMINS, DEFAULT_GROUP_ADMINS); } private DBCollection usersCollection; private void setOptionalProperty(final String name, final Properties parameters, final String defaultValue) throws BadRealmException { checkAndSetProperty(name, parameters.getProperty(name, defaultValue)); } private void checkAndSetProperty(final String name, final Properties parameters) throws BadRealmException { checkAndSetProperty(name, parameters.getProperty(name)); } private void checkAndSetProperty(final String name, final String value) throws BadRealmException { if (value == null) { final String message = sm.getString("realm.missingprop", name, SERVICE_NAME); throw new BadRealmException(message); } super.setProperty(name, value); } private List<String> getGroups(final DBObject user) { final List<String> groups = new ArrayList<>(); groups.add(getProperty(PARAM_GROUP_USERS)); final Boolean admin = (Boolean) user.get(getProperty(PARAM_ADMIN)); if (admin != null && admin) { groups.add(getProperty(PARAM_GROUP_ADMINS)); } return groups; } }