com.vaushell.gfmongodb.MongoDbUserRealm.java Source code

Java tutorial

Introduction

Here is the source code for com.vaushell.gfmongodb.MongoDbUserRealm.java

Source

/*
 * 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;
    }
}