com.googlecode.fascinator.authentication.internal.InternalAuthentication.java Source code

Java tutorial

Introduction

Here is the source code for com.googlecode.fascinator.authentication.internal.InternalAuthentication.java

Source

/* 
 * The Fascinator - Internal Authentication plugin
 * Copyright (C) 2010-2011 University of Southern Queensland
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package com.googlecode.fascinator.authentication.internal;

import com.googlecode.fascinator.api.PluginDescription;
import com.googlecode.fascinator.api.authentication.Authentication;
import com.googlecode.fascinator.api.authentication.AuthenticationException;
import com.googlecode.fascinator.api.authentication.User;
import com.googlecode.fascinator.common.JsonSimpleConfig;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * This plugin implements the Fascinator default internal userbase.
 * This plugin mostly a direct port from Fascinator IR code and is a very minimal 
 * proof-of-concept implemetation of authentication. 
 * Currently it only authenticates from a userbase stored in a local file.
 * </p>
 * 
 * <h3>Configuration</h3> 
 * <p>Standard configuration table:</p>
 * <table border="1">
 * <tr>
 * <th>Option</th>
 * <th>Description</th>
 * <th>Required</th>
 * <th>Default</th>
 * </tr>
 * 
 * <tr>
 * <td>internal/path</td>
 * <td>File path in wich the userbase information is stored</td>
 * <td><b>Yes</b></td>
 * <td>${user.home}/.fascinator/users.properties</td>
 * </tr>
 * 
 * </table>
 * 
 * <h3>Examples</h3>
 * <ol>
 * <li>
 * Using Internal authentication plugin in The Fascinator
 * 
 * <pre>
 *      "authentication": {
 *         "type": "internal",
 *         "internal": {
 *            "path": "${user.home}/.fascinator/users.properties"
 *         }
 *       }
 * </pre>
 * 
 * </li>
 * </ol>
 * 
 * <h3>Wiki Link</h3>
 * <p>
 * None
 * </p>
 *
 * @author Greg Pendlebury
 */

public class InternalAuthentication implements Authentication {

    /** Default file name for storing user properties */
    private static String DEFAULT_FILE_NAME = "users.properties";

    /** Loggin */
    @SuppressWarnings("unused")
    private final Logger log = LoggerFactory.getLogger(InternalAuthentication.class);

    /** User object */
    private InternalUser user_object;

    /** File path where user properties is stored */
    private String file_path;

    /** Property file for user properties */
    private Properties file_store;

    @Override
    public String getId() {
        return "internal";
    }

    @Override
    public String getName() {
        return "Internal Authentication";
    }

    /**
     * Gets a PluginDescription object relating to this plugin.
     * 
     * @return a PluginDescription
     */
    @Override
    public PluginDescription getPluginDetails() {
        return new PluginDescription(this);
    }

    /**
     * Initialisation of Internal Authentication plugin
     * 
     * @throws AuthenticationException if fails to initialise
     */
    @Override
    public void init(String jsonString) throws AuthenticationException {
        try {
            setConfig(new JsonSimpleConfig(jsonString));
        } catch (UnsupportedEncodingException e) {
            throw new AuthenticationException(e);
        } catch (IOException e) {
            throw new AuthenticationException(e);
        }
    }

    @Override
    public void init(File jsonFile) throws AuthenticationException {
        try {
            setConfig(new JsonSimpleConfig(jsonFile));
        } catch (IOException ioe) {
            throw new AuthenticationException(ioe);
        }
    }

    /**
     * Set default configuration
     * 
     * @param config JSON configuration
     * @throws IOException if fails to initialise
     */
    private void setConfig(JsonSimpleConfig config) throws IOException {
        // Get the basics
        user_object = new InternalUser();
        file_path = config.getString(null, "authentication", "internal", "path");
        loadUsers();
    }

    /**
     * Load users from the file
     * 
     * @throws IOException if fail to load from file
     */
    private void loadUsers() throws IOException {
        file_store = new Properties();

        // Load our userbase from disk
        try {
            File user_file = new File(file_path);
            if (!user_file.exists()) {
                user_file.getParentFile().mkdirs();
                OutputStream out = new FileOutputStream(user_file);
                IOUtils.copy(getClass().getResourceAsStream("/" + DEFAULT_FILE_NAME), out);
                out.close();
            }

            file_store.load(new FileInputStream(file_path));
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    /**
     * Save user lists to the file on the disk
     * 
     * @throws IOException if fail to save to file
     */
    private void saveUsers() throws IOException {
        if (file_store != null) {
            try {
                file_store.store(new FileOutputStream(file_path), "");
            } catch (Exception e) {
                throw new IOException(e);
            }
        }
    }

    /**
     * Password encryption method
     * 
     * @param password Password to be encrypted
     * @return encrypted password
     * @throws AuthenticationException if fail to encrypt
     */
    private String encryptPassword(String password) throws AuthenticationException {
        byte[] passwordBytes = password.getBytes();

        try {
            MessageDigest algorithm = MessageDigest.getInstance("MD5");
            algorithm.reset();
            algorithm.update(passwordBytes);

            byte messageDigest[] = algorithm.digest();
            BigInteger number = new BigInteger(1, messageDigest);

            password = number.toString(16);
            if (password.length() == 31) {
                password = "0" + password;
            }
        } catch (Exception e) {
            throw new AuthenticationException("Internal password encryption failure: " + e.getMessage());
        }
        return password;
    }

    @Override
    public void shutdown() throws AuthenticationException {
        // Don't need to do anything
    }

    /**
     * Tests the user's username/password validity.
     * 
     * @param username The username of the user logging in.
     * @param password The password of the user logging in.
     * @return A user object for the newly logged in user.
     * @throws AuthenticationException if there was an error logging in.
     */
    @Override
    public User logIn(String username, String password) throws AuthenticationException {
        // Find our user
        String uPwd = file_store.getProperty(username);
        if (uPwd == null) {
            throw new AuthenticationException("User '" + username + "' not found.");
        }
        // Encrypt the password given by the user
        String ePwd = encryptPassword(password);
        // Compare them
        if (ePwd.equals(uPwd)) {
            return getUser(username);
        } else {
            throw new AuthenticationException("Invalid password.");
        }
    }

    /**
     * Optional logout method if the implementing class wants to do any
     * post-processing.
     * 
     * @param username The username of the logging out user.
     * @throws AuthenticationException if there was an error logging out.
     */
    @Override
    public void logOut(User user) throws AuthenticationException {
        // Do nothing
    }

    /**
     * Method for testing if the implementing plugin allows the creation,
     * deletion and modification of users.
     * 
     * @return true/false reponse.
     */
    @Override
    public boolean supportsUserManagement() {
        return true;
    }

    /**
     * Describe the metadata the implementing class needs/allows for a user.
     * 
     * TODO: This is a placeholder of possible later SQUIRE integration.
     * 
     * @return TODO: possibly a JSON string.
     */
    @Override
    public String describeUser() {
        return user_object.describeMetadata();
    }

    /**
     * Create a user.
     * 
     * @param username The username of the new user.
     * @param password The password of the new user.
     * @return A user object for the newly created in user.
     * @throws AuthenticationException if there was an error creating the user.
     */
    @Override
    public User createUser(String username, String password) throws AuthenticationException {
        String user = file_store.getProperty(username);
        if (user != null) {
            throw new AuthenticationException("User '" + username + "' already exists.");
        }
        // Encrypt the new password
        String ePwd = encryptPassword(password);
        file_store.put(username, ePwd);
        try {
            saveUsers();
        } catch (IOException e) {
            throw new AuthenticationException("Error changing password: ", e);
        }

        return getUser(username);
    }

    /**
     * Delete a user.
     * 
     * @param username The username of the user to delete.
     * @throws AuthenticationException if there was an error during deletion.
     */
    @Override
    public void deleteUser(String username) throws AuthenticationException {
        String user = file_store.getProperty(username);
        if (user == null) {
            throw new AuthenticationException("User '" + username + "' not found.");
        }
        file_store.remove(username);
        try {
            saveUsers();
        } catch (IOException e) {
            throw new AuthenticationException("Error deleting user: ", e);
        }
    }

    /**
     * Change a user's password.
     * 
     * @param username The user changing their password.
     * @param password The new password for the user.
     * @throws AuthenticationException if there was an error changing the
     * password.
     */
    @Override
    public void changePassword(String username, String password) throws AuthenticationException {
        String user = file_store.getProperty(username);
        if (user == null) {
            throw new AuthenticationException("User '" + username + "' not found.");
        }
        // Encrypt the new password
        String ePwd = encryptPassword(password);
        file_store.put(username, ePwd);
        try {
            saveUsers();
        } catch (IOException e) {
            throw new AuthenticationException("Error changing password: ", e);
        }
    }

    /**
     * Modify one of the user's properties. Available properties should match up
     * with the return value of describeUser().
     * 
     * @param username The user being modified.
     * @param property The user property being modified.
     * @param newValue The new value to be assigned to the property.
     * @return An updated user object for the modifed user.
     * @throws AuthenticationException if there was an error during
     * modification.
     */
    @Override
    public User modifyUser(String username, String property, String newValue) throws AuthenticationException {
        throw new AuthenticationException("This class does not support user modification.");
    }

    @Override
    public User modifyUser(String username, String property, int newValue) throws AuthenticationException {
        throw new AuthenticationException("This class does not support user modification.");
    }

    @Override
    public User modifyUser(String username, String property, boolean newValue) throws AuthenticationException {
        throw new AuthenticationException("This class does not support user modification.");
    }

    /**
     * Returns a User object if the implementing class supports user queries
     * without authentication.
     * 
     * @param username The username of the user required.
     * @return An user object of the requested user.
     * @throws AuthenticationException if there was an error retrieving the
     * object.
     */
    @Override
    public User getUser(String username) throws AuthenticationException {
        // Find our user
        String user = file_store.getProperty(username);
        if (user == null) {
            throw new AuthenticationException("User '" + username + "' not found.");
        }
        // Purge any old data and init()
        user_object = new InternalUser();
        user_object.init(username);
        // before returning
        return user_object;
    }

    /**
     * Returns a list of users matching the search.
     * 
     * @param search The search string to execute.
     * @return A list of usernames (String) that match the search.
     * @throws AuthenticationException if there was an error searching.
     */
    @Override
    public List<User> searchUsers(String search) throws AuthenticationException {
        // Complete list of users
        String[] users = file_store.keySet().toArray(new String[file_store.size()]);
        List<User> found = new ArrayList<User>();

        // Look through the list for anyone who matches
        for (int i = 0; i < users.length; i++) {
            if (users[i].toLowerCase().contains(search.toLowerCase())) {
                found.add(getUser(users[i]));
            }
        }

        // Return the list
        return found;
    }

}