diff --git a/SConstruct b/SConstruct index 6492bb173e..506738d5e5 100644 --- a/SConstruct +++ b/SConstruct @@ -75,6 +75,7 @@ main_env.Append(LIBPATH=[ cwd+'/src/hm', cwd+'/src/um', cwd+'/src/authm', + cwd+'/src/acl', cwd+'/src/xml', ]) @@ -204,6 +205,7 @@ build_scripts=[ 'src/hm/SConstruct', 'src/um/SConstruct', 'src/authm/SConstruct', + 'src/acl/SConstruct', 'src/xml/SConstruct', 'share/man/SConstruct' ] diff --git a/include/AclManager.h b/include/AclManager.h new file mode 100644 index 0000000000..3a12d72b24 --- /dev/null +++ b/include/AclManager.h @@ -0,0 +1,254 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#ifndef ACL_MANAGER_H_ +#define ACL_MANAGER_H_ + +#include "AuthManager.h" +#include "AclRule.h" + +#include "SqlDB.h" + +using namespace std; + +/** + * This class manages the ACL rules and the authorization engine + */ +class AclManager : public Callbackable +{ +public: + AclManager(SqlDB * _db); + + AclManager():db(0),lastOID(0) + { + pthread_mutex_init(&mutex, 0); + }; + + virtual ~AclManager(); + + /** + * Loads the ACL rule set from the DB + * @return 0 on success. + */ + int start(); + + /* ---------------------------------------------------------------------- */ + /* Rule management */ + /* ---------------------------------------------------------------------- */ + + /** + * Takes an authorization request and checks if any rule in the ACL + * authorizes the operation. + * + * @param uid The user ID requesting to be authorized + * @param user_groups Set of group IDs that the user is part of + * @param obj_type The object over which the operation will be performed + * @param obj_id The object ID + * @param obj_gid The object's group ID + * @param op The operation to be authorized + * @return true if the authorization is granted by any rule + */ + const bool authorize(int uid, + const set& user_groups, + AuthRequest::Object obj_type, + int obj_id, + int obj_gid, + AuthRequest::Operation op); + /** + * Adds a new rule to the ACL rule set + * + * @param user 64 bit ID and flags + * @param resource 64 bit ID and flags + * @param rights 64 bit flags + * @param error_str Returns the error reason, if any + * + * @return the oid assigned to the rule on success, + * -1 if the rule exists, + * -2 if the rule is malformed, + * -3 if the DB insert failed + */ + virtual int add_rule(long long user, + long long resource, + long long rights, + string& error_str); + /** + * Deletes a rule from the ACL rule set + * + * @param oid Rule id + * @param error_str Returns the error reason, if any + * @return 0 on success + */ + virtual int del_rule(int oid, string& error_str); + + /* ---------------------------------------------------------------------- */ + /* DB management */ + /* ---------------------------------------------------------------------- */ + + /** + * Bootstraps the database table(s) associated to the ACL Manager + */ + static void bootstrap(SqlDB * _db); + + /** + * Dumps the rule set in XML format. + * @param oss The output stream to dump the rule set contents + * @return 0 on success + */ + virtual int dump(ostringstream& oss); + +protected: + + // ---------------------------------------- + // ACL rules management + // ---------------------------------------- + + /** + * ACL rules. Each rule is indexed by its 'user' long long attibute, + * several rules can apply to the same user + */ + multimap acl_rules; + + /** + * Rules indexed by oid. Stores the same rules as acl_rules + */ + map acl_rules_oids; + +private: + + /** + * Gets all rules that apply to the user_req and, if any of them grants + * permission, returns true. + * + * @param user_req user/group id and flags + * @param resource_oid_req 64 bit request, ob. type and individual oid + * @param resource_gid_req 64 bit request, ob. type and group id + * @param resource_all_req 64 bit request, ob. type and all flag + * @param rights_req Requested rights + * @param individual_obj_type Mask with ob. type and individual flags + * @param group_obj_type Mask with ob. type and gropu flags + * + * @return true if any rule grants permission + */ + bool match_rules( + long long user_req, + long long resource_oid_req, + long long resource_gid_req, + long long resource_all_req, + long long rights_req, + long long individual_obj_type, + long long group_obj_type); + + // ---------------------------------------- + // Mutex synchronization + // ---------------------------------------- + + pthread_mutex_t mutex; + + /** + * Function to lock the manager + */ + void lock() + { + pthread_mutex_lock(&mutex); + }; + + /** + * Function to unlock the manager + */ + void unlock() + { + pthread_mutex_unlock(&mutex); + }; + + // ---------------------------------------- + // DataBase implementation variables + // ---------------------------------------- + + /** + * Pointer to the database. + */ + SqlDB * db; + + /** + * Last object ID assigned to a rule. + */ + int lastOID; + + /** + * Tablename for the ACL rules + */ + static const char * table; + + static const char * db_names; + + static const char * db_bootstrap; + + /** + * Inserts the last oid into the pool_control table + */ + void update_lastOID(); + + /** + * Callback function to unmarshall the ACL rules + * @param num the number of columns read from the DB + * @param names the column names + * @param vaues the column values + * @return 0 on success + */ + int select_cb(void *nil, int num, char **values, char **names); + + /** + * Reads the ACL rule set from the database. + * @param db pointer to the db + * @return 0 on success + */ + int select(); + + /** + * Inserts the ACL rule in the database. + * @param rule to insert + * @return 0 on success + */ + int insert(AclRule * rule) + { + return insert(rule, db); + }; + + /** + * Inserts the ACL rule in the database. + * @param rule to insert + * @db db pointer + * + * @return 0 on success + */ + static int insert(AclRule * rule, SqlDB * db); + + /** + * Drops an ACL rule from the database + * + * @param oid Rule id + * @return 0 on success + */ + int drop(int oid); + + /** + * Callback to set the lastOID + */ + int init_cb(void *nil, int num, char **values, char **names); +}; + +#endif /*ACL_MANAGER_H*/ + diff --git a/include/AclRule.h b/include/AclRule.h new file mode 100644 index 0000000000..4047ff57ee --- /dev/null +++ b/include/AclRule.h @@ -0,0 +1,202 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#ifndef ACL_RULE_H_ +#define ACL_RULE_H_ + +#include +#include +#include + +using namespace std; + +/** + * An ACL Rule is composed of three 64 bit numbers: user, resource and rights. + * These attributes store a combination of IDs and flags + */ +class AclRule +{ +public: + + // ------------------------------------------------------------------------ + static const long long INDIVIDUAL_ID; + + static const long long GROUP_ID; + + static const long long ALL_ID; + // ------------------------------------------------------------------------ + + AclRule(int _oid, + long long _user, + long long _resource, + long long _rights): + oid(_oid), user(_user), resource(_resource), rights(_rights) + { + build_str(); + }; + + bool operator ==(const AclRule& other) const + { + return (user == other.user && + resource == other.resource && + rights == other.rights); + }; + + /** + * Returns a human readable string for this rule + * + * @return a human readable string for this rule + */ + const string& to_str() const + { + return str; + }; + + /** + * Returns whether or not the rule is malformed. + * + * @param error_str Returns the error message, if any + * @return true if the rule is wrong + */ + bool malformed(string& error_str) const; + + /** + * Function to print the object into a string in XML format + * + * @param xml the resulting XML string + * @return a reference to the generated string + */ + string& to_xml(string& xml) const; + + /** + * Rebuilds the rule from an xml formatted string + * + * @param node xml node for the ACL rule + * @return 0 on success, -1 otherwise + */ + int from_xml(xmlNodePtr node); + + /** + * Returns the 32 less significant bits of the user long long attribute + * + * @return the user or group ID + */ + int user_id() const + { + return user; + }; + + /** + * Returns the 64 bit user attribute with the ID cleared (the 32 less + * significant bits are set to 0) + * + * @return the user flags + */ + long long user_code() const + { + return user & 0xFFFFFFFF00000000LL; + }; + + /** + * Returns the 32 less significant bits of the resource long long attribute + * + * @return the resource ID + */ + int resource_id() const + { + return resource; + }; + + /** + * Returns the 64 bit resource attribute with the ID cleared (the 32 less + * significant bits are set to 0) + * + * @return the resource flags + */ + long long resource_code() const + { + return resource & 0xFFFFFFFF00000000LL; + }; + + // ------------------------------------------------------------------------ + // Functions needed by the Scheduler ACL engine + // ------------------------------------------------------------------------ + + long long get_user() const + { + return user; + } + + long long get_oid() const + { + return oid; + } + +private: + // NONE_ID can never be used in a rule. It is useful to create masks that + // will never match any existing rule + static const long long NONE_ID; + + friend class AclManager; + + /** + * Rule unique identifier + */ + int oid; + + /** + * 64 bit integer holding a user compound: + * + * 32 bits 32 bits + * +-----------------------+-----------------------+ + * | Type (user,group,all) | user/group ID | + * +-----------------------+-----------------------+ + */ + long long user; + + /** + * 64 bit integer holding a resource compound + * + * 32 bits 32 bits + * +-----------------------+-----------------------+ + * | Type (VM, Host...) | resource ID | + * +-----------------------+-----------------------+ + */ + long long resource; + + /** + * 64 bit integer containing the rights flags + * + * 64 bits + * +-----------------------------------------------+ + * | Actions (MANAGE, CREATE, USE, DELETE... | + * +-----------------------------------------------+ + */ + long long rights; + + /** + * Human readable representation of the rule + */ + string str; + + /** + * Builds the human representation of the ACL + */ + void build_str(); +}; + +#endif /*ACL_RULE_H*/ + diff --git a/include/AuthManager.h b/include/AuthManager.h index 106497d2ee..d86d984f22 100644 --- a/include/AuthManager.h +++ b/include/AuthManager.h @@ -18,6 +18,7 @@ #define AUTH_MANAGER_H_ #include +#include #include "MadManager.h" #include "ActionManager.h" @@ -259,10 +260,11 @@ private: class AuthRequest : public ActionListener { public: - AuthRequest(int _uid): + AuthRequest(int _uid, set _gids): result(false), timeout(false), uid(_uid), + gids(_gids), time_out(0), self_authorize(true) { @@ -276,15 +278,32 @@ public: */ enum Operation { - CREATE, /** Authorization to create an object */ - DELETE, /** Authorization to delete an object */ - USE, /** Authorization to use an object */ - MANAGE, /** Authorization to manage an object */ - INFO, /** Authorization to view an object */ - INFO_POOL, /** Authorization to view any object in the pool */ - INFO_POOL_MINE, /** Authorization to view user and/or group objects */ - INSTANTIATE, /** Authorization to instantiate a VM from a TEMPLATE */ - CHOWN /** Authorization to change ownership of an object */ + CREATE = 0x1LL, /**< Auth. to create an object */ + DELETE = 0x2LL, /**< Auth. to delete an object */ + USE = 0x4LL, /**< Auth. to use an object */ + MANAGE = 0x8LL, /**< Auth. to manage an object */ + INFO = 0x10LL, /**< Auth. to view an object */ + INFO_POOL = 0x20LL, /**< Auth. to view any object in the pool */ + INFO_POOL_MINE= 0x40LL, /**< Auth. to view user and/or group objects */ + INSTANTIATE = 0x80LL, /**< Auth. to instantiate a VM from a TEMPLATE*/ + CHOWN = 0x100LL /**< Auth. to change ownership of an object */ + }; + + static string Operation_to_str(Operation op) + { + switch (op) + { + case CREATE: return "CREATE"; + case DELETE: return "DELETE"; + case USE: return "USE"; + case MANAGE: return "MANAGE"; + case INFO: return "INFO"; + case INFO_POOL: return "INFO_POOL"; + case INFO_POOL_MINE: return "INFO_POOL_MINE"; + case INSTANTIATE: return "INSTANTIATE"; + case CHOWN: return "CHOWN"; + default: return ""; + } }; /** @@ -292,13 +311,29 @@ public: */ enum Object { - VM, - HOST, - NET, - IMAGE, - USER, - TEMPLATE, - GROUP + VM = 0x0000001000000000LL, + HOST = 0x0000002000000000LL, + NET = 0x0000004000000000LL, + IMAGE = 0x0000008000000000LL, + USER = 0x0000010000000000LL, + TEMPLATE = 0x0000020000000000LL, + GROUP = 0x0000040000000000LL, + ACL = 0x0000080000000000LL + }; + + static string Object_to_str(Object ob) + { + switch (ob) + { + case VM: return "VM" ; break; + case HOST: return "HOST" ; break; + case NET: return "NET" ; break; + case IMAGE: return "IMAGE" ; break; + case USER: return "USER" ; break; + case TEMPLATE: return "TEMPLATE" ; break; + case GROUP: return "GROUP" ; break; + default: return ""; + } }; /** @@ -328,6 +363,7 @@ public: */ void add_auth(Object ob, const string& ob_id, + int ob_gid, Operation op, int owner, bool pub); @@ -337,6 +373,7 @@ public: */ void add_auth(Object ob, int ob_id, + int ob_gid, Operation op, int owner, bool pub) @@ -344,7 +381,7 @@ public: ostringstream oss; oss << ob_id; - add_auth(ob,oss.str(),op,owner,pub); + add_auth(ob,oss.str(),ob_gid,op,owner,pub); }; /** @@ -425,6 +462,11 @@ private: */ int uid; + /** + * The user groups ID set + */ + set gids; + /** * Timeout for this request */ diff --git a/include/Group.h b/include/Group.h index 860d883e87..f732811b33 100644 --- a/include/Group.h +++ b/include/Group.h @@ -78,7 +78,7 @@ private: // ************************************************************************* Group(int id, const string& name): - PoolObjectSQL(id,name,-1,-1,table), + PoolObjectSQL(id,name,-1,-1,"","",table), ObjectCollection("USERS"){}; virtual ~Group(){}; diff --git a/include/Image.h b/include/Image.h index ad40595233..6c60ac4ab5 100644 --- a/include/Image.h +++ b/include/Image.h @@ -332,6 +332,8 @@ protected: Image(int uid, int gid, + const string& uname, + const string& gname, ImageTemplate* img_template); virtual ~Image(); diff --git a/include/ImagePool.h b/include/ImagePool.h index 6a5df81d8c..1c192997ec 100644 --- a/include/ImagePool.h +++ b/include/ImagePool.h @@ -48,6 +48,8 @@ public: * Function to allocate a new Image object * @param uid the user id of the image's owner * @param gid the id of the group this object is assigned to + * @param uname name of the user + * @param gname name of the group * @param img_template template associated with the image * @param oid the id assigned to the Image * @param error_str Returns the error reason, if any @@ -58,6 +60,8 @@ public: int allocate ( int uid, int gid, + const string& uname, + const string& gname, ImageTemplate * img_template, int * oid, string& error_str); @@ -172,7 +176,7 @@ private: */ PoolObjectSQL * create() { - return new Image(-1,-1,0); + return new Image(-1,-1,"","",0); }; }; diff --git a/include/Nebula.h b/include/Nebula.h index 300e5978c5..cf0b81696b 100644 --- a/include/Nebula.h +++ b/include/Nebula.h @@ -36,6 +36,7 @@ #include "RequestManager.h" #include "HookManager.h" #include "AuthManager.h" +#include "AclManager.h" #include "ImageManager.h" #include "Callbackable.h" @@ -134,6 +135,11 @@ public: return imagem; }; + AclManager * get_aclm() + { + return aclm; + }; + // -------------------------------------------------------------- // Environment & Configuration // -------------------------------------------------------------- @@ -249,7 +255,7 @@ private: Nebula():nebula_configuration(0),db(0),vmpool(0),hpool(0),vnpool(0), upool(0),ipool(0),gpool(0),tpool(0),lcm(0),vmm(0),im(0),tm(0), - dm(0),rm(0),hm(0),authm(0),imagem(0) + dm(0),rm(0),hm(0),authm(0),aclm(0),imagem(0) { const char * nl = getenv("ONE_LOCATION"); @@ -357,6 +363,11 @@ private: delete authm; } + if ( aclm != 0) + { + delete aclm; + } + if ( imagem != 0) { delete imagem; @@ -423,6 +434,7 @@ private: RequestManager * rm; HookManager * hm; AuthManager * authm; + AclManager * aclm; ImageManager * imagem; // --------------------------------------------------------------- diff --git a/include/ObjectXML.h b/include/ObjectXML.h index 72fa91d878..9be989618b 100644 --- a/include/ObjectXML.h +++ b/include/ObjectXML.h @@ -124,6 +124,20 @@ public: */ int get_nodes(const char * xpath_expr, vector& content); + /** + * Frees a vector of XMLNodes, as returned by the get_nodes function + * @param content the vector of xmlNodePtr + */ + void free_nodes(vector& content) + { + vector::iterator it; + + for (it = content.begin(); it < content.end(); it++) + { + xmlFreeNode(*it); + } + }; + /** * Updates the object representation with a new XML document. Previous * XML resources are freed diff --git a/include/PoolObjectSQL.h b/include/PoolObjectSQL.h index 58794da920..36df15280e 100644 --- a/include/PoolObjectSQL.h +++ b/include/PoolObjectSQL.h @@ -36,11 +36,25 @@ using namespace std; class PoolObjectSQL : public ObjectSQL, public ObjectXML { public: - - PoolObjectSQL(int id, const string& _name, int _uid, - int _gid, const char *_table) - :ObjectSQL(),ObjectXML(),oid(id),name(_name),uid(_uid),gid(_gid), - valid(true),public_obj(0),obj_template(0),table(_table) + PoolObjectSQL(int id, + const string& _name, + int _uid, + int _gid, + const string& _uname, + const string& _gname, + const char * _table) + :ObjectSQL(), + ObjectXML(), + oid(id), + name(_name), + uid(_uid), + gid(_gid), + uname(_uname), + gname(_gname), + valid(true), + public_obj(0), + obj_template(0), + table(_table) { pthread_mutex_init(&mutex,0); }; @@ -84,21 +98,25 @@ public: }; /** - * Changes the object's owner id + * Changes the object's owner * @param _uid New User ID + * @param _uname Name of the new user */ - void set_uid(int _uid) + void set_user(int _uid, const string& _uname) { - uid = _uid; + uid = _uid; + uname = _uname; } /** * Changes the object's group id * @param _gid New Group ID + * @param _gname Name of the new group */ - void set_gid(int _gid) + void set_group(int _gid, const string& _gname) { - gid = _gid; + gid = _gid; + gname = _gname; }; /* --------------------------------------------------------------------- */ @@ -360,6 +378,16 @@ protected: */ int gid; + /** + * Name of the object's owner, empty if owner is not used + */ + string uname; + + /** + * Name of the object's group,, empty if group is not used + */ + string gname; + /** * The contents of this object are valid */ diff --git a/include/Request.h b/include/Request.h index 25a21811f4..c4cb137a02 100644 --- a/include/Request.h +++ b/include/Request.h @@ -59,17 +59,20 @@ protected: /* ------------------- Attributes of the Request ---------------------- */ - int uid; /**< id of the user performing the request */ - + int uid; /**< id of the user */ int gid; /**< id of the user's group */ + string uname; /**< name of the user */ + string gname; /**< name of the user's group */ + + set group_ids; /**< set of user's group ids */ + PoolSQL * pool; /**< Pool of objects */ string method_name; /**< The name of the XML-RPC method */ AuthRequest::Object auth_object; /**< Auth object for the request */ - - AuthRequest::Operation auth_op; /**< Auth operation for the request */ + AuthRequest::Operation auth_op; /**< Auth operation for the request */ /* -------------------- Constructors ---------------------------------- */ @@ -88,15 +91,32 @@ protected: /* -------------------------------------------------------------------- */ /** - * Performs a basic autorization for this request using the uid/gid + * Performs a basic authorization for this request using the uid/gid * from the request. The function gets the object from the pool to get * the public attribute and its owner. The authorization is based on * object and type of operation for the request. - * @param oid of the object. + * @param oid of the object, can be -1 for objects to be created, or + * pools. * * @return true if the user is authorized. */ - bool basic_authorization(int oid); + bool basic_authorization(int oid) + { + return basic_authorization(oid, auth_op); + }; + + /** + * Performs a basic authorization for this request using the uid/gid + * from the request. The function gets the object from the pool to get + * the public attribute and its owner. The authorization is based on + * object and type of operation for the request. + * @param oid of the object, can be -1 for objects to be created, or + * pools. + * @param op operation of the request. + * + * @return true if the user is authorized. + */ + bool basic_authorization(int oid, AuthRequest::Operation op); /** * Actual Execution method for the request. Must be implemented by the diff --git a/include/RequestManagerAcl.h b/include/RequestManagerAcl.h new file mode 100644 index 0000000000..fdb75e6539 --- /dev/null +++ b/include/RequestManagerAcl.h @@ -0,0 +1,110 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#ifndef REQUEST_MANAGER_ACL_H +#define REQUEST_MANAGER_ACL_H + +#include "Request.h" +#include "Nebula.h" + +using namespace std; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class RequestManagerAcl: public Request +{ +protected: + RequestManagerAcl( const string& method_name, + const string& help, + const string& params) + :Request(method_name,params,help) + { + auth_object = AuthRequest::ACL; + auth_op = AuthRequest::MANAGE; + + Nebula& nd = Nebula::instance(); + aclm = nd.get_aclm(); + }; + + ~RequestManagerAcl(){}; + + /* -------------------------------------------------------------------- */ + + virtual void request_execute(xmlrpc_c::paramList const& _paramList) = 0; + + /* -------------------------------------------------------------------- */ + + AclManager * aclm; +}; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class AclAddRule : public RequestManagerAcl +{ +public: + AclAddRule(): + RequestManagerAcl("AclAddRule", + "Adds a new ACL rule", + "A:ssss") + {}; + + ~AclAddRule(){}; + + void request_execute(xmlrpc_c::paramList const& _paramList); +}; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class AclDelRule : public RequestManagerAcl +{ +public: + AclDelRule(): + RequestManagerAcl("AclDelRule", + "Deletes an existing ACL rule", + "A:si") + {}; + + ~AclDelRule(){}; + + void request_execute(xmlrpc_c::paramList const& _paramList); +}; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class AclInfo: public RequestManagerAcl +{ +public: + AclInfo(): + RequestManagerAcl("AclInfo", + "Returns the ACL rule set", + "A:s") + {}; + + ~AclInfo(){}; + + void request_execute(xmlrpc_c::paramList const& _paramList); +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +#endif diff --git a/include/RequestManagerAllocate.h b/include/RequestManagerAllocate.h index 15d2f975cf..1106e31df7 100644 --- a/include/RequestManagerAllocate.h +++ b/include/RequestManagerAllocate.h @@ -104,7 +104,7 @@ class VirtualNetworkAllocate: public RequestManagerAllocate { public: VirtualNetworkAllocate(): - RequestManagerAllocate("VirtualNetworkInfo", + RequestManagerAllocate("VirtualNetworkAllocate", "Allocates a new virtual network", "A:ss", true) @@ -200,7 +200,7 @@ class HostAllocate : public RequestManagerAllocate { public: HostAllocate(): - RequestManagerAllocate("HostInfo", + RequestManagerAllocate("HostAllocate", "Allocates a new host", "A:sssss", false) @@ -225,7 +225,7 @@ class UserAllocate: public RequestManagerAllocate { public: UserAllocate(): - RequestManagerAllocate("UserInfo", + RequestManagerAllocate("UserAllocate", "Returns user information", "A:sss", false) diff --git a/include/RequestManagerInfo.h b/include/RequestManagerInfo.h index cc91e86ee3..a9416f68b9 100644 --- a/include/RequestManagerInfo.h +++ b/include/RequestManagerInfo.h @@ -66,6 +66,14 @@ public: }; ~VirtualMachineInfo(){}; + + /* -------------------------------------------------------------------- */ + + void to_xml(PoolObjectSQL * object, string& str) + { + VirtualMachine * vm = static_cast(object); + vm->to_xml_extended(str); + }; }; /* ------------------------------------------------------------------------- */ diff --git a/include/RequestManagerPoolInfoFilter.h b/include/RequestManagerPoolInfoFilter.h index 102d24845d..738c05c6b6 100644 --- a/include/RequestManagerPoolInfoFilter.h +++ b/include/RequestManagerPoolInfoFilter.h @@ -31,8 +31,9 @@ class RequestManagerPoolInfoFilter: public Request { protected: RequestManagerPoolInfoFilter(const string& method_name, - const string& help) - :Request(method_name,"A:si",help) + const string& help, + const string& signature) + :Request(method_name,signature,help) { auth_op = AuthRequest::INFO_POOL; }; @@ -41,9 +42,9 @@ protected: /* -------------------------------------------------------------------- */ - static const int ALL; /**< Secify all objects in the pool (-2) */ - static const int MINE; /**< Secify user's objects in the pool (-3)*/ - static const int MINE_GROUP; /**< Secify users + group objects (-1) */ + static const int ALL; /**< Specify all objects in the pool (-2) */ + static const int MINE; /**< Specify user's objects in the pool (-3)*/ + static const int MINE_GROUP; /**< Specify users + group objects (-1) */ /* -------------------------------------------------------------------- */ @@ -56,9 +57,17 @@ protected: class VirtualMachinePoolInfo : public RequestManagerPoolInfoFilter { public: + /* -------------------------------------------------------------------- */ + + static const int ALL_VM; /**< VMs in any state (-2) */ + static const int NOT_DONE; /**< VMs in any state expect DONE (-1)*/ + + /* -------------------------------------------------------------------- */ + VirtualMachinePoolInfo(): RequestManagerPoolInfoFilter("VirtualMachinePoolInfo", - "Returns the virtual machine instances pool") + "Returns the virtual machine instances pool", + "A:siiii") { Nebula& nd = Nebula::instance(); pool = nd.get_vmpool(); @@ -76,7 +85,8 @@ class TemplatePoolInfo : public RequestManagerPoolInfoFilter public: TemplatePoolInfo(): RequestManagerPoolInfoFilter("TemplatePoolInfo", - "Returns the virtual machine template pool") + "Returns the virtual machine template pool", + "A:siii") { Nebula& nd = Nebula::instance(); pool = nd.get_tpool(); @@ -89,13 +99,13 @@ public: /* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ - class VirtualNetworkPoolInfo: public RequestManagerPoolInfoFilter { public: VirtualNetworkPoolInfo(): RequestManagerPoolInfoFilter("VirtualNetworkPoolInfo", - "Returns the virtual network pool") + "Returns the virtual network pool", + "A:siii") { Nebula& nd = Nebula::instance(); pool = nd.get_vnpool(); @@ -113,7 +123,8 @@ class ImagePoolInfo: public RequestManagerPoolInfoFilter public: ImagePoolInfo(): RequestManagerPoolInfoFilter("ImagePoolInfo", - "Returns the image pool") + "Returns the image pool", + "A:siii") { Nebula& nd = Nebula::instance(); pool = nd.get_ipool(); @@ -123,7 +134,6 @@ public: ~ImagePoolInfo(){}; }; - /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/include/User.h b/include/User.h index ee57ed35ba..b7b94775ee 100644 --- a/include/User.h +++ b/include/User.h @@ -189,11 +189,16 @@ protected: // Constructor // ************************************************************************* - User(int id, int _gid, const string& _username, const string& _password, bool _enabled): - PoolObjectSQL(id,_username,-1,_gid,table), + User(int id, + int _gid, + const string& _uname, + const string& _gname, + const string& _password, + bool _enabled): + PoolObjectSQL(id,_uname,-1,_gid,"",_gname,table), ObjectCollection("GROUPS"), - password(_password), enabled(_enabled) - { }; + password(_password), + enabled(_enabled){}; virtual ~User(){}; diff --git a/include/UserPool.h b/include/UserPool.h index 5154067aa4..e543e65cde 100644 --- a/include/UserPool.h +++ b/include/UserPool.h @@ -51,8 +51,9 @@ public: int allocate ( int * oid, int gid, - string username, - string password, + const string& uname, + const string& gname, + const string& password, bool enabled, string& error_str); @@ -102,10 +103,17 @@ public: * @param session, colon separated username and password string * @param uid of the user if authN succeeded -1 otherwise * @param gid of the user if authN succeeded -1 otherwise + * @param uname of the user if authN succeeded "" otherwise + * @param gname of the group if authN succeeded "" otherwise + * @param group_ids the user groups if authN succeeded, is empty otherwise * @return false if authn failed, true otherwise */ - bool authenticate(const string& session, int& uid, int& gid); - + bool authenticate(const string& session, + int& uid, + int& gid, + string& uname, + string& gname, + set& group_ids); /** * Returns whether there is a user with given username/password or not * @param ar, an Authorization Request @@ -133,7 +141,7 @@ private: */ PoolObjectSQL * create() { - return new User(-1,-1,"","",true); + return new User(-1,-1,"","","",true); }; }; diff --git a/include/VMTemplate.h b/include/VMTemplate.h index b47a6fd705..819eb29c01 100644 --- a/include/VMTemplate.h +++ b/include/VMTemplate.h @@ -129,7 +129,11 @@ protected: // ************************************************************************* // Constructor // ************************************************************************* - VMTemplate(int id, int uid, int gid, + VMTemplate(int id, + int uid, + int gid, + const string& uname, + const string& gname, VirtualMachineTemplate * _template_contents); ~VMTemplate(); diff --git a/include/VMTemplatePool.h b/include/VMTemplatePool.h index 4ebb5a0280..0b66952bfd 100644 --- a/include/VMTemplatePool.h +++ b/include/VMTemplatePool.h @@ -44,6 +44,8 @@ public: */ int allocate(int uid, int gid, + const string& uname, + const string& gname, VirtualMachineTemplate * template_contents, int * oid, string& error_str); @@ -109,26 +111,13 @@ public: }; private: - //-------------------------------------------------------------------------- - // Configuration Attributes for Images - // ------------------------------------------------------------------------- - - // TODO - - //-------------------------------------------------------------------------- - // Pool Attributes - // ------------------------------------------------------------------------- - - // TODO - - /** * Factory method to produce Image objects * @return a pointer to the new Image */ PoolObjectSQL * create() { - return new VMTemplate(-1,-1,-1,0); + return new VMTemplate(-1,-1,-1,"","",0); }; }; diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index ff0e436ac5..857f9623bc 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -124,6 +124,14 @@ public: */ string& to_xml(string& xml) const; + /** + * Function to print the VirtualMachine object into a string in + * XML format, with extended information (full history records) + * @param xml the resulting XML string + * @return a reference to the generated string + */ + string& to_xml_extended(string& xml) const; + /** * Rebuilds the object from an xml formatted string * @param xml_str The xml-formatted string @@ -752,6 +760,12 @@ private: */ History * previous_history; + + /** + * Complete set of history records for the VM + */ + vector history_records; + // ------------------------------------------------------------------------- // Logging // ------------------------------------------------------------------------- @@ -859,14 +873,27 @@ private: */ void parse_graphics(); + /** + * Function that renders the VM in XML format optinally including + * extended information (all history records) + * @param xml the resulting XML string + * @param extended include additional info if true + * @return a reference to the generated string + */ + string& to_xml_extended(string& xml, bool extended) const; + protected: //************************************************************************** // Constructor //************************************************************************** - VirtualMachine(int id, int uid, - int gid, VirtualMachineTemplate * _vm_template); + VirtualMachine(int id, + int uid, + int gid, + const string& uname, + const string& gname, + VirtualMachineTemplate * _vm_template); virtual ~VirtualMachine(); diff --git a/include/VirtualMachinePool.h b/include/VirtualMachinePool.h index f84723259c..44edfc2ca2 100644 --- a/include/VirtualMachinePool.h +++ b/include/VirtualMachinePool.h @@ -52,6 +52,8 @@ public: int allocate ( int uid, int gid, + const string& uname, + const string& gname, VirtualMachineTemplate * vm_template, int * oid, string& error_str, @@ -137,22 +139,9 @@ public: */ int dump(ostringstream& oss, const string& where) { - return dump(oss, -1, where); + return PoolSQL::dump(oss, "VM_POOL", VirtualMachine::table, where); } - /** - * Dumps the VM pool in XML format. A filter can be also added to the query - * Also the hostname where the VirtualMachine is running is added to the - * pool - * @param oss the output stream to dump the pool contents - * @param where filter for the objects, defaults to all - * @param state include only VMs in this state. -1 means any state, - * except DONE - * - * @return 0 on success - */ - int dump(ostringstream& oss, int state, const string& where); - private: /** * Factory method to produce VM objects @@ -160,7 +149,7 @@ private: */ PoolObjectSQL * create() { - return new VirtualMachine(-1,-1,-1,0); + return new VirtualMachine(-1,-1,-1,"","",0); }; }; diff --git a/include/VirtualNetwork.h b/include/VirtualNetwork.h index a6d056854a..dc78655908 100644 --- a/include/VirtualNetwork.h +++ b/include/VirtualNetwork.h @@ -269,6 +269,8 @@ private: VirtualNetwork(int uid, int gid, + const string& _uname, + const string& _gname, VirtualNetworkTemplate * _vn_template = 0); ~VirtualNetwork(); diff --git a/include/VirtualNetworkPool.h b/include/VirtualNetworkPool.h index 1f6f2d3724..ff0fe2aed6 100644 --- a/include/VirtualNetworkPool.h +++ b/include/VirtualNetworkPool.h @@ -51,6 +51,8 @@ public: int allocate ( int uid, int gid, + const string& uname, + const string& gname, VirtualNetworkTemplate * vn_template, int * oid, string& error_str); @@ -156,7 +158,7 @@ private: */ PoolObjectSQL * create() { - return new VirtualNetwork(-1,-1, 0); + return new VirtualNetwork(-1,-1,"","",0); }; }; diff --git a/include/test/NebulaTest.h b/include/test/NebulaTest.h index d8800d58f1..53bd573d40 100644 --- a/include/test/NebulaTest.h +++ b/include/test/NebulaTest.h @@ -35,6 +35,7 @@ #include "RequestManager.h" #include "HookManager.h" #include "AuthManager.h" +#include "AclManager.h" #include "ImageManager.h" class NebulaTest @@ -49,7 +50,7 @@ protected: need_im(false), need_tm(false), need_lcm(false), need_dm(false), need_rm(false), need_hm(false), - need_authm(false), need_imagem(false) + need_authm(false), need_aclm(false), need_imagem(false) {}; virtual ~NebulaTest(){}; @@ -75,6 +76,7 @@ public: bool need_rm; bool need_hm; bool need_authm; + bool need_aclm; bool need_imagem; static NebulaTest * instance() @@ -132,6 +134,8 @@ public: virtual AuthManager* create_authm(time_t timer_period); + virtual AclManager* create_aclm(SqlDB* db); + virtual ImageManager* create_imagem(ImagePool * ipool); }; diff --git a/install.sh b/install.sh index 0a4a446ba7..3f3b6592db 100755 --- a/install.sh +++ b/install.sh @@ -377,6 +377,7 @@ BIN_FILES="src/nebula/oned \ src/cli/oneimage \ src/cli/onegroup \ src/cli/onetemplate \ + src/cli/oneacl \ src/onedb/onedb \ share/scripts/one \ src/authm_mad/oneauth" @@ -673,6 +674,8 @@ RUBY_OPENNEBULA_LIB_FILES="src/oca/ruby/OpenNebula/Host.rb \ src/oca/ruby/OpenNebula/TemplatePool.rb \ src/oca/ruby/OpenNebula/Group.rb \ src/oca/ruby/OpenNebula/GroupPool.rb \ + src/oca/ruby/OpenNebula/Acl.rb \ + src/oca/ruby/OpenNebula/AclPool.rb \ src/oca/ruby/OpenNebula/XMLUtils.rb" #------------------------------------------------------------------------------- @@ -768,7 +771,8 @@ ONE_CLI_LIB_FILES="src/cli/one_helper/onegroup_helper.rb \ src/cli/one_helper/onetemplate_helper.rb \ src/cli/one_helper/oneuser_helper.rb \ src/cli/one_helper/onevm_helper.rb \ - src/cli/one_helper/onevnet_helper.rb" + src/cli/one_helper/onevnet_helper.rb \ + src/cli/one_helper/oneacl_helper.rb" CLI_BIN_FILES="src/cli/onevm \ src/cli/onehost \ @@ -776,7 +780,8 @@ CLI_BIN_FILES="src/cli/onevm \ src/cli/oneuser \ src/cli/oneimage \ src/cli/onetemplate \ - src/cli/onegroup" + src/cli/onegroup \ + src/cli/oneacl" CLI_CONF_FILES="src/cli/etc/onegroup.yaml \ src/cli/etc/onehost.yaml \ @@ -784,7 +789,8 @@ CLI_CONF_FILES="src/cli/etc/onegroup.yaml \ src/cli/etc/onetemplate.yaml \ src/cli/etc/oneuser.yaml \ src/cli/etc/onevm.yaml \ - src/cli/etc/onevnet.yaml" + src/cli/etc/onevnet.yaml \ + src/cli/etc/oneacl.yaml" #----------------------------------------------------------------------------- # Sunstone files diff --git a/src/acl/AclManager.cc b/src/acl/AclManager.cc new file mode 100644 index 0000000000..cb35403267 --- /dev/null +++ b/src/acl/AclManager.cc @@ -0,0 +1,629 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#include + +#include "AclManager.h" +#include "NebulaLog.h" +#include "GroupPool.h" + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +const char * AclManager::table = "acl"; + +const char * AclManager::db_names = "oid, user, resource, rights"; + +const char * AclManager::db_bootstrap = "CREATE TABLE IF NOT EXISTS " + "acl (oid INT PRIMARY KEY, user BIGINT, resource BIGINT, rights BIGINT)"; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int AclManager::init_cb(void *nil, int num, char **values, char **names) +{ + lastOID = -1; + + if ( values[0] != 0 ) + { + lastOID = atoi(values[0]); + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +AclManager::AclManager(SqlDB * _db) : db(_db), lastOID(-1) +{ + ostringstream oss; + + pthread_mutex_init(&mutex, 0); + + set_callback(static_cast (&AclManager::init_cb)); + + oss << "SELECT last_oid FROM pool_control WHERE tablename='" << table + << "'"; + + db->exec(oss, this); + + unset_callback(); + + if (lastOID == -1) + { + // Add a default rule + // @1 VM+NET+IMAGE+TEMPLATE/* CREATE+INFO_POOL_MINE + string error_str; + add_rule(0x200000001LL, 0x2d400000000LL, 0x41LL, error_str); + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int AclManager::start() +{ + return select(); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +AclManager::~AclManager() +{ + multimap::iterator it; + + lock(); + + for ( it = acl_rules.begin(); it != acl_rules.end(); it++ ) + { + delete it->second; + } + + unlock(); + + pthread_mutex_destroy(&mutex); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +const bool AclManager::authorize( + int uid, + const set& user_groups, + AuthRequest::Object obj_type, + int obj_id, + int obj_gid, + AuthRequest::Operation op) +{ + ostringstream oss; + + multimap::iterator it; + pair::iterator, + multimap::iterator> index; + + bool auth = false; + + // Build masks for request + long long user_req; + long long resource_oid_req; + + if ( obj_id >= 0 ) + { + resource_oid_req = obj_type | AclRule::INDIVIDUAL_ID | obj_id; + } + else + { + resource_oid_req = AclRule::NONE_ID; + } + + long long resource_gid_req; + + if ( obj_gid >= 0 ) + { + resource_gid_req = obj_type | AclRule::GROUP_ID | obj_gid; + } + else + { + resource_gid_req = AclRule::NONE_ID; + } + + long long resource_all_req = obj_type | AclRule::ALL_ID; + long long rights_req = op; + + long long resource_oid_mask = + ( obj_type | AclRule::INDIVIDUAL_ID | 0x00000000FFFFFFFFLL ); + + long long resource_gid_mask = + ( obj_type | AclRule::GROUP_ID | 0x00000000FFFFFFFFLL ); + + + // Create a temporal rule, to log the request + long long log_resource; + + if ( obj_id >= 0 ) + { + log_resource = resource_oid_req; + } + else if ( obj_gid >= 0 ) + { + log_resource = resource_gid_req; + } + else + { + log_resource = resource_all_req; + } + + AclRule log_rule(-1, + AclRule::INDIVIDUAL_ID | uid, + log_resource, + rights_req); + + oss << "Request " << log_rule.to_str(); + NebulaLog::log("ACL",Log::DEBUG,oss); + + // --------------------------------------------------- + // Look for rules that apply to everyone + // --------------------------------------------------- + + user_req = AclRule::ALL_ID; + auth = match_rules(user_req, + resource_oid_req, + resource_gid_req, + resource_all_req, + rights_req, + resource_oid_mask, + resource_gid_mask); + if ( auth == true ) + { + return true; + } + + // --------------------------------------------------- + // Look for rules that apply to the individual user id + // --------------------------------------------------- + + user_req = AclRule::INDIVIDUAL_ID | uid; + auth = match_rules(user_req, + resource_oid_req, + resource_gid_req, + resource_all_req, + rights_req, + resource_oid_mask, + resource_gid_mask); + if ( auth == true ) + { + return true; + } + + // ---------------------------------------------------------- + // Look for rules that apply to each one of the user's groups + // ---------------------------------------------------------- + + set::iterator g_it; + + for (g_it = user_groups.begin(); g_it != user_groups.end(); g_it++) + { + user_req = AclRule::GROUP_ID | *g_it; + + auth = match_rules(user_req, + resource_oid_req, + resource_gid_req, + resource_all_req, + rights_req, + resource_oid_mask, + resource_gid_mask); + + if ( auth == true ) + { + return true; + } + } + + oss.str("No more rules, permission not granted "); + NebulaLog::log("ACL",Log::DEBUG,oss); + + return false; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +bool AclManager::match_rules( + long long user_req, + long long resource_oid_req, + long long resource_gid_req, + long long resource_all_req, + long long rights_req, + long long resource_oid_mask, + long long resource_gid_mask) + +{ + bool auth = false; + ostringstream oss; + + multimap::iterator it; + + pair::iterator, + multimap::iterator> index; + + lock(); + + index = acl_rules.equal_range( user_req ); + + for ( it = index.first; it != index.second; it++) + { + oss.str(""); + oss << "> Rule " << it->second->to_str(); + NebulaLog::log("ACL",Log::DEBUG,oss); + + auth = + // Rule grants the requested rights + ( ( it->second->rights & rights_req ) == rights_req ) + && + ( + // Rule grants permission for all objects of this type + ( ( it->second->resource & resource_all_req ) == resource_all_req ) + || + // Or rule's object type and group object ID match + ( ( it->second->resource & resource_gid_mask ) == resource_gid_req ) + || + // Or rule's object type and individual object ID match + ( ( it->second->resource & resource_oid_mask ) == resource_oid_req ) + ); + + if ( auth == true ) + { + oss.str("Permission granted"); + NebulaLog::log("ACL",Log::DEBUG,oss); + + break; + } + } + + unlock(); + + return auth; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int AclManager::add_rule(long long user, long long resource, long long rights, + string& error_str) +{ + lock(); + + if (lastOID == INT_MAX) + { + lastOID = -1; + } + + AclRule * rule = new AclRule(++lastOID, user, resource, rights); + + ostringstream oss; + int rc; + + multimap::iterator it; + pair::iterator, + multimap::iterator> index; + + bool found = false; + + index = acl_rules.equal_range( user ); + + for ( it = index.first; (it != index.second && !found); it++) + { + found = *(it->second) == *rule; + } + + if ( found ) + { + goto error_duplicated; + } + + if ( rule->malformed(error_str) ) + { + goto error_malformed; + } + + rc = insert(rule); + + if ( rc != 0 ) + { + goto error_insert; + } + + acl_rules.insert( make_pair(rule->user, rule) ); + acl_rules_oids.insert( make_pair(rule->oid, rule) ); + + update_lastOID(); + + unlock(); + + return lastOID; + + +error_duplicated: + oss << "Rule " << rule->to_str() << " already exists"; + rc = -1; + + goto error_common; + +error_malformed: + oss << "Rule " << rule->to_str() << " is malformed: " << error_str; + rc = -2; + + goto error_common; + +error_insert: + oss << "Error inserting rule in DB"; + rc = -3; + + goto error_common; + +error_common: + error_str = oss.str(); + + delete rule; + lastOID--; + + unlock(); + + return rc; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +int AclManager::del_rule(int oid, string& error_str) +{ + + multimap::iterator it; + pair::iterator, + multimap::iterator> index; + + AclRule * rule; + int rc; + bool found = false; + + lock(); + + // Check the rule exists + found = acl_rules_oids.count(oid) > 0; + + if ( !found ) + { + ostringstream oss; + oss << "Rule " << oid << " does not exist"; + error_str = oss.str(); + + unlock(); + return -1; + } + + rule = acl_rules_oids[oid]; + + // Look for it in the multimap + + found = false; + + index = acl_rules.equal_range( rule->user ); + + it = index.first; + while ( !found && it != index.second ) + { + found = *rule == *(it->second); + + + if ( !found ) + { + it++; + } + } + + if ( !found ) + { + ostringstream oss; + oss << "Internal error: ACL Rule " << oid + << " indexed by oid, but not in by user attribute"; + + NebulaLog::log("ACL",Log::ERROR,oss); + + unlock(); + return -1; + } + + + rc = drop( oid ); + + if ( rc != 0 ) + { + error_str = "SQL DB error"; + + unlock(); + return -1; + } + + delete it->second; + + acl_rules.erase( it ); + acl_rules_oids.erase( oid ); + + unlock(); + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void AclManager::bootstrap(SqlDB * _db) +{ + ostringstream oss(db_bootstrap); + + _db->exec(oss); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void AclManager::update_lastOID() +{ + // db->escape_str is not used for 'table' since its name can't be set in + // any way by the user, it is hardcoded. + + ostringstream oss; + + oss << "REPLACE INTO pool_control (tablename, last_oid) VALUES (" + << "'" << table << "'," + << lastOID << ")"; + + db->exec(oss); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int AclManager::select_cb(void *nil, int num, char **values, char **names) +{ + if ( (num != 4) || + (!values[0]) || + (!values[1]) || + (!values[2]) || + (!values[3]) ) + { + return -1; + } + + ostringstream oss; + istringstream iss; + + int oid = atoi(values[0]); + + long long rule_values[3]; + + for ( int i = 0; i < 3; i++ ) + { + iss.str( values[i+1] ); + + iss >> rule_values[i]; + + if ( iss.fail() == true ) + { + return -1; + } + + iss.clear(); + } + + AclRule * rule = new AclRule(oid, + rule_values[0], + rule_values[1], + rule_values[2]); + + oss << "Loading ACL Rule " << rule->to_str(); + NebulaLog::log("ACL",Log::DDEBUG,oss); + + acl_rules.insert( make_pair(rule->user, rule) ); + acl_rules_oids.insert( make_pair(rule->oid, rule) ); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int AclManager::select() +{ + ostringstream oss; + int rc; + + oss << "SELECT " << db_names << " FROM " << table; + + set_callback(static_cast(&AclManager::select_cb)); + + rc = db->exec(oss,this); + + unset_callback(); + + return rc; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int AclManager::insert(AclRule * rule, SqlDB * db) +{ + ostringstream oss; + int rc; + + // Construct the SQL statement to Insert + + oss << "INSERT INTO " << table <<" ("<< db_names <<") VALUES (" + << rule->oid << "," + << rule->user << "," + << rule->resource << "," + << rule->rights << ")"; + + rc = db->exec(oss); + + return rc; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + +int AclManager::drop(int oid) +{ + ostringstream oss; + int rc; + + oss << "DELETE FROM " << table << " WHERE " + << "oid=" << oid; + + rc = db->exec(oss); + + return rc; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int AclManager::dump(ostringstream& oss) +{ + map::iterator it; + string xml; + + lock(); + + oss << ""; + + for ( it = acl_rules_oids.begin() ; it != acl_rules_oids.end(); it++ ) + { + oss << it->second->to_xml(xml); + } + + oss << ""; + + unlock(); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/acl/AclRule.cc b/src/acl/AclRule.cc new file mode 100644 index 0000000000..92243bb55e --- /dev/null +++ b/src/acl/AclRule.cc @@ -0,0 +1,403 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#include "AclRule.h" +#include "AuthManager.h" +#include "ObjectXML.h" + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +const long long AclRule::INDIVIDUAL_ID = 0x0000000100000000LL; +const long long AclRule::GROUP_ID = 0x0000000200000000LL; +const long long AclRule::ALL_ID = 0x0000000400000000LL; + +const long long AclRule::NONE_ID = 0x1000000000000000LL; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +bool AclRule::malformed(string& error_str) const +{ + ostringstream oss; + bool error = false; + + // Check user + + if ( (user & INDIVIDUAL_ID) != 0 && (user & GROUP_ID) != 0 ) + { + error = true; + oss << "[user] INDIVIDUAL (#) and GROUP (@) bits are exclusive"; + } + + if ( (user & INDIVIDUAL_ID) != 0 && (user & ALL_ID) != 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "[user] INDIVIDUAL (#) and ALL (*) bits are exclusive"; + } + + if ( (user & GROUP_ID) != 0 && (user & ALL_ID) != 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "[user] GROUP (@) and ALL (*) bits are exclusive"; + } + + if ( (user & 0x700000000LL) == 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "[user] is missing one of the INDIVIDUAL, GROUP or ALL bits"; + } + + if ( user_id() < 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "[user] ID cannot be negative"; + } + + if ( (user & ALL_ID) != 0 && user_id() != 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "when using the ALL bit, [user] ID must be 0"; + } + + // Check resource + + if ( (resource & INDIVIDUAL_ID) != 0 && (resource & GROUP_ID) != 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "[resource] INDIVIDUAL (#) and GROUP (@) bits are exclusive"; + } + + if ( (resource & INDIVIDUAL_ID) != 0 && (resource & ALL_ID) != 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "[resource] INDIVIDUAL (#) and ALL (*) bits are exclusive"; + } + + if ( (resource & GROUP_ID) != 0 && (resource & ALL_ID) != 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "[resource] GROUP (@) and ALL (*) bits are exclusive"; + } + + if ( (resource & 0x700000000LL) == 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "[resource] is missing one of the INDIVIDUAL, GROUP or ALL bits"; + } + + if ( resource_id() < 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "[resource] ID cannot be negative"; + } + + if ( (resource & ALL_ID) != 0 && resource_id() != 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "when using the ALL bit, [resource] ID must be 0"; + } + + if ( (resource & 0xFF000000000LL) == 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "[resource] type is missing"; + } + + if ( (resource & 0xFFFFF00000000000LL) != 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "wrong [resource] type"; + } + + // Check rights + + if ( rights == 0 ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "wrong [rights], it cannot be 0"; + } + + if ( rights > 0x1FFLL ) + { + if ( error ) + { + oss << "; "; + } + + error = true; + oss << "wrong [rights], it cannot be bigger than 0x1FF"; + } + + if ( error ) + { + error_str = oss.str(); + } + + return error; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void AclRule::build_str() +{ + ostringstream oss; + + if ( (user & GROUP_ID) != 0 ) + { + oss << "@" << user_id(); + } + else if ( (user & INDIVIDUAL_ID) != 0 ) + { + oss << "#" << user_id(); + } + else if ( (user & ALL_ID) != 0 ) + { + oss << "*"; + } + else + { + oss << "??"; + } + + oss << " "; + + AuthRequest::Object objects[] = { + AuthRequest::VM, + AuthRequest::HOST, + AuthRequest::NET, + AuthRequest::IMAGE, + AuthRequest::USER, + AuthRequest::TEMPLATE, + AuthRequest::GROUP + }; + + bool prefix = false; + + for ( int i = 0; i < 7; i++ ) + { + if ( (resource & objects[i]) != 0 ) + { + if ( prefix ) + { + oss << "+"; + } + + oss << AuthRequest::Object_to_str( objects[i] ); + prefix = true; + } + } + + oss << "/"; + + if ( (resource & GROUP_ID) != 0 ) + { + oss << "@" << resource_id(); + } + else if ( (resource & INDIVIDUAL_ID) != 0 ) + { + oss << "#" << resource_id(); + } + else if ( (resource & ALL_ID) != 0 ) + { + oss << "*"; + } + else + { + oss << "??"; + } + + + oss << " "; + + + AuthRequest::Operation operations[] = { + AuthRequest::CREATE, + AuthRequest::DELETE, + AuthRequest::USE, + AuthRequest::MANAGE, + AuthRequest::INFO, + AuthRequest::INFO_POOL, + AuthRequest::INFO_POOL_MINE, + AuthRequest::INSTANTIATE, + AuthRequest::CHOWN + }; + + prefix = false; + + for ( int i = 0; i < 9; i++ ) + { + if ( (rights & operations[i]) != 0 ) + { + if ( prefix ) + { + oss << "+"; + } + + oss << AuthRequest::Operation_to_str( operations[i] ); + prefix = true; + } + } + + str = oss.str(); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +string& AclRule::to_xml(string& xml) const +{ + ostringstream oss; + + oss << + "" + "" << oid << "" << + "" << hex << user << "" << + "" << hex << resource << "" << + "" << hex << rights << "" << + "" << str << "" << + ""; + + xml = oss.str(); + + return xml; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int AclRule::from_xml(xmlNodePtr node) +{ + int rc = 0; + + for (xmlNodePtr acl = node->children ; acl != 0 ; acl = acl->next) + { + if ( acl->type != XML_ELEMENT_NODE ) + { + rc = -1; + break; + } + + xmlNodePtr elem = acl->children; + + if ( elem->type != XML_TEXT_NODE ) + { + rc = -1; + break; + } + + string name = reinterpret_cast(acl->name); + istringstream iss(reinterpret_cast(elem->content)); + + if (name == "ID") + { + iss >> oid; + } + else if (name == "USER") + { + iss >> hex >> user; + } + else if (name == "RESOURCE") + { + iss >> hex >> resource; + } + else if (name == "RIGHTS") + { + iss >> hex >> rights; + } + else if (name == "STRING") + { + str = iss.str(); + } + } + + return rc; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + diff --git a/src/acl/SConstruct b/src/acl/SConstruct new file mode 100644 index 0000000000..f96ccd19bc --- /dev/null +++ b/src/acl/SConstruct @@ -0,0 +1,30 @@ +# SConstruct for src/authm + +# -------------------------------------------------------------------------- # +# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +Import('env') + +lib_name='nebula_acl' + +# Sources to generate the library +source_files=[ + 'AclManager.cc', + 'AclRule.cc' +] + +# Build library +env.StaticLibrary(lib_name, source_files) diff --git a/src/authm/AuthManager.cc b/src/authm/AuthManager.cc index 0753d69f85..b2512f401a 100644 --- a/src/authm/AuthManager.cc +++ b/src/authm/AuthManager.cc @@ -32,6 +32,7 @@ const char * AuthManager::auth_driver_name = "auth_exe"; void AuthRequest::add_auth(Object ob, const string& ob_id, + int ob_gid, Operation op, int owner, bool pub) @@ -39,16 +40,9 @@ void AuthRequest::add_auth(Object ob, ostringstream oss; bool auth; - switch (ob) - { - case VM: oss << "VM:" ; break; - case HOST: oss << "HOST:" ; break; - case NET: oss << "NET:" ; break; - case IMAGE: oss << "IMAGE:" ; break; - case USER: oss << "USER:" ; break; - case TEMPLATE: oss << "TEMPLATE:" ; break; - case GROUP: oss << "GROUP:" ; break; - } + int ob_id_int = -1; + + oss << Object_to_str(ob) << ":"; if (op == CREATE || op == INSTANTIATE) //encode the ob_id, it is a template { @@ -67,130 +61,51 @@ void AuthRequest::add_auth(Object ob, else { oss << ob_id << ":"; + + istringstream iss(ob_id); + iss >> ob_id_int; } - switch (op) - { - case CREATE: - oss << "CREATE:" ; - break; + oss << Operation_to_str(op) << ":"; - case DELETE: - oss << "DELETE:" ; - break; - - case USE: - oss << "USE:" ; - break; - - case MANAGE: - oss << "MANAGE:" ; - break; - - case INFO: - oss << "INFO:" ; - break; - - case INFO_POOL: - oss << "INFO_POOL:" ; - break; - - case INFO_POOL_MINE: - oss << "INFO_POOL_MINE:" ; - break; - - case INSTANTIATE: - oss << "INSTANTIATE:" ; - break; - - case CHOWN: - oss << "CHOWN:" ; - break; - } - - oss << owner << ":" << pub; + oss << owner << ":" << pub << ":"; // ------------------------------------------------------------------------- // Authorize the request for self authorization // ------------------------------------------------------------------------- - if ( uid == 0 ) + // There are some default conditions that grant permission without + // consulting the ACL manager + if ( + // User is oneadmin, or is in the oneadmin group + uid == 0 || + gids.count( GroupPool::ONEADMIN_ID ) == 1 || + + // User is the owner of the object, for certain operations + ( owner == uid && + ( op == DELETE || op == USE || op == MANAGE || + op == INFO || op == INSTANTIATE ) + ) || + + // Object is public and user is in its group, for certain operations + ( pub && ( gids.count( ob_gid ) == 1 ) && + (op == USE || op == INSTANTIATE || op == INFO ) && + (ob == NET || ob == IMAGE || ob == TEMPLATE) + ) + ) { auth = true; } else { - auth = false; + Nebula& nd = Nebula::instance(); + AclManager* aclm = nd.get_aclm(); - switch (op) - { - case CREATE: - if ( ob == VM || ob == NET || ob == IMAGE || ob == TEMPLATE ) - { - auth = true; - } - break; - - case INSTANTIATE: - if ( ob == VM ) - { - auth = true; - } - break; - - case DELETE: - auth = owner == uid; - break; - - case USE: - if (ob == NET || ob == IMAGE || ob == TEMPLATE) - { - auth = (owner == uid) || pub; - } - else if (ob == HOST) - { - auth = true; - } - break; - - case MANAGE: - auth = owner == uid; - break; - - case INFO: - if ( ob != USER ) // User info only for root or owner - { - auth = true; - } - else - { - istringstream iss(ob_id); - int ob_id_int; - - iss >> ob_id_int; - - if (ob_id_int == uid) - { - auth = true; - } - } - break; - - case INFO_POOL: - if ( ob != USER ) // User pool only for oneadmin - { - auth = true; - } - break; - - case INFO_POOL_MINE: - auth = true; - break; - case CHOWN: //true only for oneadmin - break; - } + auth = aclm->authorize(uid, gids, ob, ob_id_int, ob_gid, op); } + oss << auth; // Store the ACL authorization result in the request + self_authorize = self_authorize && auth; auths.push_back(oss.str()); diff --git a/src/authm/test/AuthManagerTest.cc b/src/authm/test/AuthManagerTest.cc index fc678778c3..8feaf66590 100644 --- a/src/authm/test/AuthManagerTest.cc +++ b/src/authm/test/AuthManagerTest.cc @@ -20,6 +20,8 @@ #include #include +#include "Nebula.h" +#include "NebulaTest.h" #include "test/OneUnitTest.h" #include "AuthManager.h" #include "Template.h" @@ -34,11 +36,45 @@ using namespace std; /* ************************************************************************* */ /* ************************************************************************* */ + + +class NebulaTestAuth: public NebulaTest +{ +public: + NebulaTestAuth():NebulaTest() + { + NebulaTest::the_tester = this; + + need_authm = true; + need_aclm = true; + }; + + AuthManager* create_authm(time_t timer_period) + { + ostringstream oss; + vector am_mads; + char *error = 0; + + oss << "AUTH_MAD = [ executable=\"/" << getenv("PWD") + << "/dummy\"]" << endl; + + Template * t = new Template(); + + t->parse(oss.str(),&error); + + t->get("AUTH_MAD", am_mads); + + return new AuthManager(1,3,am_mads); + }; +}; + +/* ************************************************************************* */ +/* ************************************************************************* */ + class AuthManagerTest : public OneUnitTest { CPPUNIT_TEST_SUITE (AuthManagerTest); - CPPUNIT_TEST (load); CPPUNIT_TEST (timeout); CPPUNIT_TEST (authenticate); CPPUNIT_TEST (authorize); @@ -48,75 +84,70 @@ class AuthManagerTest : public OneUnitTest CPPUNIT_TEST_SUITE_END (); +private: + NebulaTestAuth * tester; + + AuthManager * am; + public: - AuthManagerTest(){}; + AuthManagerTest() + { + xmlInitParser(); + }; - ~AuthManagerTest(){ - /*OpenSSL internal tables are allocated when an application starts up. - Since such tables do not grow in size over time they are harmless. */ + ~AuthManagerTest() + { + xmlCleanupParser(); - EVP_cleanup() ; - CRYPTO_cleanup_all_ex_data(); + // OpenSSL internal tables are allocated when an application starts up. + // Since such tables do not grow in size over time they are harmless. + + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); }; /* ********************************************************************* */ /* ********************************************************************* */ + void setUp() { + create_db(); - ostringstream oss; - vector am_mads; - char *error = 0; + tester = new NebulaTestAuth(); - MadManager::mad_manager_system_init(); + Nebula& neb = Nebula::instance(); + neb.start(); - oss << "AUTH_MAD = [ executable=\"/" << getenv("PWD") - << "/dummy\"]" << endl; - - t = new Template(); - - t->parse(oss.str(),&error); - - t->get("AUTH_MAD", am_mads); - - am = new AuthManager(1,3,am_mads); + am = neb.get_authm(); }; void tearDown() { - delete am; - delete t; - }; - - void load() - { - int rc; - - rc = am->start(); - - CPPUNIT_ASSERT(rc==0); - - am->load_mads(0); + // ----------------------------------------------------------- + // Stop the managers & free resources + // ----------------------------------------------------------- am->trigger(AuthManager::FINALIZE,0); pthread_join(am->get_thread_id(),0); - CPPUNIT_ASSERT(0==0); - } + //XML Library + xmlCleanupParser(); + + delete_db(); + + delete tester; + }; + + /* ********************************************************************* */ + /* ********************************************************************* */ //This test needs a driver that takes more than 3 secs to AUTHENTICATE void timeout() { - int rc; - AuthRequest ar(2); - - rc = am->start(); - - CPPUNIT_ASSERT(rc==0); - - am->load_mads(0); + set empty_set; + AuthRequest ar(2, empty_set); ar.add_authenticate("timeout","the_pass","the_secret"); @@ -128,24 +159,12 @@ public: CPPUNIT_ASSERT(ar.timeout==true); am->discard_request(ar.id); - - am->trigger(AuthManager::FINALIZE,0); - - pthread_join(am->get_thread_id(),0); - - CPPUNIT_ASSERT(0==0); } void authenticate() { - int rc; - AuthRequest ar(2); - - rc = am->start(); - - CPPUNIT_ASSERT(rc==0); - - am->load_mads(0); + set empty_set; + AuthRequest ar(2, empty_set); ar.add_authenticate("the_user","the_pass","the_secret"); @@ -153,53 +172,45 @@ public: ar.wait(); CPPUNIT_ASSERT(ar.result==true); - - am->trigger(AuthManager::FINALIZE,0); - - pthread_join(am->get_thread_id(),0); - - CPPUNIT_ASSERT(0==0); } void authorize() { - int rc; - AuthRequest ar(2); + set empty_set; + AuthRequest ar(2, empty_set); - rc = am->start(); + //OBJECT:OBJECT_ID:ACTION:OWNER:PUBLIC:CORE_RESULT - CPPUNIT_ASSERT(rc==0); - - am->load_mads(0); - - //OBJECT:OBJECT_ID:ACTION:OWNER:PUBLIC - - string astr="VM:VGhpcyBpcyBhIHRlbXBsYXRlCg==:CREATE:-1:0 " - "IMAGE:2:USE:3:0 " - "NET:4:DELETE:5:1 " - "HOST:6:MANAGE:7:1"; + string astr = "VM:VGhpcyBpcyBhIHRlbXBsYXRlCg==:CREATE:-1:0:0 " + "IMAGE:2:USE:3:0:0 " + "NET:4:DELETE:5:1:0 " + "HOST:6:MANAGE:7:1:0"; ar.add_auth(AuthRequest::VM, "This is a template\n", + 0, AuthRequest::CREATE, -1, false); ar.add_auth(AuthRequest::IMAGE, 2, + 0, AuthRequest::USE, 3, false); ar.add_auth(AuthRequest::NET, 4, + 0, AuthRequest::DELETE, 5, true); ar.add_auth(AuthRequest::HOST, 6, + 0, AuthRequest::MANAGE, 7, true); @@ -207,59 +218,71 @@ public: am->trigger(AuthManager::AUTHORIZE,&ar); ar.wait(); +/* + if ( ar.result != false ) + { + cout << endl << "ar.result: " << ar.result << endl; + } + + if ( ar.message != astr ) + { + cout << endl << "ar.message: " << ar.message; + cout << endl << "expected: " << astr << endl; + } +//*/ CPPUNIT_ASSERT(ar.result==false); CPPUNIT_ASSERT(ar.message==astr); - - am->trigger(AuthManager::FINALIZE,0); - - pthread_join(am->get_thread_id(),0); - - CPPUNIT_ASSERT(0==0); } void self_authorize() { - AuthRequest ar(2); - AuthRequest ar1(2); - AuthRequest ar2(3); - AuthRequest ar3(4); - AuthRequest ar4(2); - AuthRequest ar5(0); - AuthRequest ar6(0); + // Make all users belong to the USERS (1) group + set gid_set; + gid_set.insert(1); - ar.add_auth(AuthRequest::VM,"dGhpcy",AuthRequest::CREATE,2,false); - ar.add_auth(AuthRequest::NET,2,AuthRequest::USE,2,false); - ar.add_auth(AuthRequest::IMAGE,3,AuthRequest::USE,4,true); + AuthRequest ar(2, gid_set); + AuthRequest ar1(2, gid_set); + AuthRequest ar2(3, gid_set); + AuthRequest ar3(4, gid_set); + AuthRequest ar4(2, gid_set); + AuthRequest ar5(0, gid_set); + AuthRequest ar6(0, gid_set); + + ar.add_auth(AuthRequest::VM,"dGhpcy",-1,AuthRequest::CREATE,2,false); + ar.add_auth(AuthRequest::NET,2,1,AuthRequest::USE,2,false); + ar.add_auth(AuthRequest::IMAGE,3,1,AuthRequest::USE,4,true); CPPUNIT_ASSERT(ar.plain_authorize() == true); - ar1.add_auth(AuthRequest::VM,"dGhpcy",AuthRequest::CREATE,2,false); - ar1.add_auth(AuthRequest::NET,2,AuthRequest::USE,2,false); - ar1.add_auth(AuthRequest::IMAGE,3,AuthRequest::USE,4,false); + ar1.add_auth(AuthRequest::VM,"dGhpcy",-1,AuthRequest::CREATE,2,false); + ar1.add_auth(AuthRequest::NET,2,1,AuthRequest::USE,2,false); + ar1.add_auth(AuthRequest::IMAGE,3,1,AuthRequest::USE,4,false); CPPUNIT_ASSERT(ar1.plain_authorize() == false); - ar2.add_auth(AuthRequest::HOST,"dGhpcy",AuthRequest::CREATE,0,false); + ar2.add_auth(AuthRequest::HOST,"dGhpcy",-1,AuthRequest::CREATE,0,false); CPPUNIT_ASSERT(ar2.plain_authorize() == false); - ar3.add_auth(AuthRequest::VM,5,AuthRequest::MANAGE,2,false); + ar3.add_auth(AuthRequest::VM,5,1,AuthRequest::MANAGE,2,false); CPPUNIT_ASSERT(ar3.plain_authorize() == false); - ar4.add_auth(AuthRequest::VM,4,AuthRequest::MANAGE,2,false); + ar4.add_auth(AuthRequest::VM,4,1,AuthRequest::MANAGE,2,false); CPPUNIT_ASSERT(ar4.plain_authorize() == true); - ar5.add_auth(AuthRequest::HOST,4,AuthRequest::MANAGE,0,false); + ar5.add_auth(AuthRequest::HOST,4,-1,AuthRequest::MANAGE,0,false); CPPUNIT_ASSERT(ar5.plain_authorize() == true); - ar6.add_auth(AuthRequest::HOST,4,AuthRequest::CREATE,0,false); + ar6.add_auth(AuthRequest::HOST,4,-1,AuthRequest::CREATE,0,false); CPPUNIT_ASSERT(ar6.plain_authorize() == true); } void self_authenticate() { - AuthRequest ar(2); - AuthRequest ar1(2); + set empty_set; + + AuthRequest ar(2, empty_set); + AuthRequest ar1(2,empty_set); ar.add_authenticate("the_user","the_pass","the_secret"); CPPUNIT_ASSERT(ar.plain_authenticate() == false); @@ -267,11 +290,6 @@ public: ar1.add_authenticate("the_user","the_pass","the_pass"); CPPUNIT_ASSERT(ar1.plain_authenticate() == true); } - -private: - AuthManager * am; - - Template * t; }; diff --git a/src/authm/test/SConstruct b/src/authm/test/SConstruct index d3f9fcded1..081d8a09ed 100644 --- a/src/authm/test/SConstruct +++ b/src/authm/test/SConstruct @@ -16,13 +16,30 @@ Import('env') env.Prepend(LIBS=[ - 'nebula_template', + 'nebula_core_test', + 'nebula_vmm', + 'nebula_lcm', + 'nebula_im', + 'nebula_hm', + 'nebula_rm', + 'nebula_dm', + 'nebula_tm', + 'nebula_um', 'nebula_authm', - 'nebula_common', - 'nebula_core', + 'nebula_group', + 'nebula_acl', 'nebula_mad', + 'nebula_template', + 'nebula_image', + 'nebula_pool', + 'nebula_host', + 'nebula_vnm', + 'nebula_vm', + 'nebula_vmtemplate', + 'nebula_common', 'nebula_sql', 'nebula_log', + 'nebula_xml', 'crypto' ]) diff --git a/src/cli/etc/oneacl.yaml b/src/cli/etc/oneacl.yaml new file mode 100644 index 0000000000..19402950a1 --- /dev/null +++ b/src/cli/etc/oneacl.yaml @@ -0,0 +1,31 @@ +--- +:ID: + :desc: To which resource owner the rule applies to + :size: 5 + :right: true + +:USER: + :desc: To which resource owner the rule applies to + :size: 8 + :right: true + +:RES_VHNIUTG: + :desc: Which resource the rule applies to + :size: 11 + +:RID: + :desc: Resource ID + :size: 5 + :right: true + +:OPE_CDUMIPpTW: + :desc: Operation to which the rule applies + :size: 13 + :right: true + +:default: +- :ID +- :USER +- :RES_VHNIUTG +- :RID +- :OPE_CDUMIPpTW diff --git a/src/cli/one_helper/oneacl_helper.rb b/src/cli/one_helper/oneacl_helper.rb new file mode 100644 index 0000000000..bb5968d948 --- /dev/null +++ b/src/cli/one_helper/oneacl_helper.rb @@ -0,0 +1,133 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'one_helper' + +class OneAclHelper < OpenNebulaHelper::OneHelper + def self.rname + "ACL" + end + + def self.conf_file + "oneacl.yaml" + end + +private + + def factory(id = nil) + if id + OpenNebula::Acl.new_with_id(id, @client) + else + xml = OpenNebula::Acl.build_xml + OpenNebula::Acl.new(xml, @client) + end + end + + def factory_pool(filter) + OpenNebula::AclPool.new(@client) + end + + # TODO check that @content[:resources_str] is valid + def self.resource_mask(str) + resource_type=str.split("/")[0] + + mask = "-------" + + resource_type.split("+").each{|type| + case type + when "VM" + mask[0] = "V" + when "HOST" + mask[1] = "H" + when "NET" + mask[2] = "N" + when "IMAGE" + mask[3] = "I" + when "USER" + mask[4] = "U" + when "TEMPLATE" + mask[5] = "T" + when "GROUP" + mask[6] = "G" + end + } + mask + end + + # TODO check that @content[:resources_str] is valid + def self.right_mask(str) + mask = "---------" + + str.split("+").each{|type| + case type + when "CREATE" + mask[0] = "C" + when "DELETE" + mask[1] = "D" + when "USE" + mask[2] = "U" + when "MANAGE" + mask[3] = "M" + when "INFO" + mask[4] = "I" + when "INFO_POOL" + mask[5] = "P" + when "INFO_POOL_MINE" + mask[6] = "p" + when "INSTANTIATE" + mask[8] = "T" + when "CHOWN" + mask[9] = "W" + end + } + + mask + end + + def format_pool(pool, options, top=false) + config_file=self.class.table_conf + + table=CLIHelper::ShowTable.new(config_file, self) do + column :ID, "Rule Identifier", + :size=>5 do |d| + d['ID'] + end + + column :USER, "To which resource owner the rule applies to", + :size=>8 do |d| + d['STRING'].split(" ")[0] + end + + column :RES_VHNIUTG, "Resource to which the rule applies" do |d| + OneAclHelper::resource_mask d['STRING'].split(" ")[1] + end + + column :RID, "Resource ID", :right, :size=>8 do |d| + d['STRING'].split(" ")[1].split("/")[1] + end + + column :OPE_CDUMIPpTW, "Operation to which the rule applies" do |d| + OneAclHelper::right_mask d['STRING'].split(" ")[2] + end + + default :ID, :USER, :RES_VHNIUTG, :RID, :OPE_CDUMIPpTW + end + + table.show(pool, options) + + end + +end \ No newline at end of file diff --git a/src/cli/oneacl b/src/cli/oneacl new file mode 100755 index 0000000000..f34ae18240 --- /dev/null +++ b/src/cli/oneacl @@ -0,0 +1,105 @@ +#!/usr/bin/env ruby + +# -------------------------------------------------------------------------- # +# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" +end + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/cli" + +require 'command_parser' +require 'one_helper/oneacl_helper' + +cmd = CommandParser::CmdParser.new(ARGV) do + usage "oneacl COMMAND [args..] [options..]" + version OpenNebulaHelper::ONE_VERSION + + helper = OneAclHelper.new + + ######################################################################## + # Global Options + ######################################################################## + set :option, CommandParser::OPTIONS + + ######################################################################## + # Formatters for arguments + ######################################################################## + set :format, :aclid_list, OneAclHelper.list_to_id_desc do |arg| + helper.list_to_id(arg) + end + + ######################################################################## + # Commands + ######################################################################## + + addrule_desc = <<-EOT.unindent + Adds a new ACL rule + EOT + + command :create, addrule_desc, [:user,:rulestr], [:resource, nil], [:rights, nil] do + case args.length + when 1 + new_args=Acl.parse_rule(args[0]) + when 3 + new_args=args + else + next -1, "Wrong number of arguments, must be 1 or 3" + end + + errors=new_args.map do |arg| + if OpenNebula.is_error?(arg) + arg.message + else + nil + end + end + + errors.compact! + + if errors.length>0 + next -1, errors.join(', ') + end + + helper.create_resource(options) do |rule| + rule.allocate(*new_args) + end + end + + delrule_desc = <<-EOT.unindent + Deletes an existing ACL rule + EOT + + command :delete, delrule_desc, [:range] do + helper.perform_actions(args[0],options,"deleted") do |obj| + obj.delete + end + end + + list_desc = <<-EOT.unindent + Lists the ACL rule set + EOT + + command :list, list_desc,:options=>OpenNebulaHelper::XML do + helper.list_pool( options ) + end +end diff --git a/src/group/Group.cc b/src/group/Group.cc index 3b710bc7d6..acae4034d3 100644 --- a/src/group/Group.cc +++ b/src/group/Group.cc @@ -129,7 +129,7 @@ int Group::from_xml(const string& xml) // Get associated classes ObjectXML::get_nodes("/GROUP/USERS", content); - if( content.size() < 1 ) + if (content.empty()) { return -1; } @@ -137,6 +137,8 @@ int Group::from_xml(const string& xml) // Set of IDs rc += ObjectCollection::from_xml_node(content[0]); + ObjectXML::free_nodes(content); + if (rc != 0) { return -1; diff --git a/src/group/test/SConstruct b/src/group/test/SConstruct index d7ed958024..01dc379f17 100644 --- a/src/group/test/SConstruct +++ b/src/group/test/SConstruct @@ -44,6 +44,7 @@ env.Prepend(LIBS=[ 'nebula_pool', 'nebula_hm', 'nebula_authm', + 'nebula_acl', 'nebula_common', 'nebula_lcm', 'nebula_dm', diff --git a/src/host/Host.cc b/src/host/Host.cc index 992f0d87d3..32e98ad3fc 100644 --- a/src/host/Host.cc +++ b/src/host/Host.cc @@ -34,7 +34,7 @@ Host::Host( const string& _im_mad_name, const string& _vmm_mad_name, const string& _tm_mad_name): - PoolObjectSQL(id,_hostname,-1,-1,table), + PoolObjectSQL(id,_hostname,-1,-1,"","",table), state(INIT), im_mad_name(_im_mad_name), vmm_mad_name(_vmm_mad_name), @@ -240,24 +240,27 @@ int Host::from_xml(const string& xml) // Get associated classes ObjectXML::get_nodes("/HOST/HOST_SHARE", content); - if( content.size() < 1 ) + if (content.empty()) { return -1; } rc += host_share.from_xml_node( content[0] ); + ObjectXML::free_nodes(content); content.clear(); ObjectXML::get_nodes("/HOST/TEMPLATE", content); - if( content.size() < 1 ) + if( content.empty()) { return -1; } rc += obj_template->from_xml_node( content[0] ); + ObjectXML::free_nodes(content); + if (rc != 0) { return -1; diff --git a/src/host/test/SConstruct b/src/host/test/SConstruct index 9f12682dc5..03184e77b6 100644 --- a/src/host/test/SConstruct +++ b/src/host/test/SConstruct @@ -37,6 +37,7 @@ env.Prepend(LIBS=[ 'nebula_pool', 'nebula_hm', 'nebula_authm', + 'nebula_acl', 'nebula_common', 'nebula_lcm', 'nebula_dm', diff --git a/src/image/Image.cc b/src/image/Image.cc index 928d371b3c..b02a3f7d8a 100644 --- a/src/image/Image.cc +++ b/src/image/Image.cc @@ -36,8 +36,10 @@ Image::Image(int _uid, int _gid, + const string& _uname, + const string& _gname, ImageTemplate * _image_template): - PoolObjectSQL(-1,"",_uid,_gid,table), + PoolObjectSQL(-1,"",_uid,_gid,_uname,_gname,table), type(OS), regtime(time(0)), source("-"), @@ -324,6 +326,8 @@ string& Image::to_xml(string& xml) const "" << oid << "" << "" << uid << "" << "" << gid << "" << + "" << uname << "" << + "" << gname << "" << "" << name << "" << "" << type << "" << "" << public_obj << "" << @@ -332,7 +336,7 @@ string& Image::to_xml(string& xml) const "" << source << "" << "" << state << "" << "" << running_vms << "" << - obj_template->to_xml(template_xml) << + obj_template->to_xml(template_xml) << ""; xml = oss.str(); @@ -355,9 +359,13 @@ int Image::from_xml(const string& xml) update_from_str(xml); // Get class base attributes - rc += xpath(oid, "/IMAGE/ID", -1); + rc += xpath(oid, "/IMAGE/ID", -1); rc += xpath(uid, "/IMAGE/UID", -1); rc += xpath(gid, "/IMAGE/GID", -1); + + rc += xpath(uname, "/IMAGE/UNAME", "not_found"); + rc += xpath(gname, "/IMAGE/GNAME", "not_found"); + rc += xpath(name, "/IMAGE/NAME", "not_found"); rc += xpath(int_type, "/IMAGE/TYPE", 0); @@ -374,13 +382,16 @@ int Image::from_xml(const string& xml) // Get associated classes ObjectXML::get_nodes("/IMAGE/TEMPLATE", content); - if( content.size() < 1 ) + + if (content.empty()) { return -1; } rc += obj_template->from_xml_node(content[0]); + ObjectXML::free_nodes(content); + if (rc != 0) { return -1; diff --git a/src/image/ImagePool.cc b/src/image/ImagePool.cc index 19313b552d..980715b6b9 100644 --- a/src/image/ImagePool.cc +++ b/src/image/ImagePool.cc @@ -57,6 +57,8 @@ ImagePool::ImagePool(SqlDB * db, int ImagePool::allocate ( int uid, int gid, + const string& uname, + const string& gname, ImageTemplate* img_template, int * oid, string& error_str) @@ -66,7 +68,7 @@ int ImagePool::allocate ( string name; ostringstream oss; - img = new Image(uid, gid, img_template); + img = new Image(uid, gid, uname, gname, img_template); // Check name img->get_template_attribute("NAME", name); @@ -252,6 +254,7 @@ void ImagePool::authorize_disk(VectorAttribute * disk,int uid, AuthRequest * ar) ar->add_auth(AuthRequest::IMAGE, img->get_oid(), + img->get_gid(), AuthRequest::USE, img->get_uid(), img->isPublic()); diff --git a/src/image/test/ImagePoolTest.cc b/src/image/test/ImagePoolTest.cc index d7745bd474..c190a08f1d 100644 --- a/src/image/test/ImagePoolTest.cc +++ b/src/image/test/ImagePoolTest.cc @@ -25,6 +25,10 @@ using namespace std; const int uids[] = {0,1,2}; +const char* unames[] = {"one","two","three"}; +const char* gnames[] = {"oneadmin","oneadmin","users"}; + + const string names[] = {"Image one", "Second Image", "The third image"}; const string templates[] = @@ -49,20 +53,19 @@ const string templates[] = const string xmls[] = { - "001Image one0010000000000-40", + "001oneoneadminImage one0010000000000-40", - "111Second Image0100000000000-40", + "111twooneadminSecond Image0100000000000-40", - "021The third image0000000000000-40" + "021threeusersThe third image0000000000000-40" }; // This xml dump result has the STIMEs modified to 0000000000 const string xml_dump = -"001Image one0010000000000-40111Second Image0100000000000-40221The third image0000000000000-40"; +"001oneoneadminImage one0010000000000-40111twooneadminSecond Image0100000000000-40221threeusersThe third image0000000000000-40"; const string xml_dump_where = -"001Image one0010000000000-40111Second Image0100000000000-40"; - +"001oneoneadminImage one0010000000000-40111twooneadminSecond Image0100000000000-40"; /* ************************************************************************* */ /* ************************************************************************* */ @@ -106,7 +109,10 @@ public: if( rc == 0 ) { - return ImagePool::allocate(uid, 1, img_template, oid, err); + string uname = unames[uid]; + string gname = gnames[uid]; + + return ImagePool::allocate(uid, 1, uname, gname, img_template, oid, err); } else { diff --git a/src/image/test/SConstruct b/src/image/test/SConstruct index 8827054f90..391d3cce93 100644 --- a/src/image/test/SConstruct +++ b/src/image/test/SConstruct @@ -36,6 +36,7 @@ env.Prepend(LIBS=[ 'nebula_pool', 'nebula_hm', 'nebula_authm', + 'nebula_acl', 'nebula_common', 'nebula_lcm', 'nebula_dm', diff --git a/src/lcm/test/LifeCycleManagerTest.cc b/src/lcm/test/LifeCycleManagerTest.cc index 33ec6ea6ed..414be71a67 100644 --- a/src/lcm/test/LifeCycleManagerTest.cc +++ b/src/lcm/test/LifeCycleManagerTest.cc @@ -24,6 +24,9 @@ using namespace std; const int uids[] = {123, 261, 123}; const int gids[] = {150, 164, 175}; +const char* unames[] = {"one", "two", "three"}; +const char* gnames[] = {"oneadmin", "oneadmin", "users"}; + const string names[] = {"VM one", "Second VM", "VM 3"}; const string templates[] = @@ -186,8 +189,17 @@ private: if( rc == 0 ) { - return vmpool->allocate(uids[index], gids[index], vm_template, &oid, - err, false); + string uname = unames[index]; + string gname = gnames[index]; + + return vmpool->allocate(uids[index], + gids[index], + uname, + gname, + vm_template, + &oid, + err, + false); } else { diff --git a/src/lcm/test/SConstruct b/src/lcm/test/SConstruct index 41597fac76..bb9131fff1 100644 --- a/src/lcm/test/SConstruct +++ b/src/lcm/test/SConstruct @@ -36,6 +36,7 @@ env.Prepend(LIBS=[ 'nebula_xml', 'nebula_hm', 'nebula_authm', + 'nebula_acl', 'nebula_common', 'nebula_sql', 'nebula_log', diff --git a/src/nebula/Nebula.cc b/src/nebula/Nebula.cc index 3879d2ee71..f109d89462 100644 --- a/src/nebula/Nebula.cc +++ b/src/nebula/Nebula.cc @@ -251,6 +251,7 @@ void Nebula::start() UserPool::bootstrap(db); ImagePool::bootstrap(db); VMTemplatePool::bootstrap(db); + AclManager::bootstrap(db); } } catch (exception&) @@ -445,28 +446,6 @@ void Nebula::start() throw runtime_error("Could not start the Dispatch Manager"); } - // ---- Request Manager ---- - try - { - int rm_port = 0; - - nebula_configuration->get("PORT", rm_port); - - rm = new RequestManager(rm_port, log_location + "one_xmlrpc.log"); - } - catch (bad_alloc&) - { - NebulaLog::log("ONE", Log::ERROR, "Error starting RM"); - throw; - } - - rc = rm->start(); - - if ( rc != 0 ) - { - throw runtime_error("Could not start the Request Manager"); - } - // ---- Hook Manager ---- try { @@ -520,6 +499,23 @@ void Nebula::start() } } + // ---- ACL Manager ---- + try + { + aclm = new AclManager(db); + } + catch (bad_alloc&) + { + throw; + } + + rc = aclm->start(); + + if ( rc != 0 ) + { + throw runtime_error("Could not start the ACL Manager"); + } + // ---- Image Manager ---- try { @@ -541,6 +537,28 @@ void Nebula::start() throw runtime_error("Could not start the Image Manager"); } + // ---- Request Manager ---- + try + { + int rm_port = 0; + + nebula_configuration->get("PORT", rm_port); + + rm = new RequestManager(rm_port, log_location + "one_xmlrpc.log"); + } + catch (bad_alloc&) + { + NebulaLog::log("ONE", Log::ERROR, "Error starting RM"); + throw; + } + + rc = rm->start(); + + if ( rc != 0 ) + { + throw runtime_error("Could not start the Request Manager"); + } + // ----------------------------------------------------------- // Load mads // ----------------------------------------------------------- diff --git a/src/nebula/SConstruct b/src/nebula/SConstruct index 93b154eb77..4bb8e393a7 100644 --- a/src/nebula/SConstruct +++ b/src/nebula/SConstruct @@ -43,6 +43,7 @@ env.Prepend(LIBS=[ 'nebula_um', 'nebula_group', 'nebula_authm', + 'nebula_acl', 'nebula_mad', 'nebula_template', 'nebula_image', diff --git a/src/oca/ruby/OpenNebula.rb b/src/oca/ruby/OpenNebula.rb index 6f1ffa281e..c536eefe7a 100644 --- a/src/oca/ruby/OpenNebula.rb +++ b/src/oca/ruby/OpenNebula.rb @@ -39,6 +39,8 @@ require 'OpenNebula/Template' require 'OpenNebula/TemplatePool' require 'OpenNebula/Group' require 'OpenNebula/GroupPool' +require 'OpenNebula/Acl' +require 'OpenNebula/AclPool' module OpenNebula diff --git a/src/oca/ruby/OpenNebula/Acl.rb b/src/oca/ruby/OpenNebula/Acl.rb new file mode 100644 index 0000000000..8759037507 --- /dev/null +++ b/src/oca/ruby/OpenNebula/Acl.rb @@ -0,0 +1,250 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +module OpenNebula + # Abstract rules of the type USER RESOURCE RIGHTS + # which are: + # USER -> # + # @ + # ALL + # RESOURCE -> + separated list and "/{#,@}|ALL" + # VM, + # HOST + # NET + # IMAGE + # USER + # TEMPLATE + # GROUP + # ACL + # RIGHTS -> + separated list + # CREATE + # DELETE + # USE + # MANAGE + # INFO + # INFO_POOL + # INFO_POOL_MINE + # INSTANTIATE + # CHOWN + class Acl < PoolElement + + USERS = { + "UID" => 0x100000000, + "GID" => 0x200000000, + "ALL" => 0x400000000 + } + + RESOURCES = + { + "VM" => 0x1000000000, + "HOST" => 0x2000000000, + "NET" => 0x4000000000, + "IMAGE" => 0x8000000000, + "USER" => 0x10000000000, + "TEMPLATE" => 0x20000000000, + "GROUP" => 0x40000000000 + } + + RIGHTS = + { + "CREATE" => 0x1, # Auth. to create an object + "DELETE" => 0x2, # Auth. to delete an object + "USE" => 0x4, # Auth. to use an object + "MANAGE" => 0x8, # Auth. to manage an object + "INFO" => 0x10, # Auth. to view an object + "INFO_POOL" => 0x20, # Auth. to view any object in the pool + "INFO_POOL_MINE"=> 0x40, # Auth. to view user and/or group objects + "INSTANTIATE" => 0x80, # Auth. to instantiate a VM from a TEMPLATE + "CHOWN" => 0x100 # Auth. to change ownership of an object + } + + # Constructor + # + # @param xml [String] must be an xml built with {#build_xml} + # @param client [Client] represents an XML-RPC connection + def initialize(xml, client) + super(xml,client) + end + + # Creates an empty XML representation. It contains the id, if it is + # specified. + # + # @param pe_id [Integer] rule ID + # @param client [Client] represents an XML-RPC connection + # + # @return [String] an empty XML representation + def self.build_xml(pe_id=nil) + if pe_id + acl_xml = "#{pe_id}" + else + acl_xml = "" + end + + XMLElement.build_xml(acl_xml,'ACL') + end + + # Creates a new ACL rule. + # + # @param user [String] + # A string containing a hex number, e.g. 0x100000001 + # @param resource [String] + # A string containing a hex number, e.g. 0x2100000001 + # @param rights [String] + # A string containing a hex number, e.g. 0x10 + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def allocate(user, resource, rights) + return super( AclPool::ACL_POOL_METHODS[:addrule], + user, + resource, + rights ) + end + + # Deletes the Acl rule + # + # @return [nil, OpenNebula::Error] nil in case of success, Error + # otherwise + def delete() + super(AclPool::ACL_POOL_METHODS[:delrule]) + end + + # Does nothing, individual ACL rules info can't be retrieved from + # OpenNebula + # + # @return [nil] nil + def info() + return nil + end + + # Parses a rule string, e.g. "#5 HOST+VM/@12 INFO+CREATE+DELETE" + # + # @param rule_str [String] an ACL rule in string format + # + # @return [Array] an Array containing 3 strings (hex 64b numbers), + # or OpenNebula::Error objects + def self.parse_rule(rule_str) + ret = Array.new + + rule_str = rule_str.split(" ") + + if rule_str.length != 3 + return [OpenNebula::Error.new( + "String needs three components: User, Resource, Rights")] + end + + ret << parse_users(rule_str[0]) + ret << parse_resources(rule_str[1]) + ret << parse_rights(rule_str[2]) + + return ret + end + +private + + # Converts a string in the form [#, @, *] to a hex. number + # + # @param users [String] Users component string + # + # @return [String] A string containing a hex number + def self.parse_users(users) + begin + return calculate_ids(users).to_i.to_s(16) + rescue Exception => e + return OpenNebula::Error.new(e.message) + end + end + + # Converts a resources string to a hex. number + # + # @param resources [String] Resources component string + # + # @return [String] A string containing a hex number + def self.parse_resources(resources) + begin + ret = 0 + resources = resources.split("/") + + if resources.size != 2 + raise "Resource '#{resources}' malformed" + end + + resources[0].split("+").each{ |resource| + if !RESOURCES[resource.upcase] + raise "Resource '#{resource}' does not exist" + end + ret += RESOURCES[resource.upcase] + } + + ret += calculate_ids(resources[1]) + + return ret.to_i.to_s(16) + rescue Exception => e + return OpenNebula::Error.new(e.message) + end + end + + # Converts a rights string to a hex. number + # + # @param rights [String] Rights component string + # + # @return [String] A string containing a hex number + def self.parse_rights(rights) + begin + ret = 0 + rights = rights.split("+") + + rights.each{ |right| + raise "Right '#{right}' does not exist" if !RIGHTS[right.upcase] + + ret += RIGHTS[right.upcase] + } + + return ret.to_i.to_s(16) + rescue Exception => e + return OpenNebula::Error.new(e.message) + end + end + + # Calculates the numeric value for a String containing an individual + # (#), group (@) or all (*) ID component + # + # @param id_str [String] Rule Id string + # + # @return [Integer] the numeric value for the given id_str + def self.calculate_ids(id_str) + raise "ID string '#{id_str}' malformed" if + !id_str.match(/^([\#@]\d+|\*)$/) + + value = 0 + + case id_str[0..0] + when "#" + value = USERS["UID"] + users_value = id_str[1..-1].to_i + value + + when "@" + value = USERS["GID"] + users_value = id_str[1..-1].to_i + value + + when "*" + users_value = USERS["ALL"] + end + + return users_value + end + end +end diff --git a/src/oca/ruby/OpenNebula/AclPool.rb b/src/oca/ruby/OpenNebula/AclPool.rb new file mode 100644 index 0000000000..5f2de41e87 --- /dev/null +++ b/src/oca/ruby/OpenNebula/AclPool.rb @@ -0,0 +1,53 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +require 'OpenNebula/Pool' + +module OpenNebula + class AclPool < Pool + + ####################################################################### + # Constants and Class Methods + ####################################################################### + ACL_POOL_METHODS = { + :info => "acl.info", + :addrule => "acl.addrule", + :delrule => "acl.delrule" + } + + ####################################################################### + # Class constructor + ####################################################################### + def initialize(client) + super('ACL_POOL','ACL',client) + end + + def factory(element_xml) + acl=REXML::Document.new(element_xml).root + OpenNebula::Acl.new(acl['USER'], acl['RESOURCE'], acl['RIGHTS']) + end + + ####################################################################### + # XML-RPC Methods + ####################################################################### + + # Retrieves the ACL Pool + def info() + # Retrieves all the Acls in the pool. + super(ACL_POOL_METHODS[:info]) + end + end +end diff --git a/src/oca/ruby/OpenNebula/ImagePool.rb b/src/oca/ruby/OpenNebula/ImagePool.rb index 2d60908b63..4cec6b3840 100644 --- a/src/oca/ruby/OpenNebula/ImagePool.rb +++ b/src/oca/ruby/OpenNebula/ImagePool.rb @@ -47,9 +47,26 @@ module OpenNebula # XML-RPC Methods for the Image Object ####################################################################### - # Retrieves all or part of the Images in the pool. - def info() - super(IMAGE_POOL_METHODS[:info],@user_id) + # Retrieves all or part of the VirtualMachines in the pool. + def info(*args) + case args.size + when 0 + info_filter(IMAGE_POOL_METHODS[:info],@user_id,-1,-1) + when 3 + info_filter(IMAGE_POOL_METHODS[:info],args[0],args[1],args[2]) + end + end + + def info_all() + return super(IMAGE_POOL_METHODS[:info]) + end + + def info_mine() + return super(IMAGE_POOL_METHODS[:info]) + end + + def info_group() + return super(IMAGE_POOL_METHODS[:info]) end end end diff --git a/src/oca/ruby/OpenNebula/Pool.rb b/src/oca/ruby/OpenNebula/Pool.rb index 30fa180c51..2608848e84 100644 --- a/src/oca/ruby/OpenNebula/Pool.rb +++ b/src/oca/ruby/OpenNebula/Pool.rb @@ -22,7 +22,6 @@ module OpenNebula include Enumerable protected - #pool:: _String_ XML name of the root element #element:: _String_ XML name of the Pool elements #client:: _Client_ represents a XML-RPC connection @@ -48,13 +47,36 @@ module OpenNebula ####################################################################### # Common XML-RPC Methods for all the Pool Types ####################################################################### + + #Gets the pool without any filter. Host, Group and User Pools + # xml_method:: _String_ the name of the XML-RPC method + def info(xml_method) + return xmlrpc_info(xml_method) + end + def info_all(xml_method) + return xmlrpc_info(xml_method,INFO_ALL,-1,-1) + end + + def info_mine(xml_method) + return xmlrpc_info(xml_method,INFO_MINE,-1,-1) + end + + def info_group(xml_method) + return xmlrpc_info(xml_method,INFO_GROUP,-1,-1) + end + + def info_filter(xml_method, who, start_id, end_id) + return xmlrpc_info(xml_method,who, start_id, end_id) + end + + private # Calls to the corresponding info method to retreive the pool # representation in XML format # xml_method:: _String_ the name of the XML-RPC method # args:: _Array_ with additional arguments for the info call # [return] nil in case of success or an Error object - def info(xml_method,*args) + def xmlrpc_info(xml_method,*args) rc = @client.call(xml_method,*args) if !OpenNebula.is_error?(rc) @@ -66,6 +88,10 @@ module OpenNebula end public + # Constants for info queries (include/RequestManagerPoolInfoFilter.h) + INFO_GROUP = -1 + INFO_ALL = -2 + INFO_MINE = -3 # Iterates over every PoolElement in the Pool and calls the block with a # a PoolElement obtained calling the factory method diff --git a/src/oca/ruby/OpenNebula/TemplatePool.rb b/src/oca/ruby/OpenNebula/TemplatePool.rb index 8cebdd8877..c5174036bc 100644 --- a/src/oca/ruby/OpenNebula/TemplatePool.rb +++ b/src/oca/ruby/OpenNebula/TemplatePool.rb @@ -46,10 +46,26 @@ module OpenNebula # --------------------------------------------------------------------- # XML-RPC Methods for the Template Object # --------------------------------------------------------------------- + # Retrieves all or part of the VirtualMachines in the pool. + def info(*args) + case args.size + when 0 + info_filter(TEMPLATE_POOL_METHODS[:info],@user_id,-1,-1) + when 3 + info_filter(TEMPLATE_POOL_METHODS[:info],args[0],args[1],args[2]) + end + end - # Retrieves all the Templates in the pool. - def info() - super(TEMPLATE_POOL_METHODS[:info], @user_id) + def info_all() + return super(TEMPLATE_POOL_METHODS[:info]) + end + + def info_mine() + return super(TEMPLATE_POOL_METHODS[:info]) + end + + def info_group() + return super(TEMPLATE_POOL_METHODS[:info]) end end -end \ No newline at end of file +end diff --git a/src/oca/ruby/OpenNebula/VirtualMachinePool.rb b/src/oca/ruby/OpenNebula/VirtualMachinePool.rb index 766fd202dc..43b9ecf4e1 100644 --- a/src/oca/ruby/OpenNebula/VirtualMachinePool.rb +++ b/src/oca/ruby/OpenNebula/VirtualMachinePool.rb @@ -26,6 +26,10 @@ module OpenNebula :info => "vmpool.info" } + # Constants for info queries (include/RequestManagerPoolInfoFilter.h) + INFO_NOT_DONE = -1 + INFO_ALL_VM = -2 + ####################################################################### # Class constructor & Pool Methods ####################################################################### @@ -48,8 +52,66 @@ module OpenNebula ####################################################################### # Retrieves all or part of the VirtualMachines in the pool. - def info() - super(VM_POOL_METHODS[:info],@user_id) + # No arguments, returns the not-in-done VMs for the user + # [user_id, start_id, end_id] + # [user_id, start_id, end_id, state] + def info(*args) + case args.size + when 0 + info_filter(VM_POOL_METHODS[:info], + @user_id, + -1, + -1, + INFO_NOT_DONE) + when 1 + info_filter(VM_POOL_METHODS[:info], + args[0], + -1, + -1, + INFO_NOT_DONE) + when 3 + info_filter(VM_POOL_METHODS[:info], + args[0], + args[1], + args[2], + INFO_NOT_DONE) + when 4 + info_filter(VM_POOL_METHODS[:info], + args[0], + args[1], + args[2], + args[3]) + end + end + + def info_all() + return info_filter(VM_POOL_METHODS[:info], + INFO_ALL, + -1, + -1, + INFO_NOT_DONE) + end + + def info_mine() + return info_filter(VM_POOL_METHODS[:info], + INFO_MINE, + -1, + -1, + INFO_NOT_DONE) + end + + def info_group() + return info_filter(VM_POOL_METHODS[:info], + INFO_GROUP, + -1, + -1, + INFO_NOT_DONE) + end + + private + + def info_filter(xml_method, who, start_id, end_id, state) + return xmlrpc_info(xml_method, who, start_id, end_id, state) end end end diff --git a/src/oca/ruby/OpenNebula/VirtualNetworkPool.rb b/src/oca/ruby/OpenNebula/VirtualNetworkPool.rb index 0e41cc99ba..4267ac1830 100644 --- a/src/oca/ruby/OpenNebula/VirtualNetworkPool.rb +++ b/src/oca/ruby/OpenNebula/VirtualNetworkPool.rb @@ -47,9 +47,26 @@ module OpenNebula # XML-RPC Methods for the Virtual Network Object ####################################################################### - # Retrieves all or part of the VirtualNetwork in the pool. - def info() - super(VN_POOL_METHODS[:info],@user_id) + # Retrieves all or part of the VirtualMachines in the pool. + def info(*args) + case args.size + when 0 + info_filter(VN_POOL_METHODS[:info],@user_id,-1,-1) + when 3 + info_filter(VN_POOL_METHODS[:info],args[0],args[1],args[2]) + end + end + + def info_all() + return super(VN_POOL_METHODS[:info]) + end + + def info_mine() + return super(VN_POOL_METHODS[:info]) + end + + def info_group() + return super(VN_POOL_METHODS[:info]) end end end diff --git a/src/oca/ruby/test/HostPool_spec.rb b/src/oca/ruby/test/HostPool_spec.rb index 5d08319618..7c2d083330 100644 --- a/src/oca/ruby/test/HostPool_spec.rb +++ b/src/oca/ruby/test/HostPool_spec.rb @@ -1,4 +1,5 @@ -$: << '../' +$: << '../' \ + << './' require 'OpenNebula' require 'helpers/MockClient' @@ -78,4 +79,4 @@ module OpenNebula } end end -end \ No newline at end of file +end diff --git a/src/oca/ruby/test/Host_spec.rb b/src/oca/ruby/test/Host_spec.rb index e2d8cb39e4..5d0a585b19 100644 --- a/src/oca/ruby/test/Host_spec.rb +++ b/src/oca/ruby/test/Host_spec.rb @@ -1,4 +1,5 @@ -$: << '../' +$: << '../' \ + << './' require 'OpenNebula' require 'helpers/MockClient' @@ -216,4 +217,4 @@ module OpenNebula end end -end \ No newline at end of file +end diff --git a/src/oca/ruby/test/UserPool_spec.rb b/src/oca/ruby/test/UserPool_spec.rb index 7465cfc485..22f208b6af 100644 --- a/src/oca/ruby/test/UserPool_spec.rb +++ b/src/oca/ruby/test/UserPool_spec.rb @@ -1,4 +1,5 @@ -$: << '../' +$: << '../' \ + << './' require 'OpenNebula' require 'helpers/MockClient' @@ -62,4 +63,4 @@ module OpenNebula } end end -end \ No newline at end of file +end diff --git a/src/oca/ruby/test/User_spec.rb b/src/oca/ruby/test/User_spec.rb index a02911f03e..c3f25fdbc6 100644 --- a/src/oca/ruby/test/User_spec.rb +++ b/src/oca/ruby/test/User_spec.rb @@ -1,4 +1,5 @@ -$: << '../' +$: << '../' \ + << './' require 'OpenNebula' require 'helpers/MockClient' @@ -142,4 +143,4 @@ module OpenNebula end end -end \ No newline at end of file +end diff --git a/src/oca/ruby/test/VirtualMachinePool_spec.rb b/src/oca/ruby/test/VirtualMachinePool_spec.rb index cd138b5d2f..5d08cce65f 100644 --- a/src/oca/ruby/test/VirtualMachinePool_spec.rb +++ b/src/oca/ruby/test/VirtualMachinePool_spec.rb @@ -1,4 +1,5 @@ -$: << '../' +$: << '../' \ + << './' require 'OpenNebula' require 'helpers/MockClient' @@ -114,4 +115,4 @@ module OpenNebula } end end -end \ No newline at end of file +end diff --git a/src/oca/ruby/test/VirtualMachine_spec.rb b/src/oca/ruby/test/VirtualMachine_spec.rb index 4fc6c90996..982916fa5a 100644 --- a/src/oca/ruby/test/VirtualMachine_spec.rb +++ b/src/oca/ruby/test/VirtualMachine_spec.rb @@ -1,4 +1,5 @@ -$: << '../' +$: << '../' \ + << './' require 'OpenNebula' require 'helpers/MockClient' @@ -469,4 +470,4 @@ module OpenNebula @vm.name.should eql(nil) end end -end \ No newline at end of file +end diff --git a/src/oca/ruby/test/VirtualNetworkPool_spec.rb b/src/oca/ruby/test/VirtualNetworkPool_spec.rb index fd107827c6..5bebce9fa0 100644 --- a/src/oca/ruby/test/VirtualNetworkPool_spec.rb +++ b/src/oca/ruby/test/VirtualNetworkPool_spec.rb @@ -1,4 +1,5 @@ -$: << '../' +$: << '../' \ + << './' require 'OpenNebula' require 'helpers/MockClient' @@ -84,4 +85,4 @@ module OpenNebula } end end -end \ No newline at end of file +end diff --git a/src/oca/ruby/test/VirtualNetwork_spec.rb b/src/oca/ruby/test/VirtualNetwork_spec.rb index b74982bf52..3d3431da2f 100644 --- a/src/oca/ruby/test/VirtualNetwork_spec.rb +++ b/src/oca/ruby/test/VirtualNetwork_spec.rb @@ -1,4 +1,5 @@ -$: << '../' +$: << '../' \ + << './' require 'OpenNebula' require 'helpers/MockClient' @@ -152,4 +153,4 @@ module OpenNebula end end -end \ No newline at end of file +end diff --git a/src/pool/ObjectCollection.cc b/src/pool/ObjectCollection.cc index ec8da0934c..ba41fa621d 100644 --- a/src/pool/ObjectCollection.cc +++ b/src/pool/ObjectCollection.cc @@ -21,32 +21,38 @@ int ObjectCollection::from_xml_node(const xmlNodePtr node) { - ObjectXML xml(node); - int rc = 0; - int id; + xmlNodePtr cur_node = 0; + istringstream iss; + int id; + int rc = 0; - vector values; - vector::iterator it; - istringstream iss; - - string xpath_expr = "/" + collection_name + "/ID"; - - values = xml[xpath_expr.c_str()]; - - for ( it = values.begin() ; it < values.end(); it++ ) - { - iss.str(*it); - iss >> dec >> id; - - if ( iss.fail() ) + for (cur_node = node->children; cur_node != 0; cur_node = cur_node->next) + { + if ((cur_node->type == XML_ELEMENT_NODE) && + (cur_node->children != 0) && + ((cur_node->children->type == XML_TEXT_NODE ) || + (cur_node->children->type == XML_CDATA_SECTION_NODE))) { - rc = -1; + iss.clear(); + iss.str(reinterpret_cast(cur_node->children->content)); + iss >> dec >> id; + + if ( iss.fail() ) + { + rc = -1; + break; + } + else + { + collection_set.insert(id); + } } else { - collection_set.insert(id); + rc = -1; + break; } - } + } return rc; }; diff --git a/src/pool/test/TestPoolSQL.h b/src/pool/test/TestPoolSQL.h index d9eac8a5c2..9dd86a041d 100644 --- a/src/pool/test/TestPoolSQL.h +++ b/src/pool/test/TestPoolSQL.h @@ -30,7 +30,7 @@ class TestObjectSQL : public PoolObjectSQL { public: //OBJECT ATTRIBUTES - TestObjectSQL(int n=-1, string t="default"):PoolObjectSQL(-1,"",0,0,0),number(n),text(t){}; + TestObjectSQL(int n=-1, string t="default"):PoolObjectSQL(-1,"",0,0,"","",0),number(n),text(t){}; ~TestObjectSQL(){}; diff --git a/src/rm/Request.cc b/src/rm/Request.cc index a204af1e4a..f9079206ce 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -33,7 +33,12 @@ void Request::execute( NebulaLog::log("ReM",Log::DEBUG, method_name + " method invoked"); - if ( upool->authenticate(session, uid, gid) == false ) + if ( upool->authenticate(session, + uid, + gid, + uname, + gname, + group_ids) == false ) { failure_response(AUTHENTICATION, authenticate_error()); } @@ -46,24 +51,20 @@ void Request::execute( /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -bool Request::basic_authorization(int oid) +bool Request::basic_authorization(int oid, AuthRequest::Operation op) { PoolObjectSQL * object; - bool pub; - int ouid; + bool pub = false; + int ouid = 0; + int ogid = -1; if ( uid == 0 ) { return true; } - if ( oid == -1 ) - { - ouid = 0; - pub = false; - } - else + if ( oid >= 0 ) { object = pool->get(oid,true); @@ -74,17 +75,18 @@ bool Request::basic_authorization(int oid) } ouid = object->get_uid(); + ogid = object->get_gid(); pub = object->isPublic(); object->unlock(); } - AuthRequest ar(uid); + AuthRequest ar(uid, group_ids); - ar.add_auth(auth_object, oid, auth_op, ouid, pub); + ar.add_auth(auth_object, oid, ogid, op, ouid, pub); - if (UserPool::authorize(ar) == -1) - { + if (UserPool::authorize(ar) == -1) + { failure_response(AUTHORIZATION, authorization_error(ar.message)); return false; @@ -164,6 +166,8 @@ string Request::object_name(AuthRequest::Object ob) return "virtual machine template"; case AuthRequest::GROUP: return "group"; + case AuthRequest::ACL: + return "ACL"; default: return "-"; } diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index e8fca245bf..9cbf9401cc 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -33,6 +33,7 @@ #include "RequestManagerHost.h" #include "RequestManagerImage.h" #include "RequestManagerUser.h" +#include "RequestManagerAcl.h" #include #include @@ -310,6 +311,11 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr image_chown(new ImageChown()); xmlrpc_c::methodPtr user_chown(new UserChown()); + // ACL Methods + xmlrpc_c::methodPtr acl_addrule(new AclAddRule()); + xmlrpc_c::methodPtr acl_delrule(new AclDelRule()); + xmlrpc_c::methodPtr acl_info(new AclInfo()); + /* VM related methods */ RequestManagerRegistry.addMethod("one.vm.deploy", vm_deploy); RequestManagerRegistry.addMethod("one.vm.action", vm_action); @@ -383,6 +389,11 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.image.chown", image_chown); RequestManagerRegistry.addMethod("one.imagepool.info", imagepool_info); + + /* ACL related methods */ + RequestManagerRegistry.addMethod("one.acl.addrule", acl_addrule); + RequestManagerRegistry.addMethod("one.acl.delrule", acl_delrule); + RequestManagerRegistry.addMethod("one.acl.info", acl_info); }; /* -------------------------------------------------------------------------- */ diff --git a/src/rm/RequestManagerAcl.cc b/src/rm/RequestManagerAcl.cc new file mode 100644 index 0000000000..827e841c7d --- /dev/null +++ b/src/rm/RequestManagerAcl.cc @@ -0,0 +1,123 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#include "RequestManagerAcl.h" + +using namespace std; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +void AclAddRule::request_execute(xmlrpc_c::paramList const& paramList) +{ +/* + xmlrpc-c version 1.07 can manage 64 bit numbers, but not all distros. ship + the latest version. + + user = xmlrpc_c::value_i8(paramList.getI8(1)); + resource = xmlrpc_c::value_i8(paramList.getI8(2)); + rights = xmlrpc_c::value_i8(paramList.getI8(3)); +*/ + long long user; + long long resource; + long long rights; + + istringstream iss; + + iss.str( xmlrpc_c::value_string(paramList.getString(1)) ); + iss >> hex >> user; + + iss.clear(); + iss.str( xmlrpc_c::value_string(paramList.getString(2)) ); + iss >> hex >> resource; + + iss.clear(); + iss.str( xmlrpc_c::value_string(paramList.getString(3)) ); + iss >> hex >> rights; + + string error_msg; + + if ( basic_authorization(-1) == false ) + { + return; + } + + int rc = aclm->add_rule(user, resource, rights, error_msg); + + if ( rc < 0 ) + { + failure_response(INTERNAL, request_error(error_msg, "")); + return; + } + + success_response(rc); + + return; +} + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +void AclDelRule::request_execute(xmlrpc_c::paramList const& paramList) +{ + int oid = xmlrpc_c::value_int(paramList.getInt(1)); + string error_msg; + + if ( basic_authorization(-1) == false ) + { + return; + } + + int rc = aclm->del_rule(oid, error_msg); + + if ( rc < 0 ) + { + failure_response(INTERNAL, request_error(error_msg, "")); + return; + } + + success_response(oid); + + return; +} + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +void AclInfo::request_execute(xmlrpc_c::paramList const& paramList) +{ + ostringstream oss; + int rc; + + if ( basic_authorization(-1) == false ) + { + return; + } + + rc = aclm->dump(oss); + + if ( rc != 0 ) + { + failure_response(INTERNAL, request_error("Internal Error","")); + return; + } + + success_response(oss.str()); + + return; +} + +/* ------------------------------------------------------------------------- */ diff --git a/src/rm/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index 551c156703..175c92b5b0 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -30,17 +30,17 @@ bool RequestManagerAllocate::allocate_authorization(Template * tmpl) return true; } - AuthRequest ar(uid); + AuthRequest ar(uid, group_ids); if ( tmpl == 0 ) { - ar.add_auth(auth_object,-1,auth_op,uid,false); + ar.add_auth(auth_object,-1,-1,auth_op,uid,false); } else { string t64; - ar.add_auth(auth_object,tmpl->to_xml(t64),auth_op,uid,false); + ar.add_auth(auth_object,tmpl->to_xml(t64),-1,auth_op,uid,false); } if (UserPool::authorize(ar) == -1) @@ -62,12 +62,13 @@ bool VirtualMachineAllocate::allocate_authorization(Template * tmpl) return true; } - AuthRequest ar(uid); + AuthRequest ar(uid, group_ids); + string t64; VirtualMachineTemplate * ttmpl = static_cast(tmpl); - ar.add_auth(auth_object,tmpl->to_xml(t64),auth_op,uid,false); + ar.add_auth(auth_object,tmpl->to_xml(t64),-1,auth_op,uid,false); VirtualMachine::set_auth_request(uid, ar, ttmpl); @@ -132,10 +133,10 @@ int VirtualMachineAllocate::pool_allocate(xmlrpc_c::paramList const& paramList, int& id, string& error_str) { - VirtualMachineTemplate * ttmpl = static_cast(tmpl); - VirtualMachinePool * vmpool = static_cast(pool); + VirtualMachineTemplate * ttmpl= static_cast(tmpl); + VirtualMachinePool * vmpool = static_cast(pool); - return vmpool->allocate(uid, gid, ttmpl, &id, error_str, false); + return vmpool->allocate(uid, gid, uname, gname, ttmpl, &id,error_str,false); } @@ -150,7 +151,7 @@ int VirtualNetworkAllocate::pool_allocate(xmlrpc_c::paramList const& _paramList, VirtualNetworkPool * vpool = static_cast(pool); VirtualNetworkTemplate * vtmpl=static_cast(tmpl); - return vpool->allocate(uid, gid, vtmpl, &id, error_str); + return vpool->allocate(uid, gid, uname, gname, vtmpl, &id, error_str); } /* -------------------------------------------------------------------------- */ @@ -164,7 +165,7 @@ int ImageAllocate::pool_allocate(xmlrpc_c::paramList const& _paramList, ImagePool * ipool = static_cast(pool); ImageTemplate * itmpl = static_cast(tmpl); - return ipool->allocate(uid, gid, itmpl, &id, error_str); + return ipool->allocate(uid, gid, uname, gname, itmpl, &id, error_str); } /* -------------------------------------------------------------------------- */ @@ -179,7 +180,7 @@ int TemplateAllocate::pool_allocate(xmlrpc_c::paramList const& _paramList, VirtualMachineTemplate * ttmpl=static_cast(tmpl); - return tpool->allocate(uid, gid, ttmpl, &id, error_str); + return tpool->allocate(uid, gid, uname, gname, ttmpl, &id, error_str); } /* -------------------------------------------------------------------------- */ @@ -212,14 +213,17 @@ int UserAllocate::pool_allocate(xmlrpc_c::paramList const& paramList, string passwd = xmlrpc_c::value_string(paramList.getString(2)); UserPool * upool = static_cast(pool); - int users_group = gid; + + int ugid = gid; + string ugname = gname; if ( gid == GroupPool::ONEADMIN_ID ) { - users_group = GroupPool::USERS_ID; + ugid = GroupPool::USERS_ID; + ugname = GroupPool::USERS_NAME; } - return upool->allocate(&id,users_group,uname,passwd,true,error_str); + return upool->allocate(&id,ugid,uname,ugname,passwd,true,error_str); } /* -------------------------------------------------------------------------- */ diff --git a/src/rm/RequestManagerChown.cc b/src/rm/RequestManagerChown.cc index f2910aefe0..6e5c37b199 100644 --- a/src/rm/RequestManagerChown.cc +++ b/src/rm/RequestManagerChown.cc @@ -28,6 +28,9 @@ void RequestManagerChown::request_execute(xmlrpc_c::paramList const& paramList) int noid = xmlrpc_c::value_int(paramList.getInt(2)); int ngid = xmlrpc_c::value_int(paramList.getInt(3)); + string nuname; + string ngname; + Nebula& nd = Nebula::instance(); GroupPool * gpool = nd.get_gpool(); UserPool * upool = nd.get_upool(); @@ -41,18 +44,36 @@ void RequestManagerChown::request_execute(xmlrpc_c::paramList const& paramList) // ------------- Check new user and group id's --------------------- - if ( noid > -1 && upool->get(noid,false) == 0 ) + if ( noid > -1 ) { - failure_response(NO_EXISTS, + User * user; + + if ((user = upool->get(noid,true)) == 0) + { + failure_response(NO_EXISTS, get_error(object_name(AuthRequest::USER),noid)); - return; + return; + } + + nuname = user->get_name(); + + user->unlock(); } - if ( ngid > -1 && gpool->get(ngid,false) == 0 ) + if ( ngid > -1 ) { - failure_response(NO_EXISTS, + Group * group; + + if ((group = gpool->get(ngid,true)) == 0) + { + failure_response(NO_EXISTS, get_error(object_name(AuthRequest::GROUP),ngid)); - return; + return; + } + + ngname = group->get_name(); + + group->unlock(); } // ------------- Update the object --------------------- @@ -67,12 +88,12 @@ void RequestManagerChown::request_execute(xmlrpc_c::paramList const& paramList) if ( noid != -1 ) { - object->set_uid(noid); + object->set_user(noid,nuname); } if ( ngid != -1 ) { - object->set_gid(ngid); + object->set_group(ngid,ngname); } pool->update(object); @@ -93,7 +114,7 @@ void UserChown::request_execute(xmlrpc_c::paramList const& paramList) int ngid = xmlrpc_c::value_int(paramList.getInt(2)); int old_gid; - string str; + string ngname; Nebula& nd = Nebula::instance(); GroupPool * gpool = nd.get_gpool(); @@ -114,13 +135,18 @@ void UserChown::request_execute(xmlrpc_c::paramList const& paramList) failure_response(XML_RPC_API,request_error("Wrong group ID","")); return; } - else if ( gpool->get(ngid,false) == 0 ) + + if ( (group = gpool->get(ngid,true)) == 0 ) { failure_response(NO_EXISTS, get_error(object_name(AuthRequest::GROUP),ngid)); return; } + ngname = group->get_name(); + + group->unlock(); + // ------------- Change users primary group --------------------- user = upool->get(oid,true); @@ -139,7 +165,7 @@ void UserChown::request_execute(xmlrpc_c::paramList const& paramList) return; } - user->set_gid(ngid); + user->set_group(ngid,ngname); user->add_group(ngid); user->del_group(old_gid); @@ -154,7 +180,8 @@ void UserChown::request_execute(xmlrpc_c::paramList const& paramList) if( group == 0 ) { - get_error(object_name(AuthRequest::GROUP),ngid); //TODO Rollback + failure_response(NO_EXISTS, + get_error(object_name(AuthRequest::GROUP),ngid));//TODO Rollback return; } diff --git a/src/rm/RequestManagerPoolInfoFilter.cc b/src/rm/RequestManagerPoolInfoFilter.cc index 88a4fe2647..37e416294d 100644 --- a/src/rm/RequestManagerPoolInfoFilter.cc +++ b/src/rm/RequestManagerPoolInfoFilter.cc @@ -27,49 +27,168 @@ const int RequestManagerPoolInfoFilter::MINE = -3; const int RequestManagerPoolInfoFilter::MINE_GROUP = -1; +/* ------------------------------------------------------------------------- */ + +const int VirtualMachinePoolInfo::ALL_VM = -2; + +const int VirtualMachinePoolInfo::NOT_DONE = -1; + /* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ void RequestManagerPoolInfoFilter::request_execute(xmlrpc_c::paramList const& paramList) { int filter_flag = xmlrpc_c::value_int(paramList.getInt(1)); + int start_id = xmlrpc_c::value_int(paramList.getInt(2)); + int end_id = xmlrpc_c::value_int(paramList.getInt(3)); - ostringstream oss, where_string; + set::iterator it; + + ostringstream oss; + bool empty = true; + ostringstream where_string; + + ostringstream uid_filter; + ostringstream state_filter; + ostringstream id_filter; + + string uid_str; + string state_str; + string id_str; int rc; + AuthRequest::Operation request_op; + + // ------------------------------------------ + // User ID filter + // ------------------------------------------ + if ( filter_flag < MINE ) { - failure_response(XML_RPC_API, request_error("Incorrect filter_flag","")); + failure_response(XML_RPC_API,request_error("Incorrect filter_flag","")); return; } - + switch(filter_flag) { case MINE: - where_string << "UID=" << uid; - auth_op = AuthRequest::INFO_POOL_MINE; + uid_filter << "uid = " << uid; + + request_op = AuthRequest::INFO_POOL_MINE; break; case ALL: + request_op = AuthRequest::INFO_POOL; break; case MINE_GROUP: - where_string << "UID=" << uid << " OR GID=" << gid; - auth_op = AuthRequest::INFO_POOL_MINE; + + uid_filter << "uid = " << uid; + + for ( it = group_ids.begin() ; it != group_ids.end(); it++ ) + { + uid_filter << " OR gid = " << *it; + } + + request_op = AuthRequest::INFO_POOL_MINE; break; default: - where_string << "UID=" << filter_flag; + uid_filter << "uid = " << filter_flag; + + request_op = AuthRequest::INFO_POOL; break; } - if ( basic_authorization(-1) == false ) + uid_str = uid_filter.str(); + + + // ------------------------------------------ + // Resource ID filter + // ------------------------------------------ + + if ( start_id != -1 ) + { + id_filter << "oid >= " << start_id; + + if ( end_id != -1 ) + { + id_filter << " AND oid <= " << end_id; + } + } + + id_str = id_filter.str(); + + // ------------ State filter for VM -------------- + if ( auth_object == AuthRequest::VM ) + { + int state = xmlrpc_c::value_int(paramList.getInt(4)); + + if (( state < MINE ) || ( state > VirtualMachine::FAILED )) + { + failure_response(XML_RPC_API, + request_error("Incorrect filter_flag, state","")); + return; + } + + switch(state) + { + case VirtualMachinePoolInfo::ALL_VM: + break; + + case VirtualMachinePoolInfo::NOT_DONE: + state_filter << "state <> " << VirtualMachine::DONE; + break; + + default: + state_filter << "state = " << state; + break; + } + } + + state_str = state_filter.str(); + + // ------------------------------------------ + // Compound WHERE clause + // ------------------------------------------ + + if (!uid_str.empty()) + { + where_string << "(" << uid_str << ")" ; + empty = false; + } + + if (!id_str.empty()) + { + if (!empty) + { + where_string << " AND "; + } + + where_string << "(" << id_str << ")"; + empty = false; + } + + if (!state_str.empty()) + { + if (!empty) + { + where_string << " AND "; + } + + where_string << "(" << state_str << ")"; + } + + // ------------------------------------------ + // Authorize & get the pool + // ------------------------------------------ + + if ( basic_authorization(-1, request_op) == false ) { return; } - - // Call the template pool dump + rc = pool->dump(oss,where_string.str()); if ( rc != 0 ) diff --git a/src/rm/RequestManagerVMTemplate.cc b/src/rm/RequestManagerVMTemplate.cc index b93ca5f202..39e9e0aea0 100644 --- a/src/rm/RequestManagerVMTemplate.cc +++ b/src/rm/RequestManagerVMTemplate.cc @@ -25,7 +25,7 @@ void VMTemplateInstantiate::request_execute(xmlrpc_c::paramList const& paramList int id = xmlrpc_c::value_int(paramList.getInt(1)); string name = xmlrpc_c::value_string(paramList.getString(2)); - int rc, ouid, vid; + int rc, ouid, ogid, vid; Nebula& nd = Nebula::instance(); VirtualMachinePool* vmpool = nd.get_vmpool(); @@ -46,6 +46,7 @@ void VMTemplateInstantiate::request_execute(xmlrpc_c::paramList const& paramList tmpl = rtmpl->clone_template(); ouid = rtmpl->get_uid(); + ogid = rtmpl->get_gid(); rtmpl->unlock(); @@ -54,9 +55,9 @@ void VMTemplateInstantiate::request_execute(xmlrpc_c::paramList const& paramList if ( uid != 0 ) { - AuthRequest ar(uid); + AuthRequest ar(uid, group_ids); - ar.add_auth(auth_object, id, auth_op, ouid, false); + ar.add_auth(auth_object, id, ogid, auth_op, ouid, false); VirtualMachine::set_auth_request(uid, ar, tmpl); @@ -68,7 +69,7 @@ void VMTemplateInstantiate::request_execute(xmlrpc_c::paramList const& paramList } } - rc = vmpool->allocate(uid, gid, tmpl, &vid, error_str, false); + rc = vmpool->allocate(uid, gid, uname, gname, tmpl, &vid, error_str, false); if ( rc < 0 ) { diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index 10142230c0..de02b00f1b 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -25,6 +25,7 @@ bool RequestManagerVirtualMachine::vm_authorization(int oid, int hid, ImageTempl PoolObjectSQL * object; int ouid; + int ogid; if ( uid == 0 ) { @@ -40,16 +41,17 @@ bool RequestManagerVirtualMachine::vm_authorization(int oid, int hid, ImageTempl } ouid = object->get_uid(); + ogid = object->get_gid(); object->unlock(); - AuthRequest ar(uid); + AuthRequest ar(uid, group_ids); - ar.add_auth(auth_object, oid, auth_op, ouid, false); + ar.add_auth(auth_object, oid, ogid, auth_op, ouid, false); if (hid != -1) { - ar.add_auth(AuthRequest::HOST,hid,AuthRequest::USE,0,false); + ar.add_auth(AuthRequest::HOST,hid,-1,AuthRequest::USE,0,false); } else if (tmpl != 0) { @@ -57,6 +59,7 @@ bool RequestManagerVirtualMachine::vm_authorization(int oid, int hid, ImageTempl ar.add_auth(AuthRequest::IMAGE, tmpl->to_xml(t64), + -1, AuthRequest::CREATE, uid, false); @@ -395,7 +398,7 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis // ------------------ Create the image ------------------ - rc = ipool->allocate(uid, gid, itemplate, &iid,error_str); + rc = ipool->allocate(uid, gid, uname, gname, itemplate, &iid,error_str); if ( rc < 0 ) { diff --git a/src/rm/SConstruct b/src/rm/SConstruct index a025765a41..b6880c9758 100644 --- a/src/rm/SConstruct +++ b/src/rm/SConstruct @@ -38,36 +38,7 @@ source_files=[ 'RequestManagerHost.cc', 'RequestManagerImage.cc', 'RequestManagerChown.cc', - -# 'RequestManagerAction.cc', -# 'RequestManagerAllocate.cc', -# 'RequestManagerDeploy.cc', -# 'RequestManagerMigrate.cc', -# 'RequestManagerSaveDisk.cc', -# 'RequestManagerHostAllocate.cc', -# 'RequestManagerHostEnable.cc', -# 'RequestManagerImageAllocate.cc', -# 'RequestManagerImageUpdate.cc', -# 'RequestManagerImageRemoveAttribute.cc', -# 'RequestManagerImagePublish.cc', -# 'RequestManagerImagePersistent.cc', -# 'RequestManagerImageEnable.cc', -# 'RequestManagerClusterAdd.cc', -# 'RequestManagerClusterAllocate.cc', -# 'RequestManagerClusterRemove.cc', -# 'RequestManagerGroupAllocate.cc', -# 'RequestManagerVirtualNetworkAllocate.cc', -# 'RequestManagerVirtualNetworkInfo.cc', -# 'RequestManagerVirtualNetworkPublish.cc', -# 'RequestManagerVirtualNetworkAddLeases.cc', -# 'RequestManagerVirtualNetworkRemoveLeases.cc', -# 'RequestManagerUserAllocate.cc', -# 'RequestManagerUserChangePassword.cc', -# 'RequestManagerTemplateAllocate.cc', -# 'RequestManagerTemplateUpdate.cc', -# 'RequestManagerTemplateRemoveAttribute.cc', -# 'RequestManagerTemplatePublish.cc', -# 'RequestManagerChown.cc', + 'RequestManagerAcl.cc', ] # Build library diff --git a/src/scheduler/include/AclXML.h b/src/scheduler/include/AclXML.h new file mode 100644 index 0000000000..d3aecd593c --- /dev/null +++ b/src/scheduler/include/AclXML.h @@ -0,0 +1,83 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#ifndef ACL_XML_H_ +#define ACL_XML_H_ + +#include "AclManager.h" +#include "Client.h" + +using namespace std; + +/** + * This class manages the ACL rules and the authorization engine + */ +class AclXML : public AclManager +{ +public: + AclXML(Client * _client):AclManager(), client(_client){}; + + virtual ~AclXML(){}; + + /** + * Loads the ACL rule set from the DB + * @return 0 on success. + */ + int set_up(); + +private: + /* ---------------------------------------------------------------------- */ + /* Re-implement DB public functions not used in scheduler */ + /* ---------------------------------------------------------------------- */ + int start() + { + return -1; + } + + int add_rule(long long user, + long long resource, + long long rights, + string& error_str) + { + return -1; + }; + + int del_rule(int oid, string& error_str) + { + return -1; + }; + + int dump(ostringstream& oss) + { + return -1; + }; + + Client * client; + + /** + * Loads the ACL rule set from its XML representation: + * as obtained by a dump call + * + * @param xml_str string with the XML document for the ACL + * @return 0 on success. + */ + int load_rules(const string& xml_str); + + void flush_rules(); +}; + +#endif /*ACL_XML_H*/ + diff --git a/src/scheduler/include/HostPoolXML.h b/src/scheduler/include/HostPoolXML.h index deead4b2f8..8160262df6 100644 --- a/src/scheduler/include/HostPoolXML.h +++ b/src/scheduler/include/HostPoolXML.h @@ -49,9 +49,9 @@ protected: return get_nodes("/HOST_POOL/HOST[STATE<3]", content); }; - virtual void add_object(xmlNodePtr node); + void add_object(xmlNodePtr node); - virtual int load_info(xmlrpc_c::value &result); + int load_info(xmlrpc_c::value &result); }; #endif /* HOST_POOL_XML_H_ */ diff --git a/src/scheduler/include/PoolXML.h b/src/scheduler/include/PoolXML.h index e206524de8..4acc8e3f51 100644 --- a/src/scheduler/include/PoolXML.h +++ b/src/scheduler/include/PoolXML.h @@ -53,7 +53,7 @@ public: flush(); // ------------------------------------------------------------------------- - // Load the ids (to get an updated list of hosts) + // Load the ids (to get an updated list of the pool) // ------------------------------------------------------------------------- xmlrpc_c::value result; @@ -97,6 +97,8 @@ public: add_object(nodes[i]); } + free_nodes(nodes); + return 0; }; diff --git a/src/scheduler/include/Scheduler.h b/src/scheduler/include/Scheduler.h index 6cd9886af7..93e2ff7345 100644 --- a/src/scheduler/include/Scheduler.h +++ b/src/scheduler/include/Scheduler.h @@ -18,10 +18,12 @@ #define SCHEDULER_H_ #include "Log.h" +#include "UserPoolXML.h" #include "HostPoolXML.h" #include "VirtualMachinePoolXML.h" #include "SchedulerPolicy.h" #include "ActionManager.h" +#include "AclXML.h" using namespace std; @@ -48,6 +50,8 @@ protected: int _machines_limit, int _dispatch_limit, int _host_dispatch_limit): hpool(0), vmpool(0), + upool(0), + acls(0), timer(_timer), url(_url), machines_limit(_machines_limit), @@ -71,6 +75,16 @@ protected: delete vmpool; } + if ( upool != 0) + { + delete upool; + } + + if ( acls != 0) + { + delete acls; + } + if ( client != 0) { delete client; @@ -83,6 +97,8 @@ protected: HostPoolXML * hpool; VirtualMachinePoolXML * vmpool; + UserPoolXML * upool; + AclXML * acls; // --------------------------------------------------------------- // Scheduler Policies diff --git a/src/scheduler/include/UserPoolXML.h b/src/scheduler/include/UserPoolXML.h new file mode 100644 index 0000000000..44d151c71c --- /dev/null +++ b/src/scheduler/include/UserPoolXML.h @@ -0,0 +1,57 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + + +#ifndef USER_POOL_XML_H_ +#define USER_POOL_XML_H_ + +#include "PoolXML.h" +#include "UserXML.h" + +using namespace std; + +class UserPoolXML : public PoolXML +{ +public: + + UserPoolXML(Client* client):PoolXML(client){}; + + int set_up(); + + /** + * Gets an object from the pool + * @param oid the object unique identifier + * + * @return a pointer to the object, 0 in case of failure + */ + UserXML * get(int oid) const + { + return static_cast(PoolXML::get(oid)); + }; + +protected: + + int get_suitable_nodes(vector& content) + { + return get_nodes("/USER_POOL/USER[ENABLED=1]", content); + }; + + void add_object(xmlNodePtr node); + + int load_info(xmlrpc_c::value &result); +}; + +#endif /* HOST_POOL_XML_H_ */ diff --git a/src/scheduler/include/UserXML.h b/src/scheduler/include/UserXML.h new file mode 100644 index 0000000000..ede214cd52 --- /dev/null +++ b/src/scheduler/include/UserXML.h @@ -0,0 +1,63 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + + +#ifndef USER_XML_H_ +#define USER_XML_H_ + +#include "ObjectXML.h" +#include + +using namespace std; + +class UserXML : public ObjectXML +{ +public: + UserXML(const string &xml_doc):ObjectXML(xml_doc) + { + init_attributes(); + }; + + UserXML(const xmlNodePtr node):ObjectXML(node) + { + init_attributes(); + }; + + int get_uid() + { + return oid; + }; + + int get_gid() + { + return gid; + }; + + const set& get_groups() + { + return group_ids; + }; + +private: + int oid; + int gid; + + set group_ids; + + void init_attributes(); +}; + +#endif /* USER_XML_H_ */ diff --git a/src/scheduler/include/VirtualMachineXML.h b/src/scheduler/include/VirtualMachineXML.h index ed2ad44b7c..5a6baec137 100644 --- a/src/scheduler/include/VirtualMachineXML.h +++ b/src/scheduler/include/VirtualMachineXML.h @@ -46,6 +46,11 @@ public: return oid; }; + int get_uid() const + { + return uid; + }; + /** * Adds a new share to the map of suitable shares to start this VM * @param hid of the selected host @@ -138,6 +143,8 @@ protected: */ int oid; + int uid; + int memory; float cpu; diff --git a/src/scheduler/src/pool/AclXML.cc b/src/scheduler/src/pool/AclXML.cc new file mode 100644 index 0000000000..d9ed6b4954 --- /dev/null +++ b/src/scheduler/src/pool/AclXML.cc @@ -0,0 +1,114 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#include "AclXML.h" +#include "ObjectXML.h" +#include + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int AclXML::set_up() +{ + xmlrpc_c::value result; + + try + { + client->call(client->get_endpoint(), // serverUrl + "one.acl.info", // methodName + "s", // arguments format + &result, // resultP + client->get_oneauth().c_str());// argument + + vector values = + xmlrpc_c::value_array(result).vectorValueValue(); + + bool success = xmlrpc_c::value_boolean(values[0]); + string message = xmlrpc_c::value_string(values[1]); + + if( !success ) + { + ostringstream oss; + + oss << "ONE returned error while retrieving the acls:" << endl; + oss << message; + + NebulaLog::log("ACL", Log::ERROR, oss); + return -1; + } + + flush_rules(); + + load_rules(message); + + return 0; + } + catch (exception const& e) + { + ostringstream oss; + oss << "Exception raised: " << e.what(); + + NebulaLog::log("ACL", Log::ERROR, oss); + + return -1; + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int AclXML::load_rules(const string& xml_str) +{ + ObjectXML acl_xml(xml_str); + + vector rules; + vector::iterator it; + + acl_xml.get_nodes("/ACL_POOL/ACL",rules); + + for (it = rules.begin(); it != rules.end() ; it++) + { + AclRule * rule = new AclRule(0,0,0,0); + int rc = rule->from_xml(*it); + + if ( rc == 0 ) + { + acl_rules.insert( make_pair(rule->get_user(), rule) ); + acl_rules_oids.insert( make_pair(rule->get_oid(), rule) ); + } + } + + acl_xml.free_nodes(rules); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void AclXML::flush_rules() +{ + multimap::iterator it; + + for ( it = acl_rules.begin(); it != acl_rules.end(); it++ ) + { + delete it->second; + } + + acl_rules.clear(); + acl_rules_oids.clear(); +} + diff --git a/src/scheduler/src/pool/SConstruct b/src/scheduler/src/pool/SConstruct index 31fe496bf4..428b17b01b 100644 --- a/src/scheduler/src/pool/SConstruct +++ b/src/scheduler/src/pool/SConstruct @@ -21,6 +21,9 @@ Import('sched_env') lib_name='scheduler_pool' source_files=[ + 'AclXML.cc', + 'UserPoolXML.cc', + 'UserXML.cc', 'HostPoolXML.cc', 'HostXML.cc', 'VirtualMachinePoolXML.cc', diff --git a/src/scheduler/src/pool/UserPoolXML.cc b/src/scheduler/src/pool/UserPoolXML.cc new file mode 100644 index 0000000000..04252e45cf --- /dev/null +++ b/src/scheduler/src/pool/UserPoolXML.cc @@ -0,0 +1,90 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#include "UserPoolXML.h" + + +int UserPoolXML::set_up() +{ + ostringstream oss; + int rc; + + rc = PoolXML::set_up(); + + if ( rc == 0 ) + { + oss.str(""); + oss << "Users (enabled):"; + + map::iterator it; + + for (it=objects.begin();it!=objects.end();it++) + { + oss << " " << it->first; + } + + NebulaLog::log("HOST",Log::DEBUG,oss); + } + + return rc; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void UserPoolXML::add_object(xmlNodePtr node) +{ + if ( node == 0 || node->children == 0 ) + { + NebulaLog::log("USER",Log::ERROR, + "XML Node does not represent a valid User"); + + return; + } + + UserXML* user = new UserXML(node); + + objects.insert(pair(user->get_uid(), user)); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int UserPoolXML::load_info(xmlrpc_c::value &result) +{ + try + { + client->call(client->get_endpoint(), // serverUrl + "one.userpool.info", // methodName + "s", // arguments format + &result, // resultP + client->get_oneauth().c_str()); // argument + return 0; + } + catch (exception const& e) + { + ostringstream oss; + oss << "Exception raised: " << e.what(); + + NebulaLog::log("USER", Log::ERROR, oss); + + return -1; + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + diff --git a/src/scheduler/src/pool/UserXML.cc b/src/scheduler/src/pool/UserXML.cc new file mode 100644 index 0000000000..95cfb108d0 --- /dev/null +++ b/src/scheduler/src/pool/UserXML.cc @@ -0,0 +1,74 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +#include "UserXML.h" +#include + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void UserXML::init_attributes() +{ + vector content; + + oid = atoi(((*this)["/USER/ID"] )[0].c_str() ); + gid = atoi(((*this)["/USER/GID"] )[0].c_str() ); + + get_nodes("/USER/GROUPS",content); + + if (!content.empty()) + { + xmlNodePtr cur_node = 0; + istringstream iss; + int id; + + for (cur_node = content[0]->children; + cur_node != 0; + cur_node = cur_node->next) + { + if ((cur_node->type == XML_ELEMENT_NODE) && + (cur_node->children != 0) && + ((cur_node->children->type == XML_TEXT_NODE ) || + (cur_node->children->type == XML_CDATA_SECTION_NODE))) + { + iss.clear(); + iss.str(reinterpret_cast(cur_node->children->content)); + iss >> dec >> id; + + if ( iss.fail() ) + { + //TODO Print a warning message + break; + } + else + { + group_ids.insert(id); + } + } + else + { + //TODO Print a warning message + break; + } + } + } + + free_nodes(content); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + diff --git a/src/scheduler/src/pool/VirtualMachinePoolXML.cc b/src/scheduler/src/pool/VirtualMachinePoolXML.cc index 2cb47da06d..f81e359a65 100644 --- a/src/scheduler/src/pool/VirtualMachinePoolXML.cc +++ b/src/scheduler/src/pool/VirtualMachinePoolXML.cc @@ -64,13 +64,15 @@ int VirtualMachinePoolXML::load_info(xmlrpc_c::value &result) { try { - client->call(client->get_endpoint(), // serverUrl - "one.vmpool.info", // methodName - "sii", // arguments format - &result, // resultP - client->get_oneauth().c_str(), // auth string - -2, // VM from all users - 1); // in pending state + client->call(client->get_endpoint(), // serverUrl + "one.vmpool.info", // methodName + "siiii", // arguments format + &result, // resultP + client->get_oneauth().c_str(), // auth string + -2, // VM from all users + -1, // start_id (none) + -1, // end_id (none) + 1); // in pending state return 0; } catch (exception const& e) diff --git a/src/scheduler/src/pool/VirtualMachineXML.cc b/src/scheduler/src/pool/VirtualMachineXML.cc index d64ede4c47..ce0cdb4667 100644 --- a/src/scheduler/src/pool/VirtualMachineXML.cc +++ b/src/scheduler/src/pool/VirtualMachineXML.cc @@ -22,7 +22,8 @@ void VirtualMachineXML::init_attributes() { vector result; - oid = atoi(((*this)["/VM/ID"] )[0].c_str() ); + oid = atoi(((*this)["/VM/ID"] )[0].c_str()); + uid = atoi(((*this)["/VM/UID"])[0].c_str()); result = ((*this)["/VM/TEMPLATE/MEMORY"]); if (result.size() > 0) diff --git a/src/scheduler/src/sched/SConstruct b/src/scheduler/src/sched/SConstruct index 41cb064c98..e66ebfacf2 100644 --- a/src/scheduler/src/sched/SConstruct +++ b/src/scheduler/src/sched/SConstruct @@ -32,6 +32,7 @@ sched_env.Prepend(LIBS=[ 'scheduler_pool', 'nebula_log', 'scheduler_client', + 'nebula_acl', 'nebula_xml', 'nebula_common', 'crypto', diff --git a/src/scheduler/src/sched/Scheduler.cc b/src/scheduler/src/sched/Scheduler.cc index 7bd288d11c..fe0f057841 100644 --- a/src/scheduler/src/sched/Scheduler.cc +++ b/src/scheduler/src/sched/Scheduler.cc @@ -121,6 +121,8 @@ void Scheduler::start() hpool = new HostPoolXML(client); vmpool = new VirtualMachinePoolXML(client, machines_limit); + upool = new UserPoolXML(client); + acls = new AclXML(client); // ----------------------------------------------------------- // Load scheduler policies @@ -228,6 +230,28 @@ int Scheduler::set_up_pools() return rc; } + //-------------------------------------------------------------------------- + //Cleans the cache and get the users + //-------------------------------------------------------------------------- + + rc = upool->set_up(); + + if ( rc != 0 ) + { + return rc; + } + + //-------------------------------------------------------------------------- + //Cleans the cache and get the ACLs + //-------------------------------------------------------------------------- + + rc = acls->set_up(); + + if ( rc != 0 ) + { + return rc; + } + //-------------------------------------------------------------------------- //Get the matching hosts for each VM //-------------------------------------------------------------------------- @@ -246,6 +270,7 @@ void Scheduler::match() int vm_memory; int vm_cpu; int vm_disk; + int uid; string reqs; HostXML * host; @@ -254,6 +279,9 @@ void Scheduler::match() char * error; bool matched; + UserXML * user; + set gids; + int rc; map::const_iterator vm_it; @@ -268,8 +296,9 @@ void Scheduler::match() vm = static_cast(vm_it->second); reqs = vm->get_requirements(); + uid = vm->get_uid(); - for (h_it=hosts.begin(); h_it != hosts.end(); h_it++) + for (h_it=hosts.begin(), matched=false; h_it != hosts.end(); h_it++) { host = static_cast(h_it->second); @@ -298,12 +327,58 @@ void Scheduler::match() { matched = true; } - + if ( matched == false ) + { + ostringstream oss; + + oss << "Host " << host->get_hid() << + " filtered out. It does not fullfil REQUIREMENTS."; + + NebulaLog::log("SCHED",Log::DEBUG,oss); + continue; + } + + // ----------------------------------------------------------------- + // Check if user is authorized + // ----------------------------------------------------------------- + + user = upool->get(uid); + matched = false; + + if ( user != 0 ) + { + const set groups = user->get_groups(); + + if ( uid == 0 || user->get_gid() == 0 ) + { + matched = true; + } + else + { + matched = acls->authorize(uid, + groups, + AuthRequest::HOST, + host->get_hid(), + -1, + AuthRequest::USE); + } + } + else { continue; } + if ( matched == false ) + { + ostringstream oss; + + oss << "Host " << host->get_hid() << + " filtered out. User is not authorized to use it."; + + NebulaLog::log("SCHED",Log::DEBUG,oss); + continue; + } // ----------------------------------------------------------------- // Check host capacity // ----------------------------------------------------------------- @@ -319,6 +394,16 @@ void Scheduler::match() vm->add_host(host->get_hid()); } } + else + { + ostringstream oss; + + oss << "Host " << host->get_hid() << + " filtered out. It does not have enough capacity."; + + NebulaLog::log("SCHED",Log::DEBUG,oss); + + } } } } diff --git a/src/sunstone/etc/sunstone-plugins.yaml b/src/sunstone/etc/sunstone-plugins.yaml index 05b6eb0aed..133556742c 100644 --- a/src/sunstone/etc/sunstone-plugins.yaml +++ b/src/sunstone/etc/sunstone-plugins.yaml @@ -1,16 +1,19 @@ --- - plugins/dashboard-tab.js: - :ALL: true + :ALL: false :user: :group: + oneadmin: true - plugins/hosts-tab.js: - :ALL: true + :ALL: false :user: :group: + oneadmin: true - plugins/groups-tab.js: - :ALL: true + :ALL: false :user: :group: + oneadmin: true - plugins/templates-tab.js: :ALL: true :user: @@ -28,6 +31,7 @@ :user: :group: - plugins/users-tab.js: - :ALL: true + :ALL: false :user: :group: + oneadmin: true diff --git a/src/sunstone/models/OpenNebulaJSON/UserJSON.rb b/src/sunstone/models/OpenNebulaJSON/UserJSON.rb index 0072ecf0c8..89f9780b7a 100644 --- a/src/sunstone/models/OpenNebulaJSON/UserJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON/UserJSON.rb @@ -39,6 +39,8 @@ module OpenNebulaJSON rc = case action_hash['perform'] when "passwd" then self.passwd(action_hash['params']) when "chgrp" then self.chgrp(action_hash['params']) + when "addgroup" then self.addgroup(action_hash['params']) + when "delgroup" then self.delgroup(action_hash['params']) else error_msg = "#{action_hash['perform']} action not " << " available for this resource" @@ -52,7 +54,16 @@ module OpenNebulaJSON end def chgrp(params=Hash.new) - super(params['group_id']) + super(params['group_id'].to_i) end + + def addgroup(params=Hash.new) + super(params['group_id'].to_i) + end + + def delgroup(params=Hash.new) + super(params['group_id'].to_i) + end + end end diff --git a/src/sunstone/models/SunstonePlugins.rb b/src/sunstone/models/SunstonePlugins.rb index e9246437f2..63b08e550b 100644 --- a/src/sunstone/models/SunstonePlugins.rb +++ b/src/sunstone/models/SunstonePlugins.rb @@ -69,19 +69,28 @@ class SunstonePlugins @installed_plugins.include? plugin end - def authorized_plugins(user,group=nil) + def authorized_plugins(user, group) auth_plugins = {"user-plugins"=>Array.new, "plugins"=>Array.new} @plugins_conf.each do |plugin_conf| plugin = plugin_conf.keys.first - perms = plugin_conf[plugin] + perms = plugin_conf[plugin] + if installed?(plugin) p_path, p_name = plugin.split('/') - if perms[:user] and perms[:user][user] - auth_plugins[p_path] << p_name - elsif perms[:group] and perms[:group][group] - auth_plugins[p_path] << p_name + if perms[:user] and perms[:user].has_key? user + if perms[:user][user] + auth_plugins[p_path] << p_name + else + next + end + elsif perms[:group] and perms[:group].has_key? group + if perms[:group][group] + auth_plugins[p_path] << p_name + else + next + end elsif perms[:ALL] auth_plugins[p_path] << p_name end diff --git a/src/sunstone/models/SunstoneServer.rb b/src/sunstone/models/SunstoneServer.rb index d654f50649..0ed284f9a7 100644 --- a/src/sunstone/models/SunstoneServer.rb +++ b/src/sunstone/models/SunstoneServer.rb @@ -41,9 +41,13 @@ class SunstoneServer return [500, false] end - user_pass = user_pool["USER[NAME=\"#{user}\"]/PASSWORD"] + user_pass = user_pool["USER[NAME=\"#{user}\"]/PASSWORD"] + user_id = user_pool["USER[NAME=\"#{user}\"]/ID"] + user_gid = user_pool["USER[NAME=\"#{user}\"]/GID"] + user_gname = user_pool["USER[NAME=\"#{user}\"]/GNAME"] + if user_pass == sha1_pass - return [204, user_pool["USER[NAME=\"#{user}\"]/ID"]] + return [204, [user_id, user_gid, user_gname]] else return [401, nil] end diff --git a/src/sunstone/public/js/opennebula.js b/src/sunstone/public/js/opennebula.js index 09885d3188..6c9d1a3c0b 100644 --- a/src/sunstone/public/js/opennebula.js +++ b/src/sunstone/public/js/opennebula.js @@ -151,7 +151,7 @@ var OpenNebula = { { return Error('Incorrect Pool'); } - + var p_pool = []; if (response[pool_name]) { @@ -227,7 +227,7 @@ var OpenNebula = { var password = params.data.password; var remember = params.remember; - var resource = OpenNebula.Auth.resource; + var resource = OpenNebula.Auth.resource; var request = OpenNebula.Helper.request(resource,"login"); $.ajax({ @@ -709,7 +709,7 @@ var OpenNebula = { var callback_error = params.error; var timeout = params.timeout || false; var resource = OpenNebula.Network.resource; - + var request = OpenNebula.Helper.request(resource,"list"); $.ajax({ @@ -1257,7 +1257,7 @@ var OpenNebula = { var callback_error = params.error; var id = params.data.id; var resource = OpenNebula.VM.resource; - + var method = "suspend"; var action = OpenNebula.Helper.action(method); var request = OpenNebula.Helper.request(resource,method, id); @@ -1331,7 +1331,7 @@ var OpenNebula = { "type" : type } var resource = OpenNebula.VM.resource; - + var action = OpenNebula.Helper.action(method,saveas_params) var request = OpenNebula.Helper.request(resource,method, [id,disk_id, image_name, type]); @@ -1387,7 +1387,7 @@ var OpenNebula = { } }); }, - + "resubmit": function(params) { var callback = params.success; @@ -1419,7 +1419,7 @@ var OpenNebula = { } }); }, - + "startvnc" : function(params){ var callback = params.success; var callback_error = params.error; @@ -1449,7 +1449,7 @@ var OpenNebula = { } }); }, - + "stopvnc" : function(params){ var callback = params.success; var callback_error = params.error; @@ -1687,6 +1687,36 @@ var OpenNebula = { }); }, + "show" : function(params) + { + var callback = params.success; + var callback_error = params.error; + var id = params.data.id; + + var resource = OpenNebula.User.resource; + var request = OpenNebula.Helper.request(resource,"show", id); + + $.ajax({ + url: "user/" + id, + type: "GET", + dataType: "json", + success: function(response) + { + if (callback) + { + callback(request, response); + } + }, + error: function(response) + { + if (callback_error) + { + callback_error(request, OpenNebula.Error(response)); + } + } + }); + }, + "delete": function(params) { var callback = params.success; @@ -1759,7 +1789,7 @@ var OpenNebula = { var action = OpenNebula.Helper.action(method, { "password" : passwd }); - + var resource = OpenNebula.User.resource; var request = OpenNebula.Helper.request(resource,method, passwd); @@ -1794,6 +1824,67 @@ var OpenNebula = { var action = OpenNebula.Helper.action(method, {"group_id": gid}); var request = OpenNebula.Helper.request(OpenNebula.User.resource,method, [id, gid]); + $.ajax({ + url: "user/" + id + "/action", + type: "POST", + data: JSON.stringify(action), + success: function() + { + if (callback) + { + callback(request); + } + }, + error: function(response) + { + if (callback_error) + { + callback_error(request, OpenNebula.Error(response)); + } + } + }); + }, + + "addgroup" : function(params){ + var callback = params.success; + var callback_error = params.error; + var id = params.data.id; + var gid = params.data.extra_param; + + var method = "addgroup"; + var action = OpenNebula.Helper.action(method, {"group_id": gid}); + var request = OpenNebula.Helper.request(OpenNebula.User.resource,method, [id, gid]); + + $.ajax({ + url: "user/" + id + "/action", + type: "POST", + data: JSON.stringify(action), + success: function() + { + if (callback) + { + callback(request); + } + }, + error: function(response) + { + if (callback_error) + { + callback_error(request, OpenNebula.Error(response)); + } + } + }); + }, + "delgroup" : function(params){ + var callback = params.success; + var callback_error = params.error; + var id = params.data.id; + var gid = params.data.extra_param; + + var method = "delgroup"; + var action = OpenNebula.Helper.action(method, {"group_id": gid}); + var request = OpenNebula.Helper.request(OpenNebula.User.resource,method, [id, gid]); + $.ajax({ url: "user/" + id + "/action", type: "POST", @@ -2012,7 +2103,7 @@ var OpenNebula = { var method = "enable"; var action = OpenNebula.Helper.action(method); - + var resource = OpenNebula.Image.resource; var request = OpenNebula.Helper.request(resource,method, id); @@ -2141,7 +2232,7 @@ var OpenNebula = { var method = "persistent"; var action = OpenNebula.Helper.action(method); - + var resource = OpenNebula.Image.resource; var request = OpenNebula.Helper.request(resource,method, id); @@ -2174,7 +2265,7 @@ var OpenNebula = { var method = "nonpersistent"; var action = OpenNebula.Helper.action(method); - + var resource = OpenNebula.Image.resource; var request = OpenNebula.Helper.request(resource,method, id); @@ -2207,10 +2298,10 @@ var OpenNebula = { OpenNebula.Helper.chgrp(params,OpenNebula.Image.resource,"image"); } }, - + "Template" : { "resource" : "VMTEMPLATE", - + "create" : function(params) { var callback = params.success; @@ -2240,7 +2331,7 @@ var OpenNebula = { } } }); - + }, "fetch_template" : function(params) { @@ -2283,7 +2374,7 @@ var OpenNebula = { var method = "update"; var action = OpenNebula.Helper.action(method, template_obj); - var resource = OpenNebula.Template.resource; + var resource = OpenNebula.Template.resource; var request = OpenNebula.Helper.request(resource,method, [id, template_obj]); $.ajax({ @@ -2427,7 +2518,7 @@ var OpenNebula = { callback_error(request, OpenNebula.Error(response)); } } - }); + }); }, "delete" : function(params) { @@ -2455,7 +2546,7 @@ var OpenNebula = { callback_error(request, OpenNebula.Error(response)); } } - }); + }); }, "chown" : function(params){ diff --git a/src/sunstone/public/js/plugins/groups-tab.js b/src/sunstone/public/js/plugins/groups-tab.js index be557a1ab9..e78b0870b2 100644 --- a/src/sunstone/public/js/plugins/groups-tab.js +++ b/src/sunstone/public/js/plugins/groups-tab.js @@ -14,7 +14,7 @@ /* limitations under the License. */ /* -------------------------------------------------------------------------- */ -var group_select=""; +var groups_select=""; var group_list_json = {}; var dataTable_groups; @@ -28,8 +28,8 @@ var groups_tab_content = \ All\ ID\ - Owner\ Name\ + Users\ \ \ \ @@ -157,11 +157,23 @@ Sunstone.addMainTab('groups_tab',groups_tab); function groupElementArray(group_json){ var group = group_json.GROUP; + + var users_str=""; + if (group.USERS.ID && + group.USERS.ID.constructor == Array){ + for (var i=0; i', group.ID, - getUserName(group.UID), - group.NAME ]; + group.NAME, + users_str ]; } function groupInfoListener(){ @@ -176,7 +188,7 @@ function groupInfoListener(){ } function updateGroupSelect(){ - groups_select = makeSelectOptions(dataTable_groups,1,3,-1,"",-1); + groups_select = makeSelectOptions(dataTable_groups,1,2,-1,"",-1); } function updateGroupElement(request, group_json){ diff --git a/src/sunstone/public/js/plugins/hosts-tab.js b/src/sunstone/public/js/plugins/hosts-tab.js index 0ae4eae897..35674a5c71 100644 --- a/src/sunstone/public/js/plugins/hosts-tab.js +++ b/src/sunstone/public/js/plugins/hosts-tab.js @@ -222,7 +222,7 @@ var host_actions = { "Host.update_dialog" : { type: "custom", call: function() { - popUpTemplateUpdateDialog("Host",hosts_select); + popUpTemplateUpdateDialog("Host",hosts_select,getSelectedNodes(dataTable_hosts)); } }, diff --git a/src/sunstone/public/js/plugins/images-tab.js b/src/sunstone/public/js/plugins/images-tab.js index 191fb0c5be..343f345fc5 100644 --- a/src/sunstone/public/js/plugins/images-tab.js +++ b/src/sunstone/public/js/plugins/images-tab.js @@ -223,7 +223,7 @@ var image_actions = { "Image.update_dialog" : { type: "custom", call: function() { - popUpTemplateUpdateDialog("Image",images_select); + popUpTemplateUpdateDialog("Image",images_select,getSelectedNodes(dataTable_images)); } }, @@ -442,8 +442,8 @@ function imageElementArray(image_json){ return [ '', image.ID, - getUserName(image.UID), - getGroupName(image.GID), + image.UNAME, + image.GNAME, image.NAME, OpenNebula.Helper.image_type(image.TYPE), pretty_time(image.REGTIME), @@ -530,6 +530,14 @@ function updateImageInfo(request,img){ Name\ '+img_info.NAME+'\ \ + \ + Owner\ + '+img_info.UNAME+'\ + \ + \ + Group\ + '+img_info.GNAME+'\ + \ \ Type\ '+OpenNebula.Helper.image_type(img_info.TYPE)+'\ diff --git a/src/sunstone/public/js/plugins/templates-tab.js b/src/sunstone/public/js/plugins/templates-tab.js index 3fc9d0d659..51646f88cd 100644 --- a/src/sunstone/public/js/plugins/templates-tab.js +++ b/src/sunstone/public/js/plugins/templates-tab.js @@ -591,7 +591,7 @@ var template_actions = { "Template.update_dialog" : { type: "custom", call: function() { - popUpTemplateUpdateDialog("Template",templates_select); + popUpTemplateUpdateDialog("Template",templates_select,getSelectedNodes(dataTable_templates)); } }, @@ -768,8 +768,8 @@ function templateElementArray(template_json){ return [ '', template.ID, - getUserName(template.UID), - getGroupName(template.GID), + template.UNAME, + template.GNAME, template.NAME, pretty_time(template.REGTIME), parseInt(template.PUBLIC) ? "yes" : "no" @@ -852,6 +852,14 @@ function updateTemplateInfo(request,template){ Name\ '+template_info.NAME+'\ \ + \ + Owner\ + '+template_info.UNAME+'\ + \ + \ + Group\ + '+template_info.GNAME+'\ + \ \ Register time\ '+pretty_time(template_info.REGTIME)+'\ diff --git a/src/sunstone/public/js/plugins/users-tab.js b/src/sunstone/public/js/plugins/users-tab.js index b1e9c9df12..dc8f568dfc 100644 --- a/src/sunstone/public/js/plugins/users-tab.js +++ b/src/sunstone/public/js/plugins/users-tab.js @@ -97,6 +97,46 @@ var user_actions = { notify: false }, + "User.chgrp" : { + type: "multiple", + call: OpenNebula.User.chgrp, + callback : function(req){ + Sunstone.runAction("User.show",req.request.data[0]); + }, + elements : function() {return getSelectedNodes(dataTable_users);}, + error: onError, + notify: true + }, + + "User.addgroup" : { + type: "multiple", + call: OpenNebula.User.addgroup, + callback : function(req){ + Sunstone.runAction("User.show",req.request.data[0]); + }, + elements : function() {return getSelectedNodes(dataTable_users);}, + error: onError, + notify: true + }, + + "User.delgroup" : { + type: "multiple", + call: OpenNebula.User.delgroup, + callback : function(req){ + Sunstone.runAction("User.show",req.request.data[0]); + }, + elements : function() {return getSelectedNodes(dataTable_users);}, + error: onError, + notify: true + }, + + "User.show" : { + type: "single", + call: OpenNebula.User.show, + callback: updateUserElement, + error: onError + }, + "User.delete" : { type: "multiple", call: OpenNebula.User.delete, @@ -119,6 +159,27 @@ var user_buttons = { text: "+ New", condition: True }, + "User.chgrp" : { + type: "confirm_with_select", + text: "Change main group", + select: function(){ return groups_select; }, + tip: "This will change the main group of the selected users. Select the new group:", + condition: True + }, + "User.addgroup" : { + type: "confirm_with_select", + text: "Add to group", + select: function(){ return groups_select; }, + tip: "Select the new group to add users:", + condition: True + }, + "User.delgroup" : { + type: "confirm_with_select", + text: "Delete from group", + select: function(){ return groups_select; }, + tip: "Select the group from which to delete users:", + condition: True + }, "User.delete" : { type: "action", text: "Delete", @@ -146,30 +207,38 @@ function userElementArray(user_json){ name = user.NAME; } - var i = 1; - var groups_str=getGroupName(user.GID)+", "; - var groups_full_str=getGroupName(user.GID)+", "; - var group_field; + var groups_str=""; + if (user.GROUPS.ID.constructor == Array){ //several groups + for (var i=0; i< user.GROUPS.ID.length; i++){ + groups_str+=getGroupName(user.GROUPS.ID[i])+', '; + }; + groups_str = groups_str.slice(0,-2); + } else { //one group + groups_str = getGroupName(user.GROUPS.ID); + }; - if (user.GROUPS.ID){ - $.each(user.GROUPS.ID,function() { - if (i<=5) { - groups_str+=getGroupName(this)+", "; - }; - groups_full_str+=getGroupName(this)+", "; - i++; - }); - if (i>0){ - groups_str = groups_str.slice(0, -2); - groups_full_str = groups_str.slice(0, -2); - }; - if (i>5){ - groups_str+="..."; - group_field = '
'+groups_str+'
'; - } else { - group_field=groups_str; - }; - } + // var groups_full_str=getGroupName(user.GID)+", "; + // var group_field; + + // if (user.GROUPS.ID){ + // $.each(user.GROUPS.ID,function() { + // if (i<=5) { + // groups_str+=getGroupName(this)+", "; + // }; + // groups_full_str+=getGroupName(this)+", "; + // i++; + // }); + // if (i>0){ + // groups_str = groups_str.slice(0, -2); + // groups_full_str = groups_str.slice(0, -2); + // }; + // if (i>5){ + // groups_str+="..."; + // group_field = '
'+groups_str+'
'; + // } else { + // group_field=groups_str; + // }; + // } @@ -177,7 +246,7 @@ function userElementArray(user_json){ '', user.ID, name, - group_field + groups_str ] } diff --git a/src/sunstone/public/js/plugins/vms-tab.js b/src/sunstone/public/js/plugins/vms-tab.js index 090d10e9d2..035423e7ec 100644 --- a/src/sunstone/public/js/plugins/vms-tab.js +++ b/src/sunstone/public/js/plugins/vms-tab.js @@ -580,8 +580,8 @@ function vMachineElementArray(vm_json){ return [ '', vm.ID, - getUserName(vm.UID), - getGroupName(vm.GID), + vm.UNAME, + vm.GNAME, vm.NAME, state, vm.CPU, @@ -659,6 +659,14 @@ function updateVMInfo(request,vm){ Name\ '+vm_info.NAME+'\ \ + \ + Owner\ + '+vm_info.UNAME+'\ + \ + \ + Group\ + '+vm_info.GNAME+'\ + \ \ State\ '+OpenNebula.Helper.resource_state("vm",vm_info.STATE)+'\ diff --git a/src/sunstone/public/js/plugins/vnets-tab.js b/src/sunstone/public/js/plugins/vnets-tab.js index 922a8d105f..8658d833fb 100644 --- a/src/sunstone/public/js/plugins/vnets-tab.js +++ b/src/sunstone/public/js/plugins/vnets-tab.js @@ -318,8 +318,8 @@ function vNetworkElementArray(vn_json){ return [ '', network.ID, - getUserName(network.UID), - getGroupName(network.GID), + network.UNAME, + network.GNAME, network.NAME, parseInt(network.TYPE) ? "FIXED" : "RANGED", network.BRIDGE, @@ -401,8 +401,12 @@ function updateVNetworkInfo(request,vn){ '+vn_info.ID+'\ \ \ - UID\ - '+vn_info.UID+'\ + Owner\ + '+vn_info.UNAME+'\ + \ + \ + Group\ + '+vn_info.GNAME+'\ \ \ Public\ diff --git a/src/sunstone/public/js/sunstone-util.js b/src/sunstone/public/js/sunstone-util.js index 8c43b26883..bfed8e919d 100644 --- a/src/sunstone/public/js/sunstone-util.js +++ b/src/sunstone/public/js/sunstone-util.js @@ -375,11 +375,17 @@ function waitingNodes(dataTable){ }; function getUserName(uid){ - return getName(uid,dataTable_users); + if (typeof(dataTable_users) != "undefined"){ + return getName(uid,dataTable_users); + } + return uid; } function getGroupName(gid){ - return getName(gid,dataTable_groups); + if (typeof(dataTable_groups) != "undefined"){ + return getName(gid,dataTable_groups); + } + return gid; } function getName(id,dataTable){ @@ -598,9 +604,13 @@ function setupTemplateUpdateDialog(){ $('#template_update_dialog #template_update_select').live("change",function(){ var id = $(this).val(); - var resource = $('#template_update_dialog #template_update_button').val(); - $('#template_update_dialog #template_update_textarea').val("Loading..."); - Sunstone.runAction(resource+".fetch_template",id); + if (id.length){ + var resource = $('#template_update_dialog #template_update_button').val(); + $('#template_update_dialog #template_update_textarea').val("Loading..."); + Sunstone.runAction(resource+".fetch_template",id); + } else { + $('#template_update_dialog #template_update_textarea').val(""); + } }); $('#template_update_dialog #template_update_button').click(function(){ @@ -613,10 +623,26 @@ function setupTemplateUpdateDialog(){ }); } -function popUpTemplateUpdateDialog(elem_str,select_items){ +function popUpTemplateUpdateDialog(elem_str,select_items,sel_elems){ $('#template_update_dialog #template_update_button').val(elem_str); $('#template_update_dialog #template_update_select').html(select_items); $('#template_update_dialog #template_update_textarea').val(""); + + if (sel_elems.length >= 1){ //several items in the list are selected + //grep them + var new_select= sel_elems.length > 1? '' : ""; + $('option','').each(function(){ + if ($.inArray($(this).val(),sel_elems) >= 0){ + new_select+=''; + }; + }); + $('#template_update_dialog #template_update_select').html(new_select); + if (sel_elems.length == 1) { + $('#template_update_dialog #template_update_select option').attr("selected","selected"); + $('#template_update_dialog #template_update_select').trigger("change"); + } + }; + $('#template_update_dialog').dialog('open'); return false; } diff --git a/src/sunstone/sunstone-server.rb b/src/sunstone/sunstone-server.rb index 96a15d8a03..5c63fc18ac 100755 --- a/src/sunstone/sunstone-server.rb +++ b/src/sunstone/sunstone-server.rb @@ -15,7 +15,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # -#--------------------------------------------------------------------------- # +#--------------------------------------------------------------------------- # ONE_LOCATION = ENV["ONE_LOCATION"] @@ -76,11 +76,13 @@ helpers do rc = SunstoneServer.authorize(user, sha1_pass) if rc[1] - session[:user] = user - session[:user_id] = rc[1] - session[:password] = sha1_pass - session[:ip] = request.ip - session[:remember] = params[:remember] + session[:user] = user + session[:user_id] = rc[1][0] + session[:user_gid] = rc[1][1] + session[:user_gname] = rc[1][2] + session[:password] = sha1_pass + session[:ip] = request.ip + session[:remember] = params[:remember] if params[:remember] env['rack.session.options'][:expire_after] = 30*60*60*24 @@ -137,7 +139,7 @@ get '/' do :expires=>time) p = SunstonePlugins.new - @plugins = p.authorized_plugins(session[:user]) + @plugins = p.authorized_plugins(session[:user], session[:user_gname]) erb :index end diff --git a/src/test/Nebula.cc b/src/test/Nebula.cc index c909dc21ba..c0794b1ce2 100644 --- a/src/test/Nebula.cc +++ b/src/test/Nebula.cc @@ -132,6 +132,11 @@ void Nebula::start() delete authm; } + if ( aclm != 0) + { + delete aclm; + } + if ( nebula_configuration != 0) { delete nebula_configuration; @@ -178,6 +183,7 @@ void Nebula::start() ImagePool::bootstrap(db); VMTemplatePool::bootstrap(db); GroupPool::bootstrap(db); + AclManager::bootstrap(db); } catch (exception&) { @@ -235,9 +241,6 @@ void Nebula::start() throw; } - // Set pointer to null, to prevent its deletion on the destructor - db = 0; - // ----------------------------------------------------------- //Managers // ----------------------------------------------------------- @@ -368,30 +371,6 @@ void Nebula::start() } } - // ---- Request Manager ---- - if (tester->need_rm) - { - try - { - rm = tester->create_rm(log_location + "one_xmlrpc.log"); - } - catch (bad_alloc&) - { - NebulaLog::log("ONE", Log::ERROR, "Error starting RM"); - throw; - } - - if( rm != 0 ) - { - rc = rm->start(); - } - - if ( rc != 0 ) - { - throw runtime_error("Could not start the Request Manager"); - } - } - // ---- Hook Manager ---- if (tester->need_hm) { @@ -438,6 +417,26 @@ void Nebula::start() } } + // ---- ACL Manager ---- + if (tester->need_aclm) + { + try + { + aclm = new AclManager(db); + } + catch (bad_alloc&) + { + throw; + } + + rc = aclm->start(); + + if ( rc != 0 ) + { + throw runtime_error("Could not start the ACL Manager"); + } + } + // ---- Image Manager ---- if (tester->need_imagem) { @@ -461,6 +460,30 @@ void Nebula::start() } } + // ---- Request Manager ---- + if (tester->need_rm) + { + try + { + rm = tester->create_rm(log_location + "one_xmlrpc.log"); + } + catch (bad_alloc&) + { + NebulaLog::log("ONE", Log::ERROR, "Error starting RM"); + throw; + } + + if( rm != 0 ) + { + rc = rm->start(); + } + + if ( rc != 0 ) + { + throw runtime_error("Could not start the Request Manager"); + } + } + // ----------------------------------------------------------- // Load mads // ----------------------------------------------------------- @@ -496,6 +519,11 @@ void Nebula::start() { authm->load_mads(0); } + + // ----------------------------------------------------------- + // Set DB pointer to null, to prevent its deletion on the destructor + // ----------------------------------------------------------- + db = 0; }; /* -------------------------------------------------------------------------- */ diff --git a/src/test/NebulaTest.cc b/src/test/NebulaTest.cc index f50cda3339..d25c93adbb 100644 --- a/src/test/NebulaTest.cc +++ b/src/test/NebulaTest.cc @@ -139,6 +139,11 @@ AuthManager* NebulaTest::create_authm(time_t timer_period) return 0; } +AclManager* NebulaTest::create_aclm(SqlDB* db) +{ + return new AclManager(db); +} + ImageManager* NebulaTest::create_imagem(ImagePool * ipool) { map mad_value; diff --git a/src/um/User.cc b/src/um/User.cc index 3dd668b573..7250be3fb3 100644 --- a/src/um/User.cc +++ b/src/um/User.cc @@ -128,12 +128,13 @@ string& User::to_xml(string& xml) const oss << "" - "" << oid <<"" << - "" << gid <<"" << - "" << name <<"" << - "" << password <<"" << - "" << enabled_int <<"" << - collection_xml << + "" << oid <<"" << + "" << gid <<"" << + "" << gname <<"" << + "" << name <<"" << + "" << password <<""<< + "" << enabled_int <<"" << + collection_xml << ""; xml = oss.str(); @@ -155,6 +156,7 @@ int User::from_xml(const string& xml) rc += xpath(oid, "/USER/ID", -1); rc += xpath(gid, "/USER/GID", -1); + rc += xpath(gname, "/USER/GNAME", "not_found"); rc += xpath(name, "/USER/NAME", "not_found"); rc += xpath(password, "/USER/PASSWORD", "not_found"); rc += xpath(int_enabled, "/USER/ENABLED", 0); @@ -164,7 +166,7 @@ int User::from_xml(const string& xml) // Get associated classes ObjectXML::get_nodes("/USER/GROUPS", content); - if( content.size() < 1 ) + if (content.empty()) { return -1; } @@ -172,6 +174,7 @@ int User::from_xml(const string& xml) // Set of IDs rc += ObjectCollection::from_xml_node(content[0]); + ObjectXML::free_nodes(content); if (rc != 0) { diff --git a/src/um/UserPool.cc b/src/um/UserPool.cc index ae896b742b..467263ab08 100644 --- a/src/um/UserPool.cc +++ b/src/um/UserPool.cc @@ -88,8 +88,13 @@ UserPool::UserPool(SqlDB * db):PoolSQL(db,User::table) string error_str; string sha1_pass = SSLTools::sha1_digest(one_pass); - allocate(&one_uid,GroupPool::ONEADMIN_ID,one_name,sha1_pass, - true, error_str); + allocate(&one_uid, + GroupPool::ONEADMIN_ID, + one_name, + GroupPool::ONEADMIN_NAME, + sha1_pass, + true, + error_str); } else { @@ -117,8 +122,9 @@ UserPool::UserPool(SqlDB * db):PoolSQL(db,User::table) int UserPool::allocate ( int * oid, int gid, - string username, - string password, + const string& uname, + const string& gname, + const string& password, bool enabled, string& error_str) { @@ -130,12 +136,12 @@ int UserPool::allocate ( ostringstream oss; - if ( username.empty() ) + if ( uname.empty() ) { goto error_name; } - user = get(username,false); + user = get(uname,false); if ( user !=0 ) { @@ -143,7 +149,7 @@ int UserPool::allocate ( } // Build a new User object - user = new User(-1, gid, username, password, enabled); + user = new User(-1, gid, uname, gname, password, enabled); user->add_collection_id(gid); //Adds the primary group to the collection @@ -190,7 +196,12 @@ error_common: /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -bool UserPool::authenticate(const string& session, int& user_id, int& group_id) +bool UserPool::authenticate(const string& session, + int& user_id, + int& group_id, + string& uname, + string& gname, + set& group_ids) { map::iterator index; @@ -198,6 +209,9 @@ bool UserPool::authenticate(const string& session, int& user_id, int& group_id) string username; string secret, u_pass; + string tuname; + string tgname; + int uid, gid; int rc; bool result; @@ -207,6 +221,8 @@ bool UserPool::authenticate(const string& session, int& user_id, int& group_id) user_id = -1; group_id = -1; + uname = ""; + gname = ""; result = false; rc = User::split_secret(session,username,secret); @@ -224,6 +240,11 @@ bool UserPool::authenticate(const string& session, int& user_id, int& group_id) uid = user->oid; gid = user->gid; + tuname = user->name; + tgname = user->gname; + + group_ids = user->get_groups(); + user->unlock(); } else //External User @@ -233,7 +254,7 @@ bool UserPool::authenticate(const string& session, int& user_id, int& group_id) gid = -1; } - AuthRequest ar(uid); + AuthRequest ar(uid, group_ids); ar.add_authenticate(username,u_pass,secret); @@ -241,8 +262,12 @@ bool UserPool::authenticate(const string& session, int& user_id, int& group_id) { if (ar.plain_authenticate()) { - user_id = 0; - group_id = GroupPool::ONEADMIN_ID; + user_id = uid; + group_id = gid; + + uname = tuname; + gname = tgname; + result = true; } } @@ -252,6 +277,10 @@ bool UserPool::authenticate(const string& session, int& user_id, int& group_id) { user_id = uid; group_id = gid; + + uname = tuname; + gname = tgname; + result = true; } } @@ -266,6 +295,10 @@ bool UserPool::authenticate(const string& session, int& user_id, int& group_id) { user_id = uid; group_id = gid; + + uname = tuname; + gname = tgname; + result = true; } else //External user, username & pass in driver message @@ -286,6 +319,7 @@ bool UserPool::authenticate(const string& session, int& user_id, int& group_id) allocate(&user_id, GroupPool::USERS_ID, mad_name, + GroupPool::USERS_NAME, mad_pass, true, error_str); @@ -302,7 +336,12 @@ bool UserPool::authenticate(const string& session, int& user_id, int& group_id) } else { + group_ids.insert( GroupPool::USERS_ID ); group_id = GroupPool::USERS_ID; + + uname = mad_name; + gname = GroupPool::USERS_NAME; + result = true; } } diff --git a/src/um/test/SConstruct b/src/um/test/SConstruct index a766dc55aa..f74e3c2d93 100644 --- a/src/um/test/SConstruct +++ b/src/um/test/SConstruct @@ -44,6 +44,7 @@ env.Prepend(LIBS=[ 'nebula_pool', 'nebula_hm', 'nebula_authm', + 'nebula_acl', 'nebula_common', 'nebula_lcm', 'nebula_dm', diff --git a/src/um/test/UserPoolTest.cc b/src/um/test/UserPoolTest.cc index c90dd971cb..344b9eb2f2 100644 --- a/src/um/test/UserPoolTest.cc +++ b/src/um/test/UserPoolTest.cc @@ -31,10 +31,10 @@ const string usernames[] = { "A user", "B user", "C user", "D user", "E user" }; const string passwords[] = { "A pass", "B pass", "C pass", "D pass", "E pass" }; const string dump_result = - "00one_user_test5baa61e4c9b93f3f0682250b6cf8331b7ee68fd81010ap1020a namepass1030a_namepassword1040another namesecret1050user123410"; + "00oneadminone_user_test5baa61e4c9b93f3f0682250b6cf8331b7ee68fd81010oneadminap1020oneadmina namepass1030oneadmina_namepassword1040oneadminanother namesecret1050oneadminuser123410"; const string dump_where_result = - "10ap1020a namepass1030a_namepassword1040another namesecret10"; + "10oneadminap1020oneadmina namepass1030oneadmina_namepassword1040oneadminanother namesecret10"; #include "NebulaTest.h" @@ -97,7 +97,7 @@ protected: int oid; string err; - return ((UserPool*)pool)->allocate(&oid, 0, usernames[index], + return ((UserPool*)pool)->allocate(&oid, 0, usernames[index],"oneadmin", passwords[index], true, err); }; @@ -187,24 +187,28 @@ public: bool rc; int oid, gid; + set groups; + string uname, gname; // There is an initial user, created with the one_auth file: // one_user_test:password string session="one_user_test:5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8"; - rc = user_pool->authenticate( session, oid, gid ); + rc = user_pool->authenticate( session, oid, gid, uname, gname, groups ); CPPUNIT_ASSERT( rc == true ); CPPUNIT_ASSERT( oid == 0 ); - CPPUNIT_ASSERT( oid == 0 ); + CPPUNIT_ASSERT( gid == 0 ); + CPPUNIT_ASSERT( uname == "one_user_test" ); + CPPUNIT_ASSERT( gname == "oneadmin" ); session = "one_user_test:wrong_password"; - rc = user_pool->authenticate( session, oid, gid ); + rc = user_pool->authenticate( session, oid, gid , uname, gname, groups ); CPPUNIT_ASSERT( rc == false ); CPPUNIT_ASSERT( oid == -1 ); CPPUNIT_ASSERT( gid == -1 ); session = "unknown_user:5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8"; - rc = user_pool->authenticate( session, oid, gid ); + rc = user_pool->authenticate( session, oid, gid, uname, gname, groups ); CPPUNIT_ASSERT( rc == false ); CPPUNIT_ASSERT( oid == -1 ); CPPUNIT_ASSERT( gid == -1 ); @@ -293,17 +297,17 @@ public: UserPool * up = static_cast(pool); // Allocate a user. - rc = up->allocate(&oid, 0,usernames[0], passwords[0], true, err); + rc = up->allocate(&oid, 0,usernames[0], "oneadmin",passwords[0], true, err); CPPUNIT_ASSERT( oid == 1 ); CPPUNIT_ASSERT( oid == rc ); // Try to allocate twice the same user, should fail - rc = up->allocate(&oid, 0,usernames[0], passwords[0], true, err); + rc = up->allocate(&oid, 0,usernames[0], "oneadmin", passwords[0], true, err); CPPUNIT_ASSERT( rc == -1 ); CPPUNIT_ASSERT( oid == rc ); // Try again, with different password - rc = up->allocate(&oid, 0, usernames[0], passwords[1], true, err); + rc = up->allocate(&oid, 0, usernames[0], "oneadmin", passwords[1], true, err); CPPUNIT_ASSERT( rc == -1 ); CPPUNIT_ASSERT( oid == rc ); } @@ -318,7 +322,7 @@ public: for(int i=0; i<5; i++) { - ((UserPool*)pool)->allocate(&oid, 0, d_names[i], d_pass[i], true, err); + ((UserPool*)pool)->allocate(&oid, 0, d_names[i], "oneadmin", d_pass[i], true, err); } ostringstream oss; @@ -345,7 +349,7 @@ public: for(int i=0; i<5; i++) { - ((UserPool*)pool)->allocate(&oid, 0, d_names[i], d_pass[i], true, err); + ((UserPool*)pool)->allocate(&oid, 0, d_names[i], "oneadmin",d_pass[i], true, err); } // Note: second parameter of dump is the WHERE constraint. The "order diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index d6a505fc4c..6d5e0da844 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -37,11 +37,13 @@ /* Virtual Machine :: Constructor/Destructor */ /* ************************************************************************** */ -VirtualMachine::VirtualMachine(int id, - int _uid, - int _gid, +VirtualMachine::VirtualMachine(int id, + int _uid, + int _gid, + const string& _uname, + const string& _gname, VirtualMachineTemplate * _vm_template): - PoolObjectSQL(id,"",_uid,_gid,table), + PoolObjectSQL(id,"",_uid,_gid,_uname,_gname,table), last_poll(0), state(INIT), lcm_state(LCM_INIT), @@ -68,14 +70,9 @@ VirtualMachine::VirtualMachine(int id, VirtualMachine::~VirtualMachine() { - if ( history != 0 ) + for (unsigned int i=0 ; i < history_records.size() ; i++) { - delete history; - } - - if ( previous_history != 0 ) - { - delete previous_history; + delete history_records[i]; } if ( _log != 0 ) @@ -126,18 +123,26 @@ int VirtualMachine::select(SqlDB * db) //Get History Records. Current history is built in from_xml() (if any). if( hasHistory() ) { - last_seq = history->seq; + last_seq = history->seq - 1; - if ( last_seq > 0 ) + for (int i = last_seq; i >= 0; i--) { - previous_history = new History(oid, last_seq - 1); + History * hp; - rc = previous_history->select(db); + hp = new History(oid, i); + rc = hp->select(db); if ( rc != 0) { goto error_previous_history; } + + history_records[i] = hp; + + if ( i == last_seq ) + { + previous_history = hp; + } } } @@ -166,6 +171,7 @@ int VirtualMachine::select(SqlDB * db) error_previous_history: ose << "Can not get previous history record (seq:" << history->seq << ") for VM id: " << oid; + log("ONE", Log::ERROR, ose); return -1; } @@ -558,15 +564,12 @@ void VirtualMachine::add_history( { seq = history->seq + 1; - if (previous_history != 0) - { - delete previous_history; - } - previous_history = history; } history = new History(oid,seq,hid,hostname,vm_dir,vmm_mad,tm_mad); + + history_records.push_back(history); }; /* -------------------------------------------------------------------------- */ @@ -582,21 +585,18 @@ void VirtualMachine::cp_history() } htmp = new History(oid, - history->seq + 1, - history->hid, - history->hostname, - history->vm_dir, - history->vmm_mad_name, - history->tm_mad_name); + history->seq + 1, + history->hid, + history->hostname, + history->vm_dir, + history->vmm_mad_name, + history->tm_mad_name); - if ( previous_history != 0 ) - { - delete previous_history; - } previous_history = history; + history = htmp; - history = htmp; + history_records.push_back(history); } /* -------------------------------------------------------------------------- */ @@ -612,18 +612,17 @@ void VirtualMachine::cp_previous_history() } htmp = new History(oid, - history->seq + 1, - previous_history->hid, - previous_history->hostname, - previous_history->vm_dir, - previous_history->vmm_mad_name, - previous_history->tm_mad_name); - - delete previous_history; + history->seq + 1, + previous_history->hid, + previous_history->hostname, + previous_history->vm_dir, + previous_history->vmm_mad_name, + previous_history->tm_mad_name); previous_history = history; + history = htmp; - history = htmp; + history_records.push_back(history); } /* -------------------------------------------------------------------------- */ @@ -1137,11 +1136,26 @@ error_yy: pthread_mutex_unlock(&lex_mutex); return -1; } - /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ string& VirtualMachine::to_xml(string& xml) const +{ + return to_xml_extended(xml,false); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +string& VirtualMachine::to_xml_extended(string& xml) const +{ + return to_xml_extended(xml,true); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +string& VirtualMachine::to_xml_extended(string& xml, bool extended) const { string template_xml; @@ -1153,6 +1167,8 @@ string& VirtualMachine::to_xml(string& xml) const << "" << oid << "" << "" << uid << "" << "" << gid << "" + << "" << uname << "" + << "" << gname << "" << "" << name << "" << "" << last_poll << "" << "" << state << "" @@ -1168,7 +1184,21 @@ string& VirtualMachine::to_xml(string& xml) const if ( hasHistory() ) { - oss << history->to_xml(history_xml); + oss << ""; + + if ( extended ) + { + for (unsigned int i=0; i < history_records.size(); i++) + { + oss << history_records[i]->to_xml(history_xml); + } + } + else + { + oss << history->to_xml(history_xml); + } + + oss << ""; } oss << ""; @@ -1185,39 +1215,43 @@ int VirtualMachine::from_xml(const string &xml_str) { vector content; - int int_state; - int int_lcmstate; + int istate; + int ilcmstate; int rc = 0; // Initialize the internal XML object update_from_str(xml_str); // Get class base attributes - rc += xpath(oid, "/VM/ID", -1); - rc += xpath(uid, "/VM/UID", -1); - rc += xpath(gid, "/VM/GID", -1); - rc += xpath(name, "/VM/NAME", "not_found"); + rc += xpath(oid, "/VM/ID", -1); - rc += xpath(last_poll, "/VM/LAST_POLL",0); - rc += xpath(int_state, "/VM/STATE", 0); - rc += xpath(int_lcmstate,"/VM/LCM_STATE", 0); + rc += xpath(uid, "/VM/UID", -1); + rc += xpath(gid, "/VM/GID", -1); - rc += xpath(stime, "/VM/STIME", 0); - rc += xpath(etime, "/VM/ETIME", 0); - rc += xpath(deploy_id, "/VM/DEPLOY_ID",""); + rc += xpath(uname, "/VM/UNAME", "not_found"); + rc += xpath(gname, "/VM/GNAME", "not_found"); + rc += xpath(name, "/VM/NAME", "not_found"); - rc += xpath(memory, "/VM/MEMORY", 0); - rc += xpath(cpu, "/VM/CPU", 0); - rc += xpath(net_tx, "/VM/NET_TX", 0); - rc += xpath(net_rx, "/VM/NET_RX", 0); + rc += xpath(last_poll, "/VM/LAST_POLL", 0); + rc += xpath(istate, "/VM/STATE", 0); + rc += xpath(ilcmstate, "/VM/LCM_STATE", 0); - state = static_cast( int_state ); - lcm_state = static_cast( int_lcmstate ); + rc += xpath(stime, "/VM/STIME", 0); + rc += xpath(etime, "/VM/ETIME", 0); + rc += xpath(deploy_id, "/VM/DEPLOY_ID",""); + + rc += xpath(memory, "/VM/MEMORY", 0); + rc += xpath(cpu, "/VM/CPU", 0); + rc += xpath(net_tx, "/VM/NET_TX", 0); + rc += xpath(net_rx, "/VM/NET_RX", 0); + + state = static_cast(istate); + lcm_state = static_cast(ilcmstate); // Get associated classes ObjectXML::get_nodes("/VM/TEMPLATE", content); - if( content.size() < 1 ) + if (content.empty()) { return -1; } @@ -1226,13 +1260,20 @@ int VirtualMachine::from_xml(const string &xml_str) rc += obj_template->from_xml_node(content[0]); // Last history entry + ObjectXML::free_nodes(content); content.clear(); - ObjectXML::get_nodes("/VM/HISTORY", content); - if( !content.empty() ) + ObjectXML::get_nodes("/VM/HISTORY_RECORDS/HISTORY", content); + + if (!content.empty()) { history = new History(oid); rc += history->from_xml_node(content[0]); + + history_records.resize(history->seq + 1); + history_records[history->seq] = history; + + ObjectXML::free_nodes(content); } if (rc != 0) diff --git a/src/vm/VirtualMachinePool.cc b/src/vm/VirtualMachinePool.cc index 9bb3bdd4fb..4cb4aa1a63 100644 --- a/src/vm/VirtualMachinePool.cc +++ b/src/vm/VirtualMachinePool.cc @@ -178,6 +178,8 @@ VirtualMachinePool::VirtualMachinePool(SqlDB * db, int VirtualMachinePool::allocate ( int uid, int gid, + const string& uname, + const string& gname, VirtualMachineTemplate * vm_template, int * oid, string& error_str, @@ -188,7 +190,7 @@ int VirtualMachinePool::allocate ( // ------------------------------------------------------------------------ // Build a new Virtual Machine object // ------------------------------------------------------------------------ - vm = new VirtualMachine(-1, uid, gid, vm_template); + vm = new VirtualMachine(-1, uid, gid, uname, gname, vm_template); if (on_hold == true) { @@ -248,29 +250,3 @@ int VirtualMachinePool::get_pending( /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ - -int VirtualMachinePool::dump( ostringstream& oss, - int state, - const string& where) -{ - ostringstream where_oss; - - if ( state != -1 ) - { - where_oss << VirtualMachine::table << ".state = " << state; - } - else - { - where_oss << VirtualMachine::table << ".state <> 6"; - } - - if ( !where.empty() ) - { - where_oss << " AND " << where; - } - - return PoolSQL::dump(oss, "VM_POOL", VirtualMachine::table,where_oss.str()); -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ diff --git a/src/vm/test/SConstruct b/src/vm/test/SConstruct index bd2813a595..fe0a76e259 100644 --- a/src/vm/test/SConstruct +++ b/src/vm/test/SConstruct @@ -22,6 +22,8 @@ env.Prepend(LIBS=[ 'nebula_hm', 'nebula_vnm', 'nebula_authm', + 'nebula_acl', + 'nebula_group', 'nebula_template', 'nebula_pool', 'nebula_xml', diff --git a/src/vm/test/VirtualMachinePoolTest.cc b/src/vm/test/VirtualMachinePoolTest.cc index aeb53b41b7..5bcd6c1e7d 100644 --- a/src/vm/test/VirtualMachinePoolTest.cc +++ b/src/vm/test/VirtualMachinePoolTest.cc @@ -46,21 +46,21 @@ const string templates[] = const string xmls[] = { - "01231VM one001231the_userusersVM one010000000000000000", - "12611Second VM0" + "12611the_userusersSecond VM0" "1000000000000<" "/ETIME>0000", - "01231VM one001231the_userusersVM one010000000000000000