diff --git a/include/Group.h b/include/Group.h index fcc1a33abc..68b0704a54 100644 --- a/include/Group.h +++ b/include/Group.h @@ -75,6 +75,26 @@ public: return del_collection_id(id); } + /** + * Adds a User to the admin set. ACL Rules are updated only for this user. + * + * @param user_id ID of the user + * @param error_msg Returns the error reason, if any + * + * @return 0 on success + */ + int add_admin(int user_id, string& error_msg); + + /** + * Deletes a User from the admin set. ACL Rules are updated only for this user. + * + * @param user_id ID of the user + * @param error_msg Returns the error reason, if any + * + * @return 0 on success + */ + int del_admin(int user_id, string& error_msg); + /** * Object quotas, provides set and check interface */ @@ -113,7 +133,8 @@ private: Group(int id, const string& name): PoolObjectSQL(id,GROUP,name,-1,-1,"","",table), ObjectCollection("USERS"), - quota() + quota(), + admins("ADMINS") { // Allow users in this group to see it group_u = 1; @@ -126,6 +147,18 @@ private: delete obj_template; }; + // ************************************************************************* + // Administrators + // ************************************************************************* + + /** + * Stores a collection with the admin users + */ + ObjectCollection admins; + + void add_admin_rules(int user_id); + void del_admin_rules(int user_id); + // ************************************************************************* // DataBase implementation (Private) // ************************************************************************* diff --git a/include/ObjectCollection.h b/include/ObjectCollection.h index 9bcf8bfa70..4174db18a2 100644 --- a/include/ObjectCollection.h +++ b/include/ObjectCollection.h @@ -85,6 +85,16 @@ public: return set (collection_set); }; + /** + * Returns true if the collection contains the given id + * @param id ID to search + * @return true if the collection contains the given id + */ + bool collection_contains(int id) + { + return collection_set.count(id) > 0; + } + private: /** diff --git a/include/RequestManagerGroup.h b/include/RequestManagerGroup.h index 2d313a87fd..b8b40a13ab 100644 --- a/include/RequestManagerGroup.h +++ b/include/RequestManagerGroup.h @@ -66,6 +66,66 @@ public: RequestAttributes& att); }; +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class GroupEditAdmin : public Request +{ +public: + void request_execute(xmlrpc_c::paramList const& _paramList, + RequestAttributes& att); + +protected: + GroupEditAdmin( const string& method_name, + const string& help, + const string& params) + :Request(method_name,params,help) + { + Nebula& nd = Nebula::instance(); + pool = nd.get_gpool(); + upool = nd.get_upool(); + + auth_object = PoolObjectSQL::GROUP; + auth_op = AuthRequest::ADMIN; + }; + + UserPool* upool; + + virtual int edit_admin(Group* group, int user_id, string& error_msg) = 0; +}; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class GroupAddAdmin : public GroupEditAdmin +{ +public: + GroupAddAdmin(): + GroupEditAdmin( "GroupAddAdmin", + "Adds a user to the group admin set", + "A:sii"){}; + + ~GroupAddAdmin(){}; + + int edit_admin(Group* group, int user_id, string& error_msg); +}; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class GroupDelAdmin : public GroupEditAdmin +{ +public: + GroupDelAdmin(): + GroupEditAdmin( "GroupDelAdmin", + "Removes a user from the group admin set", + "A:sii"){}; + + ~GroupDelAdmin(){}; + + int edit_admin(Group* group, int user_id, string& error_msg); +}; + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/src/group/Group.cc b/src/group/Group.cc index f7b2fa9263..b6aa7c25b3 100644 --- a/src/group/Group.cc +++ b/src/group/Group.cc @@ -214,6 +214,7 @@ string& Group::to_xml_extended(string& xml, bool extended) const { ostringstream oss; string collection_xml; + string admins_xml; string template_xml; ObjectCollection::to_xml(collection_xml); @@ -223,7 +224,8 @@ string& Group::to_xml_extended(string& xml, bool extended) const "" << oid << "" << "" << name << "" << obj_template->to_xml(template_xml) << - collection_xml; + collection_xml << + admins.to_xml(admins_xml); if (extended) { @@ -276,6 +278,19 @@ int Group::from_xml(const string& xml) ObjectXML::free_nodes(content); content.clear(); + // Set of Admin IDs + ObjectXML::get_nodes("/GROUP/ADMINS", content); + + if (content.empty()) + { + return -1; + } + + rc += admins.from_xml_node(content[0]); + + ObjectXML::free_nodes(content); + content.clear(); + // Get associated metadata for the group ObjectXML::get_nodes("/GROUP/TEMPLATE", content); @@ -296,3 +311,181 @@ int Group::from_xml(const string& xml) return 0; } + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + +int Group::add_admin(int user_id, string& error_msg) +{ + int rc; + ostringstream oss; + + if ( collection_contains(user_id) == false ) + { + oss << "User " << user_id << " is not part of Group " + << oid << "."; + + error_msg = oss.str(); + + return -1; + } + + rc = admins.add_collection_id(user_id); + + if (rc == -1) + { + oss << "User " << user_id << " is already an administrator of Group " + << oid << "."; + + error_msg = oss.str(); + + return -1; + } + + add_admin_rules(user_id); + + return 0; +} + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + +void Group::add_admin_rules(int user_id) +{ + int rc; + string error_msg; + + AclManager* aclm = Nebula::instance().get_aclm(); + + // # USER/@ USE+MANAGE+ADMIN+CREATE * + rc = aclm->add_rule( + AclRule::INDIVIDUAL_ID | + user_id, + + PoolObjectSQL::USER | + AclRule::GROUP_ID | + oid, + + AuthRequest::USE | + AuthRequest::MANAGE | + AuthRequest::ADMIN | + AuthRequest::CREATE, + + AclRule::ALL_ID, + + error_msg); + + if (rc < 0) + { + NebulaLog::log("GROUP",Log::ERROR,error_msg); + } + + // # VM+NET+IMAGE+TEMPLATE+DOCUMENT+SECGROUP/@ USE+MANAGE * + rc = aclm->add_rule( + AclRule::INDIVIDUAL_ID | + user_id, + + PoolObjectSQL::VM | + PoolObjectSQL::NET | + PoolObjectSQL::IMAGE | + PoolObjectSQL::TEMPLATE | + PoolObjectSQL::DOCUMENT | + PoolObjectSQL::SECGROUP | + AclRule::GROUP_ID | + oid, + + AuthRequest::USE | + AuthRequest::MANAGE, + + AclRule::ALL_ID, + + error_msg); + + if (rc < 0) + { + NebulaLog::log("GROUP",Log::ERROR,error_msg); + } +} + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + +int Group::del_admin(int user_id, string& error_msg) +{ + int rc = admins.del_collection_id(user_id); + + if (rc == -1) + { + ostringstream oss; + oss << "User " << user_id << " is not an administrator of Group " + << oid << "."; + + error_msg = oss.str(); + + return -1; + } + + del_admin_rules(user_id); + + return 0; +} + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + +void Group::del_admin_rules(int user_id) +{ + int rc; + string error_msg; + + AclManager* aclm = Nebula::instance().get_aclm(); + + // # USER/@ USE+MANAGE+ADMIN+CREATE * + rc = aclm->del_rule( + AclRule::INDIVIDUAL_ID | + user_id, + + PoolObjectSQL::USER | + AclRule::GROUP_ID | + oid, + + AuthRequest::USE | + AuthRequest::MANAGE | + AuthRequest::ADMIN | + AuthRequest::CREATE, + + AclRule::ALL_ID, + + error_msg); + + if (rc < 0) + { + NebulaLog::log("GROUP",Log::ERROR,error_msg); + } + + // # VM+NET+IMAGE+TEMPLATE+DOCUMENT+SECGROUP/@ USE+MANAGE * + rc = aclm->del_rule( + AclRule::INDIVIDUAL_ID | + user_id, + + PoolObjectSQL::VM | + PoolObjectSQL::NET | + PoolObjectSQL::IMAGE | + PoolObjectSQL::TEMPLATE | + PoolObjectSQL::DOCUMENT | + PoolObjectSQL::SECGROUP | + AclRule::GROUP_ID | + oid, + + AuthRequest::USE | + AuthRequest::MANAGE, + + AclRule::ALL_ID, + + error_msg); + + if (rc < 0) + { + NebulaLog::log("GROUP",Log::ERROR,error_msg); + } +} diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index 43464c212b..8965b5819f 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -484,23 +484,31 @@ void RequestManager::register_xml_methods() xmlrpc_c::method * group_allocate_pt; xmlrpc_c::method * group_update_pt; xmlrpc_c::method * group_delete_pt; + xmlrpc_c::method * group_add_admin_pt; + xmlrpc_c::method * group_del_admin_pt; if (nebula.is_federation_slave()) { group_allocate_pt = new RequestManagerProxy("one.group.allocate"); group_delete_pt = new RequestManagerProxy("one.group.delete"); group_update_pt = new RequestManagerProxy("one.group.update"); + group_add_admin_pt = new RequestManagerProxy("one.group.addadmin"); + group_del_admin_pt = new RequestManagerProxy("one.group.deladmin"); } else { group_allocate_pt = new GroupAllocate(); group_delete_pt = new GroupDelete(); group_update_pt = new GroupUpdateTemplate(); + group_add_admin_pt = new GroupAddAdmin(); + group_del_admin_pt = new GroupDelAdmin(); } xmlrpc_c::methodPtr group_allocate(group_allocate_pt); xmlrpc_c::methodPtr group_delete(group_delete_pt); xmlrpc_c::methodPtr group_update(group_update_pt); + xmlrpc_c::methodPtr group_add_admin(group_add_admin_pt); + xmlrpc_c::methodPtr group_del_admin(group_del_admin_pt); xmlrpc_c::methodPtr group_info(new GroupInfo()); xmlrpc_c::methodPtr group_set_quota(new GroupSetQuota()); @@ -513,6 +521,8 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.group.info", group_info); RequestManagerRegistry.addMethod("one.group.quota", group_set_quota); RequestManagerRegistry.addMethod("one.group.update", group_update); + RequestManagerRegistry.addMethod("one.group.addadmin", group_add_admin); + RequestManagerRegistry.addMethod("one.group.deladmin", group_del_admin); RequestManagerRegistry.addMethod("one.grouppool.info", grouppool_info); diff --git a/src/rm/RequestManagerGroup.cc b/src/rm/RequestManagerGroup.cc index d90541457b..5f7fb3d5be 100644 --- a/src/rm/RequestManagerGroup.cc +++ b/src/rm/RequestManagerGroup.cc @@ -81,3 +81,113 @@ void GroupSetQuota:: /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ + +void GroupEditAdmin::request_execute( + xmlrpc_c::paramList const& paramList, + RequestAttributes& att) +{ + int group_id = xmlrpc_c::value_int(paramList.getInt(1)); + int user_id = xmlrpc_c::value_int(paramList.getInt(2)); + + PoolObjectAuth group_perms; + PoolObjectAuth user_perms; + + string group_name; + string user_name; + string error_str; + + Group* group; + + int rc; + + // ------------------------------------------------------------------------- + // Authorize the action + // ------------------------------------------------------------------------- + + rc = get_info(pool, group_id, PoolObjectSQL::GROUP, + att, group_perms, group_name, true); + + if ( rc == -1 ) + { + return; + } + + rc = get_info(upool, user_id, PoolObjectSQL::USER, att, user_perms, + user_name, false); + + if ( rc == -1 ) + { + failure_response(NO_EXISTS, get_error(object_name(PoolObjectSQL::USER), + user_id), att); + + return; + } + + if ( att.uid != 0 ) + { + AuthRequest ar(att.uid, att.group_ids); + + ar.add_auth(AuthRequest::ADMIN, group_perms); // MANAGE GROUP + + ar.add_auth(AuthRequest::ADMIN, user_perms); // MANAGE USER + + if (UserPool::authorize(ar) == -1) + { + failure_response(AUTHORIZATION, + authorization_error(ar.message, att), + att); + + return; + } + } + + group = static_cast(pool)->get(group_id, true); + + if ( group == 0 ) + { + failure_response(NO_EXISTS, + get_error(object_name(auth_object),group_id), + att); + + return; + } + + rc = edit_admin(group, user_id, error_str); + + if (rc == 0) + { + pool->update(group); + } + + group->unlock(); + + if (rc != 0) + { + failure_response(INTERNAL, + request_error("Cannot edit group", error_str), + att); + + return; + } + + success_response(group_id, att); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int GroupAddAdmin::edit_admin(Group* group, int user_id, string& error_msg) +{ + return group->add_admin(user_id, error_msg); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int GroupDelAdmin::edit_admin(Group* group, int user_id, string& error_msg) +{ + return group->del_admin(user_id, error_msg); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */