/*******************************************************************************
*Copyright (c) 2009 Eucalyptus Systems, Inc.
*
* 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, only version 3 of the License.
*
*
* This file 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, see <http://www.gnu.org/licenses/>.
*
* Please contact Eucalyptus Systems, Inc., 130 Castilian
* Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/>
* if you need additional information or have any questions.
*
* This file may incorporate work covered under the following copyright and
* permission notice:
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Regents of the University of California
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms, with
* or without modification, are permitted provided that the following
* conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF
* THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
* LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
* SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
* BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
* THE REGENTS’ DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
* OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
* WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
* ANY SUCH LICENSES OR RIGHTS.
*******************************************************************************/
/*
*
* Author: Dmitrii Zagorodnov dmitrii@cs.ucsb.edu
*/
package edu.ucsb.eucalyptus.admin.server;
import com.eucalyptus.auth.Debugging;
import com.eucalyptus.auth.Groups;
import com.eucalyptus.auth.UserInfo;
import com.eucalyptus.auth.Users;
import com.eucalyptus.auth.crypto.Crypto;
import com.eucalyptus.auth.principal.User;
import com.eucalyptus.bootstrap.HttpServerBootstrapper;
import com.eucalyptus.component.Component;
import com.eucalyptus.component.Components;
import com.eucalyptus.component.Service;
import com.eucalyptus.component.ServiceConfiguration;
import com.eucalyptus.config.ClusterConfiguration;
import com.eucalyptus.config.Configuration;
import com.eucalyptus.system.BaseDirectory;
import com.eucalyptus.system.SubDirectory;
import com.eucalyptus.util.EucalyptusCloudException;
import com.google.gwt.user.client.rpc.SerializableException;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.gwt.user.server.rpc.UnexpectedException;
import edu.ucsb.eucalyptus.admin.client.CloudInfoWeb;
import edu.ucsb.eucalyptus.admin.client.ClusterInfoWeb;
import edu.ucsb.eucalyptus.admin.client.DownloadsWeb;
import edu.ucsb.eucalyptus.admin.client.EucalyptusWebBackend;
import edu.ucsb.eucalyptus.admin.client.SystemConfigWeb;
import edu.ucsb.eucalyptus.admin.client.StorageInfoWeb;
import edu.ucsb.eucalyptus.admin.client.UserInfoWeb;
import edu.ucsb.eucalyptus.admin.client.VmTypeWeb;
import edu.ucsb.eucalyptus.admin.client.WalrusInfoWeb;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.ProxyHost;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
public class EucalyptusWebBackendImpl extends RemoteServiceServlet implements EucalyptusWebBackend {
private static Logger LOG = Logger.getLogger( EucalyptusWebBackendImpl.class );
private static String PROPERTIES_FILE = BaseDirectory.CONF.toString() + File.separator + "eucalyptus-web.properties";
private static HashMap sessions = new HashMap();
private static Properties props = new Properties();
private static long session_timeout_ms = 1000 * 60 * 60 * 24 * 14L; /* 2 weeks (TODO: put into config?) */
private static long pass_expiration_ms = 1000 * 60 * 60 * 24 * 365L; /* 1 year (TODO: put into config?) */
private static long recovery_expiration_ms = 1000 * 60 * 30; // 30 minutes (TODO: put into config?)
/* parameters to be read from config file */
private static String thanks_for_signup;
private static String signup_request_subject;
private static String signup_approval_subject;
private static String signup_approval_header;
private static String signup_approval_footer;
private static String signup_rejection_subject;
private static String signup_rejection_message;
private static String password_recovery_message;
private static String password_recovery_subject;
/* if these are not in config file, we'll use admin's email */
private static String signup_email;
private static String reply_email;
private boolean system_ready;
private void load_props() throws SerializableException
{
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(PROPERTIES_FILE);
props.load(fileInputStream);
props.setProperty("version", System.getProperty("euca.version"));
thanks_for_signup = props.getProperty("thanks-for-signup");
signup_email = props.getProperty("signup-email-address");
reply_email = props.getProperty("reply-email-address");
signup_request_subject = props.getProperty("signup-request-subject");
signup_approval_subject = props.getProperty("signup-approval-subject");
signup_approval_header = props.getProperty("signup-approval-header");
signup_approval_footer = props.getProperty("signup-approval-footer");
signup_rejection_subject = props.getProperty("signup-rejection-subject");
signup_rejection_message = props.getProperty("signup-rejection-message");
password_recovery_message = props.getProperty("password-recovery-message");
password_recovery_subject = props.getProperty("password-recovery-subject");
if (thanks_for_signup==null) {
throw new SerializableException("Server configuration is missing 'thanks-for-signup' value");
}
if (signup_request_subject==null) {
throw new SerializableException("Server configuration is missing 'signup-request-subject' value");
}
if (signup_approval_subject==null) {
throw new SerializableException("Server configuration is missing 'signup-approval-subject' value");
}
if (signup_approval_header==null) {
throw new SerializableException("Server configuration is missing 'signup-approval-header' value");
}
if (signup_approval_footer==null) {
throw new SerializableException("Server configuration is missing 'signup-approval-footer' value");
}
if (signup_rejection_subject==null) {
throw new SerializableException("Server configuration is missing 'signup-rejection-subject' value");
}
if (signup_rejection_message==null) {
throw new SerializableException("Server configuration is missing 'signup-rejection-message' value");
}
if (password_recovery_message==null) {
throw new SerializableException("Server configuration is missing 'password-recovery-message' value");
}
if (password_recovery_subject==null) {
throw new SerializableException("Server configuration is missing 'password-recovery-subject' value");
}
system_ready = true;
if ( signup_email==null) {
try {
signup_email = EucalyptusManagement.getAdminEmail();
} catch (Exception e) {
signup_email = ""; /* signup will not work until admin email address is set */
system_ready = false;
}
}
if (reply_email==null) {
reply_email = signup_email;
}
} catch (IOException e) {
throw new SerializableException("Could not read server configuration");
} catch (IllegalArgumentException e) {
throw new SerializableException("Malformed escape sequence in server configuration");
} finally {
if(fileInputStream != null)
try {
fileInputStream.close();
} catch (IOException e) {
LOG.error(e);
}
}
}
public String addUserRecord ( UserInfoWeb user )
throws SerializableException
{
return addUserRecord(null, user);
}
private void notifyAdminOfSignup (UserInfoWeb user)
throws SerializableException
{
try {
String http_eucalyptus = ServletUtils.getRequestUrl(getThreadLocalRequest());
String approve_link = http_eucalyptus + "?action=approve&user=" + user.getUserName();
String reject_link = http_eucalyptus + "?action=reject&user=" + user.getUserName();
String email_message =
"Someone has requested an account on the Eucalyptus system\n" +
"\n Name: " + user.getRealName() +
"\n Username: " + user.getUserName() +
"\n Email address: " + user.getEmail() +
"\n Telephone: " + user.getTelephoneNumber() +
"\n Affiliation: " + user.getAffiliation() +
"\n Project PI: " + user.getProjectPIName() +
"\n Project description: " +
"\n=====\n" + user.getProjectDescription() +
"\n=====\n\n" +
"To APPROVE this request, click on the following link:\n\n " +
approve_link +
"\n\n" +
"To REJECT this request, click on the following link:\n\n " +
reject_link +
"\n\n";
ServletUtils.sendMail( reply_email, signup_email,
signup_request_subject + " (" + user.getEmail() + ")",
email_message);
} catch (Exception e) {
LOG.error ("Signup mailing problem: " + e.getMessage()); /* TODO: log properly */
throw new SerializableException ("Internal problem (failed to notify administrator by email)");
}
}
public String addUserRecord(String sessionId, UserInfoWeb user)
throws SerializableException
{
if (user==null) {
throw new SerializableException("Invalid RPC arguments");
}
if (user.getUserName().equalsIgnoreCase( "eucalyptus" )) {
throw new SerializableException("User 'eucalyptus' is not allowed");
}
// these two won't happen unless the user hacks the client side
if ( user.getUserName().matches(".*[^\\w\\-\\.@]+.*") ) {
throw new SerializableException ("Invalid characters in the username");
}
if ( user.getUserName().length() < 1 || user.getUserName().length() > 30)
{
throw new SerializableException ( "Invalid length of username" );
}
load_props(); /* get parameters from config file */
boolean admin = false;
try {
SessionInfo session = verifySession (sessionId);
UserInfoWeb requestingUser = verifyUser (session, session.getUserId(), true);
if ( !requestingUser.isAdministrator().booleanValue()) {
user.setAdministrator(false); // in case someone is trying to be sneaky
throw new SerializableException("Administrative privileges required");
} else {
admin = true;
}
} catch (Exception e) { } /* that's OK, this was an anonymous request */
/* add the user */
long now = System.currentTimeMillis();
user.setPasswordExpires( new Long(now + pass_expiration_ms) );
user.setConfirmationCode( Crypto.generateSessionToken( user.getUserName() ) );
EucalyptusManagement.addWebUser(user);
String response;
if (admin) {
/* enable the new user right away */
user.setApproved(true);
user.setEnabled(true);
response = notifyUserApproved(user);
} else {
/* if anonymous, then notify admin */
user.setApproved(false);
user.setEnabled(false);
notifyAdminOfSignup (user);
response = thanks_for_signup;
}
EucalyptusManagement.commitWebUser(user);
return response;
}
private void notifyUserRecovery(UserInfoWeb user)
{
try {
String http_eucalyptus = ServletUtils.getRequestUrl(getThreadLocalRequest());
String confirm_link = http_eucalyptus + "?action=recover"
+ "&code=" + user.getConfirmationCode();
String email_message = password_recovery_message + "\n\n" +
confirm_link +
"\n";
ServletUtils.sendMail(reply_email, user.getEmail(),
password_recovery_subject,
email_message);
} catch (Exception e) {
// TODO: log this using the proper procedure
LOG.error ("Password recovery mailing problem: " + e.getMessage());
LOG.error ("Confirmation code for user '" + user.getUserName()
+ "' and address " + user.getEmail()
+ " is " + user.getConfirmationCode());
}
}
public String recoverPassword ( UserInfoWeb web_user )
throws SerializableException
{
if (web_user==null) {
throw new SerializableException("Invalid RPC arguments");
}
String response;
if (web_user.getPassword()==null) { // someone is initiating password recovery
try {
UserInfoWeb db_user = EucalyptusManagement.getWebUser(web_user.getUserName());
if (db_user.getEmail().equalsIgnoreCase(web_user.getEmail())) {
long expires = System.currentTimeMillis() + recovery_expiration_ms;
db_user.setConfirmationCode(String.format("%015d", expires) + Crypto.generateSessionToken( db_user.getUserName() ) );
EucalyptusManagement.commitWebUser(db_user);
notifyUserRecovery(db_user);
}
} catch (Exception e) { } // pretend all is well regardless of the outcome
response = "Please, check your email for further instructions.";
} else { // someone is trying to change the password
String code = web_user.getConfirmationCode();
if (code==null) {
throw new SerializableException("Insufficient parameters");
}
UserInfoWeb db_user;
try {
db_user = EucalyptusManagement.getWebUserByCode(code);
long expires = Long.parseLong(code.substring(0, 15));
long now = System.currentTimeMillis();
if (now > expires) {
throw new SerializableException("Recovery attempt expired");
}
db_user.setConfirmationCode("-unset-"); // so the code cannot be reused
db_user.setPassword (web_user.getPassword());
db_user.setPasswordExpires( new Long(now + pass_expiration_ms) );
EucalyptusManagement.commitWebUser(db_user);
} catch (Exception e) {
throw new SerializableException("Incorrect code");
}
response = "Your password has been reset.";
}
return response;
}
/* ensure the sessionId is (still) valid */
public static SessionInfo verifySession (String sessionId)
throws SerializableException
{
if (sessionId==null) {
throw new SerializableException("Invalid RPC arguments");
}
SessionInfo session = (SessionInfo)sessions.get(sessionId);
if (session==null) {
throw new SerializableException("Earlier session not found");
}
final long now = System.currentTimeMillis();
if ((now-session.getLastAccessed()) > session_timeout_ms) {
sessions.remove(sessionId);
throw new SerializableException("Earlier session expired");
}
session.setLastAccessed(System.currentTimeMillis());
return session;
}
private static boolean isPasswordExpired (UserInfoWeb user) {
final long now = System.currentTimeMillis();
if ((now > 0) && (now >= user.getPasswordExpires().longValue())) {
return true;
}
return false;
}
/* ensure the user exists and valid */
private static UserInfoWeb verifyUser (SessionInfo session, String userName, boolean verifyPasswordAge)
throws SerializableException
{
UserInfoWeb user;
if (userName==null) {
throw new SerializableException("Invalid RPC arguments: userIname is missing");
}
try {
user = EucalyptusManagement.getWebUser(userName);
} catch (Exception e) {
if (session!=null) {
sessions.remove(session.getSessionId());
}
throw new SerializableException("Login incorrect");
}
if (!user.isApproved()) {
throw new SerializableException("User not approved yet");
}
if (!user.isEnabled()) {
throw new SerializableException("Disabled user account");
}
if (!user.isConfirmed()) {
throw new SerializableException("Unconfirmed account (click on the link in confirmation email)");
}
if (verifyPasswordAge) {
if (isPasswordExpired(user) &&
!(user.isAdministrator() && user.getEmail().equalsIgnoreCase(UserInfo.BOGUS_ENTRY))) { // first-time config will catch that
throw new SerializableException("Password expired");
}
}
return user;
}
public String getNewSessionID (String userId, String md5Password)
throws SerializableException
{
String sessionId = null;
UserInfoWeb user;
if (md5Password==null) {
throw new SerializableException("Invalid RPC arguments: password is missing");
}
// you can get a sessionId with an expired password so you can change it => false
user = verifyUser (null, userId, false);
if (!user.getPassword().equals( md5Password )) {
throw new SerializableException("Login incorrect");
}
sessionId = ServletUtils.genGUID();
SessionInfo session = new SessionInfo(sessionId, userId, System.currentTimeMillis());
session.setStartedOn(session.getLastAccessed());
sessions.put(session.getSessionId(), session);
return session.getSessionId();
}
private String notifyUserApproved(UserInfoWeb user)
{
String confString = " and confirmed";
try {
if (!user.isConfirmed().booleanValue()) {
confString = "";
String http_eucalyptus = ServletUtils.getRequestUrl(getThreadLocalRequest());
String confirm_link = http_eucalyptus + "?action=confirm"
+ "&code=" + user.getConfirmationCode();
String email_message = signup_approval_header + "\n\n" +
confirm_link +
"\n\n" + signup_approval_footer;
ServletUtils.sendMail(reply_email, user.getEmail(),
signup_approval_subject,
email_message);
}
} catch (Exception e) {
// TODO: log this using the proper procedure
LOG.error ("Approval mailing problem: " + e.getMessage());
LOG.error ("Confirmation code for user " + user.getUserName()
+ " is " + user.getConfirmationCode());
return "Internal problem (failed to notify user " + user.getEmail() + " by email)";
}
return "User '" + user.getUserName() + "' was approved" + confString + ", thank you.";
}
private String notifyUserRejected(UserInfoWeb user)
{
try {
ServletUtils.sendMail(reply_email, user.getEmail(),
signup_rejection_subject,
signup_rejection_message);
} catch (Exception e) {
LOG.error ("Rejection mailing problem: " + e.getMessage());
return "Internal problem (failed to notify user " + user.getEmail() + ")";
}
return "User '" + user.getUserName() + "' was rejected.";
}
public String performAction (String sessionId, String action, String param)
throws SerializableException
{
load_props();
if (action==null || param==null) {
throw new SerializableException("Invalid RPC arguments: action or param are missing");
}
/* these don't need a session */
if (action.equals("recover") ||
action.equals("confirm")) {
UserInfoWeb user = EucalyptusManagement.getWebUserByCode(param);
String response = "OK";
if (action.equals("confirm")) {
if ( user != null ) {
user.setConfirmed(true);
user.setConfirmationCode("-unset-"); // so the code cannot be reused
EucalyptusManagement.commitWebUser(user);
}
response = "Your account is now active.";
} else if (action.equals("recover")) { // this is just a way to verify that the code is valid (TODO: remove?)
if (user == null) {
throw new SerializableException("Invalid code");
}
response = "Your password has been reset.";
}
return response;
}
final SessionInfo session = verifySession (sessionId);
final UserInfoWeb user = verifyUser (session, session.getUserId(), true);
String response = "Your `" + action + "` request succeeded."; /* generic response */
if (action.equals("approve")
|| action.equals("reject")
|| action.equals ( "delete" )
|| action.equals ( "disable" )
|| action.equals ( "enable" )) {
String userName = param;
if (!user.isAdministrator()) {
throw new SerializableException("Administrative privileges required");
}
UserInfoWeb new_user = EucalyptusManagement.getWebUser(userName);
if (action.equals("approve")) {
new_user.setApproved(true);
new_user.setEnabled(true);
new_user.setConfirmed(false);
EucalyptusManagement.commitWebUser(new_user);
response = notifyUserApproved(new_user);
} else if (action.equals("reject")) {
EucalyptusManagement.deleteWebUser(new_user);
response = notifyUserRejected(new_user);
} else if (action.equals("delete")) {
EucalyptusManagement.deleteWebUser(new_user);
/* TODO: maybe tell the user that his account was deleted? */
} else if (action.equals("disable")) {
new_user.setEnabled(false);
EucalyptusManagement.commitWebUser(new_user);
} else if (action.equals("enable")) {
new_user.setEnabled(true);
EucalyptusManagement.commitWebUser(new_user);
}
response = "Request to " + action + " user '" + userName + "' succeeded.";
} else if (action.equals("delete_image")
|| action.equals("enable_image")
|| action.equals("disable_image")) {
String imageId = param;
if (!user.isAdministrator()) {
throw new SerializableException("Administrative privileges required");
}
if (action.equals("delete_image")) {
EucalyptusManagement.deleteImage(imageId);
} else if (action.equals("disable_image")) {
EucalyptusManagement.disableImage(imageId);
} else if (action.equals("enable_image")) {
EucalyptusManagement.enableImage(imageId);
}
response = "Your request succeeded, thank you.";
} else {
throw new SerializableException("Action '" + action + "' is not implemented.");
}
return response;
}
public void logoutSession(String sessionId)
throws SerializableException
{
if (sessionId==null) {
throw new SerializableException("Invalid RPC arguments: sessionId is missing");
}
SessionInfo session = (SessionInfo)sessions.get(sessionId);
if (session!=null) {
sessions.remove(sessionId);
SessionInfo old = (SessionInfo)sessions.get(sessionId);
}
}
public List getUserRecord (String sessionId, String userId)
throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
List l = new ArrayList();
if (userId==null) {
l.add(user);
} else {
if (!user.isAdministrator()) {
throw new SerializableException("Only administrators can view users");
}
if (userId.equals("*")) {
l.addAll( EucalyptusManagement.getWebUsers(userId) ); /* NOTE: userId is currently ignored */
} else {
l.add(EucalyptusManagement.getWebUser(user.getUserName()));
}
}
return l;
}
public static UserInfoWeb getUserRecord (String sessionId) // a *static* getUserRecord, for ImageStoreService
throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
return user;
}
public List getImageInfo (String sessionId, String userId)
throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
/* TODO: right now userId parameter is ignored since we only have public images */
return EucalyptusManagement.getWebImages(userId);
}
/* from here on down, all requests require users to be enabled, approved, and confirmed */
public String getNewCert(String sessionId)
throws SerializableException
{
/* perform full checks */
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
return user.getToken();
}
public HashMap getProperties()
throws SerializableException
{
load_props();
HashMap h = new HashMap();
for (Enumeration e = props.propertyNames(); e.hasMoreElements() ;) {
String key = (String)e.nextElement();
h.put(key, props.getProperty(key));
}
h.put("ready", system_ready);
return h;
}
public String changePassword (String sessionId, String oldPassword, String newPassword )
throws SerializableException
{
/* check everything except password expiration */
SessionInfo session = verifySession (sessionId);
// naturally, it is OK to change the password if it expired => false
UserInfoWeb user = verifyUser (session, session.getUserId(), false);
/* check old password if the user is changing password voluntarily */
if ( !isPasswordExpired((UserInfoWeb)user) ) {
if ( !oldPassword.equals(user.getPassword()) ) {
throw new SerializableException("Old password is incorrect");
}
}
user.setPassword( newPassword );
final long now = System.currentTimeMillis();
user.setPasswordExpires( new Long(now + pass_expiration_ms) );
EucalyptusManagement.commitWebUser( user );
return "Password has been changed";
}
public String updateUserRecord (String sessionId, UserInfoWeb newRecord )
throws SerializableException
{
/* perform full checks */
SessionInfo session = verifySession (sessionId);
UserInfoWeb callerRecord = verifyUser (session, session.getUserId(), true);
String userName = newRecord.getUserName();
UserInfoWeb oldRecord;
try {
oldRecord = EucalyptusManagement.getWebUser(userName);
} catch (Exception e) {
throw new SerializableException("Login incorrect");
}
if (! callerRecord.isAdministrator()
&& ! callerRecord.getUserName().equals(userName)) {
throw new SerializableException ("Operation restricted to owner and administrator");
}
// only an admin should be able to change this settings
if (callerRecord.isAdministrator()) {
// set password and expiration for admin when logging in for the first time
if (oldRecord.getEmail().equalsIgnoreCase(UserInfo.BOGUS_ENTRY)) {
long now = System.currentTimeMillis();
oldRecord.setPasswordExpires( new Long(now + pass_expiration_ms) );
oldRecord.setPassword (newRecord.getPassword());
}
// admin can reset pwd of another user, but
// to reset his own password he has to use
// "change password" functionality
if(!callerRecord.getUserName().equals(userName))
oldRecord.setPassword (newRecord.getPassword());
if(oldRecord.isAdministrator() != newRecord.isAdministrator())
oldRecord.setAdministrator(newRecord.isAdministrator());
if(oldRecord.isEnabled() != newRecord.isEnabled())
oldRecord.setEnabled(newRecord.isEnabled( ));
// once confirmed, cannot be unconfirmed; also, confirmation implies approval and enablement
if (!oldRecord.isConfirmed() && newRecord.isConfirmed()) {
oldRecord.setConfirmed(true);
oldRecord.setEnabled(true);
oldRecord.setApproved(true);
}
}
oldRecord.setRealName (newRecord.getRealName());
oldRecord.setEmail (newRecord.getEmail());
oldRecord.setTelephoneNumber (newRecord.getTelephoneNumber());
oldRecord.setAffiliation (newRecord.getAffiliation());
oldRecord.setProjectDescription (newRecord.getProjectDescription());
oldRecord.setProjectPIName (newRecord.getProjectPIName());
EucalyptusManagement.commitWebUser( oldRecord );
return "Account of user '" + userName + "' was updated";
}
public List<ClusterInfoWeb> getClusterList(String sessionId) throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
try {
return RemoteInfoHandler.getClusterList();
} catch ( EucalyptusCloudException e ) {
throw new SerializableException( e.getMessage( ) );
}
}
public void setClusterList(String sessionId, List<ClusterInfoWeb> clusterList ) throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
try {
RemoteInfoHandler.setClusterList( clusterList );
} catch ( EucalyptusCloudException e ) {
throw new SerializableException( e.getMessage( ) );
}
}
public List<StorageInfoWeb> getStorageList(String sessionId) throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
try {
return RemoteInfoHandler.getStorageList();
} catch(EucalyptusCloudException e) {
throw new SerializableException(e.getMessage());
}
}
public void setStorageList(String sessionId, List<StorageInfoWeb> storageList ) throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
try {
RemoteInfoHandler.setStorageList(storageList);
} catch(EucalyptusCloudException e) {
throw new SerializableException(e.getMessage());
}
}
public List<WalrusInfoWeb> getWalrusList(String sessionId) throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
try {
return RemoteInfoHandler.getWalrusList();
} catch(EucalyptusCloudException e) {
throw new SerializableException(e.getMessage());
}
}
public void setWalrusList(String sessionId, List<WalrusInfoWeb> walrusList ) throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
try {
RemoteInfoHandler.setWalrusList(walrusList);
} catch(EucalyptusCloudException e) {
throw new SerializableException(e.getMessage());
}
}
public SystemConfigWeb getSystemConfig( final String sessionId ) throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
return EucalyptusManagement.getSystemConfig();
}
public void setSystemConfig( final String sessionId, final SystemConfigWeb systemConfig ) throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
EucalyptusManagement.setSystemConfig(systemConfig);
}
public List<VmTypeWeb> getVmTypes( final String sessionId ) throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
return RemoteInfoHandler.getVmTypes();
}
public void setVmTypes( final String sessionId, final List<VmTypeWeb> vmTypes ) throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
RemoteInfoHandler.setVmTypes(vmTypes);
}
public CloudInfoWeb getCloudInfo(final String sessionId, final boolean setExternalHostPort) throws SerializableException
{
SessionInfo session = verifySession (sessionId);
UserInfoWeb user = verifyUser (session, session.getUserId(), true);
return EucalyptusManagement.getCloudInfo(setExternalHostPort);
}
private static List<DownloadsWeb> getDownloadsFromUrl(final String downloadsUrl) {
List<DownloadsWeb> downloadsList = new ArrayList<DownloadsWeb>();
HttpClient httpClient = new HttpClient();
//support for http proxy
if(HttpServerBootstrapper.httpProxyHost != null && (HttpServerBootstrapper.httpProxyHost.length() > 0)) {
String proxyHost = HttpServerBootstrapper.httpProxyHost;
if(HttpServerBootstrapper.httpProxyPort != null && (HttpServerBootstrapper.httpProxyPort.length() > 0)) {
int proxyPort = Integer.parseInt(HttpServerBootstrapper.httpProxyPort);
httpClient.getHostConfiguration().setProxy(proxyHost, proxyPort);
} else {
httpClient.getHostConfiguration().setProxyHost(new ProxyHost(proxyHost));
}
}
GetMethod method = new GetMethod(downloadsUrl);
Integer timeoutMs = new Integer(3 * 1000);
method.getParams().setSoTimeout(timeoutMs);
try {
httpClient.executeMethod(method);
String str = "";
InputStream in = method.getResponseBodyAsStream();
byte[] readBytes = new byte[1024];
int bytesRead = -1;
while((bytesRead = in.read(readBytes)) > 0) {
str += new String(readBytes, 0, bytesRead);
}
String entries[] = str.split("[\\r\\n]+");
for (int i=0; i<entries.length; i++) {
String entry[] = entries[i].split("\\t");
if (entry.length == 3) {
downloadsList.add (new DownloadsWeb(entry[0], entry[1], entry[2]));
}
}
} catch (MalformedURLException e) {
LOG.warn("Malformed URL exception: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
LOG.warn("I/O exception: " + e.getMessage());
e.printStackTrace();
} finally {
method.releaseConnection();
}
return downloadsList;
}
public List<DownloadsWeb> getDownloads(final String sessionId, final String downloadsUrl) throws SerializableException {
SessionInfo session = verifySession(sessionId);
UserInfoWeb user = verifyUser(session, session.getUserId(), true);
return getDownloadsFromUrl(downloadsUrl);
}
private static String readFileAsString(String filePath) throws java.io.IOException {
byte[] buffer = new byte[(int) new File(filePath).length()];
BufferedInputStream f = null;
try {
f = new BufferedInputStream (new FileInputStream(filePath));
f.read(buffer);
} finally {
if (f != null) try { f.close(); } catch (IOException ignored) { }
}
return new String(buffer);
}
public String getFileContentsByPath(final String sessionId, final String path) throws SerializableException {
SessionInfo session = verifySession(sessionId);
UserInfoWeb user = verifyUser(session, session.getUserId(), true);
String realPath = BaseDirectory.HOME.toString() + "/var/run/eucalyptus/webapp/" + path;
LOG.debug("feeding contents of " + realPath);
// TODO: verify path
try {
String result = readFileAsString (realPath);
LOG.debug("read from " + realPath + " string of size " + result.length());
return result;
} catch (java.io.IOException e) {
LOG.debug("failed to feed " + realPath + " due to exception " + e.getMessage());
throw new SerializableException (e.getMessage());
}
}
/**
* Overridden to really throw Jetty RetryRequest Exception (as opposed to sending failure to client).
*
* @param caught the exception
*/
protected void doUnexpectedFailure(Throwable caught)
{
throwIfRetryRequest(caught);
super.doUnexpectedFailure(caught);
}
private static final String JETTY_RETRY_REQUEST_EXCEPTION = "org.mortbay.jetty.RetryRequest";
/**
* Throws the Jetty RetryRequest if found.
*
* @param caught the exception
*/
protected void throwIfRetryRequest(Throwable caught)
{
if (caught instanceof UnexpectedException )
{
caught = caught.getCause();
}
if (JETTY_RETRY_REQUEST_EXCEPTION.equals(caught.getClass().getName()))
{
throw (RuntimeException)caught;
}
}
}