/*
* ProActive Parallel Suite(TM):
* The Open Source library for parallel and distributed
* Workflows & Scheduling, Orchestration, Cloud Automation
* and Big Data Analysis on Enterprise Grids & Clouds.
*
* Copyright (c) 2007 - 2017 ActiveEon
* Contact: contact@activeeon.com
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation: version 3 of
* the License.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If needed, contact us to obtain a release under GPL Version 2 or 3
* or a different license than the AGPL.
*/
package org.ow2.proactive.authentication;
import java.io.File;
import java.security.KeyException;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.objectweb.proactive.Body;
import org.objectweb.proactive.RunActive;
import org.objectweb.proactive.Service;
import org.objectweb.proactive.api.PAActiveObject;
import org.objectweb.proactive.core.body.request.Request;
import org.ow2.proactive.authentication.crypto.CredData;
import org.ow2.proactive.authentication.crypto.Credentials;
/**
* An active object responsible for authentication.
*
* @author The ProActive Team
* @since ProActive Scheduling 0.9.1
*/
public abstract class AuthenticationImpl implements Authentication, RunActive {
/** Activation is used to control authentication during scheduling initialization */
private boolean activated = false;
/**
* Defines login method
*
* @return a string which represents the login method.
*/
protected abstract String getLoginMethod();
/**
* Path to the private key file for used for authentication
*/
protected String privateKeyPath;
/**
* Path to the private key file for used for authentication
*/
protected String publicKeyPath;
/**
* Empty constructor
*/
public AuthenticationImpl() {
}
/**
* Default constructor
* <p>
* Loads jaas.config and stores it in global system property,
* also locates keypair used for authentication:
* public key is used to encrypt credentials to make the old deprecated API still compatible,
* private key is used to decrypt credentials in the new API.
*
* @param jaasPath path to the jaas configuration file
* @param privPath path to the private key file
* @param pubPath path to the public key file
*
*/
public AuthenticationImpl(String jaasPath, String privPath, String pubPath) {
File jaasFile = new File(jaasPath);
if (jaasFile.exists() && !jaasFile.isDirectory()) {
System.setProperty("java.security.auth.login.config", jaasPath);
} else {
throw new RuntimeException("Could not find Jaas configuration at: " + jaasPath);
}
File privFile = new File(privPath);
if (privFile.exists() && !privFile.isDirectory()) {
this.privateKeyPath = privPath;
} else {
throw new RuntimeException("Could not find private key file at: " + privPath);
}
File pubFile = new File(pubPath);
if (pubFile.exists() && !pubFile.isDirectory()) {
this.publicKeyPath = pubPath;
} else {
throw new RuntimeException("Could not find public key file at: " + pubPath);
}
}
/**
* Performs login.
*
* @param cred encrypted username and password
* @return the name of the user logged
* @throws LoginException if username or password is incorrect.
*/
public Subject authenticate(Credentials cred) throws LoginException {
if (activated == false) {
throw new LoginException("Authentication active object is not activated.");
}
CredData credentials = null;
try {
credentials = cred.decrypt(privateKeyPath);
} catch (KeyException e) {
throw new LoginException("Could not decrypt credentials: " + e);
}
String username = credentials.getLogin();
String password = credentials.getPassword();
if (username == null || username.equals("")) {
throw new LoginException("Bad user name (user is null or empty)");
}
try {
// Verify that this user//password can connect to this existing scheduler
getLogger().info(username + " is trying to connect");
Map<String, Object> params = new HashMap<>(4);
//user name to check
params.put("username", username);
//password to check
params.put("pw", password);
//Load LoginContext according to login method defined in jaas.config
LoginContext lc = new LoginContext(getLoginMethod(), new NoCallbackHandler(params));
lc.login();
getLogger().info("User " + username + " logged successfully");
return lc.getSubject();
} catch (LoginException e) {
getLogger().info(e.getMessage());
//Nature of exception is hidden for user, we don't want to inform
//user about the reason of non authentication
throw new LoginException("Authentication failed");
}
}
/**
* Request this AuthenticationImpl's public key.
* <p>
* The public key provided by this method can be used to create encrypted credentials with
* {@link org.ow2.proactive.authentication.crypto.Credentials#createCredentials(String, String, PublicKey)}.
* The private key corresponding to this public key will be used for decryption.
*
* @return this AuthenticationImpl's public key
* @throws LoginException the key could not be retrieved
*/
public PublicKey getPublicKey() throws LoginException {
if (activated == false) {
throw new LoginException("Authentication active object is not activated.");
}
try {
return Credentials.getPublicKey(this.publicKeyPath);
} catch (KeyException e) {
getLogger().error("", e);
throw new LoginException("Could not retrieve public key");
}
}
/**
* @see org.ow2.proactive.authentication.Authentication#isActivated()
*/
public boolean isActivated() {
return activated;
}
/**
* Activates or desactivates authentication active object
*
* @param activated the status of the desired activated state.
*/
public void setActivated(boolean activated) {
this.activated = activated;
}
/**
* Terminates the active object
*
* @return true if the object has been terminated.
*/
public boolean terminate() {
PAActiveObject.terminateActiveObject(false);
getLogger().info("Authentication service is now shutdown!");
return true;
}
/**
* Method controls the execution of every request.
* Tries to keep this active object alive in case of any exception.
*/
public void runActivity(Body body) {
Service service = new Service(body);
while (body.isActive()) {
Request request = null;
try {
request = service.blockingRemoveOldest();
if (request != null) {
try {
service.serve(request);
} catch (Throwable e) {
getLogger().error("Cannot serve request: " + request, e);
}
}
} catch (InterruptedException e) {
getLogger().warn("runActivity interrupted", e);
}
}
}
}