From 4be47e357a4105b65a164b9a1b9fd5e9b0b9d6c8 Mon Sep 17 00:00:00 2001 From: Javi Fontan Date: Fri, 22 Aug 2014 12:55:05 +0200 Subject: [PATCH 1/3] feature #3159: map AD groups to ONE groups in ldap driver Generates mapping file and returns a list of IDs of ONE mapped groups that the user should belong. --- src/authm_mad/remotes/ldap/authenticate | 14 ++- src/authm_mad/remotes/ldap/ldap_auth.conf | 15 +++ src/authm_mad/remotes/ldap/ldap_auth.rb | 108 +++++++++++++++++++--- 3 files changed, 125 insertions(+), 12 deletions(-) diff --git a/src/authm_mad/remotes/ldap/authenticate b/src/authm_mad/remotes/ldap/authenticate index 8a01485974..95ec7cf4d9 100755 --- a/src/authm_mad/remotes/ldap/authenticate +++ b/src/authm_mad/remotes/ldap/authenticate @@ -83,9 +83,21 @@ begin end if ldap.authenticate(user_name, secret) + groups = ldap.get_groups + if groups.empty? + if !server_conf[:mapping_default] + STDERR.puts "User does not belong to a mapped group" + authenticated = false + break + else + groups = [server_conf[:mapping_default]] + end + end + + group_list = groups.join(' ') escaped_user=URI_PARSER.escape(user) escaped_secret=URI_PARSER.escape(user_name) - puts "ldap #{escaped_user} #{escaped_secret}" + puts "ldap #{escaped_user} #{escaped_secret} #{group_list}" authenticated=true break else diff --git a/src/authm_mad/remotes/ldap/ldap_auth.conf b/src/authm_mad/remotes/ldap/ldap_auth.conf index f4241781f5..066eb7280b 100644 --- a/src/authm_mad/remotes/ldap/ldap_auth.conf +++ b/src/authm_mad/remotes/ldap/ldap_auth.conf @@ -49,6 +49,21 @@ server 1: # user field that that is in in the group group_field, if not set 'dn' will be used #:user_group_field: 'dn' + # Generate mapping file from group template info + :mapping_generate: true + + # Seconds a mapping file remain untouched until the next regeneration + :mapping_timeout: 300 + + # Name of the mapping file in OpenNebula var diretory + :mapping_filename: server1.yaml + + # Key from the OpenNebula template to map to an AD group + :mapping_key: GROUP_DN + + # Default group ID used for users in an AD group not mapped + :mapping_default: 1 + # this example server wont be called as it is not in the :order list server 2: :auth_method: :simple diff --git a/src/authm_mad/remotes/ldap/ldap_auth.rb b/src/authm_mad/remotes/ldap/ldap_auth.rb index 5c6345a59e..161ef225ea 100644 --- a/src/authm_mad/remotes/ldap/ldap_auth.rb +++ b/src/authm_mad/remotes/ldap/ldap_auth.rb @@ -15,22 +15,39 @@ # ---------------------------------------------------------------------------- # require 'rubygems' +require 'opennebula' require 'net/ldap' +require 'yaml' + +if !defined?(ONE_LOCATION) + ONE_LOCATION=ENV["ONE_LOCATION"] +end + +if !ONE_LOCATION + VAR_LOCATION="/var/lib/one/" +else + VAR_LOCATION=ONE_LOCATION+"/var/" +end module OpenNebula; end class OpenNebula::LdapAuth def initialize(options) @options={ - :host => 'localhost', - :port => 389, - :user => nil, - :password => nil, - :base => nil, - :auth_method => :simple, - :user_field => 'cn', - :user_group_field => 'dn', - :group_field => 'member' + :host => 'localhost', + :port => 389, + :user => nil, + :password => nil, + :base => nil, + :auth_method => :simple, + :user_field => 'cn', + :user_group_field => 'dn', + :group_field => 'member', + :mapping_generate => true, + :mapping_timeout => 300, + :mapping_filename => 'server1.yaml', + :mapping_key => 'GROUP_DN', + :mapping_default => 1 }.merge(options) ops={} @@ -47,9 +64,61 @@ class OpenNebula::LdapAuth ops[:port]=@options[:port].to_i if @options[:port] ops[:encryption]=@options[:encryption] if @options[:encryption] + @options[:mapping_file_path] = VAR_LOCATION + @options[:mapping_filename] + generate_mapping if @options[:mapping_generate] + load_mapping + @ldap=Net::LDAP.new(ops) end + def generate_mapping + file=@options[:mapping_file_path] + generate = false + + if File.exists?(file) + stat = File.stat(file) + age = Time.now.to_i - stat.mtime.to_i + generate = true if age > @options[:mapping_timeout] + else + generate = true + end + + return if !generate + + client = OpenNebula::Client.new + group_pool = OpenNebula::GroupPool.new(client) + group_pool.info + + groups = group_pool.to_hash[''] + groups=[group_pool.get_hash['GROUP_POOL']['GROUP']].flatten + + yaml={} + + groups.each do |group| + if group['TEMPLATE'] && group['TEMPLATE'][@options[:mapping_key]] + yaml[group['TEMPLATE'][@options[:mapping_key]]] = group['ID'] + end + end + + File.open(file, 'w') do |f| + f.write(yaml.to_yaml) + end + end + + def load_mapping + file=@options[:mapping_file_path] + + @mapping = {} + + if File.exists?(file) + @mapping = YAML.load(File.read(file)) + end + + if @mapping.class != Hash + @mapping = {} + end + end + def find_user(name) begin result=@ldap.search( @@ -57,12 +126,14 @@ class OpenNebula::LdapAuth :filter => "#{@options[:user_field]}=#{name}") if result && result.first - [result.first.dn, result.first[@options[:user_group_field]]] + @user = result.first + [@user.dn, @user[@options[:user_group_field]]] else result=@ldap.search(:base => name) if result && result.first - [name, result.first[@options[:user_group_field]]] + @user = result.first + [name, @user[@options[:user_group_field]]] else [nil, nil] end @@ -99,5 +170,20 @@ class OpenNebula::LdapAuth false end end + + def get_groups + groups = [] + + [@user['memberOf']].flatten.each do |group| + if @mapping[group] + groups << @mapping[group] + else + groups << @options[:mapping_default] + end + end + + groups.delete(false) + groups.compact + end end From 9c0f2bd86aba6c46afce46acbf418953298c6601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn?= Date: Mon, 1 Sep 2014 17:26:13 +0200 Subject: [PATCH 2/3] Feature #3159: Core gets group IDs from external auth drivers --- src/um/UserPool.cc | 120 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 6 deletions(-) diff --git a/src/um/UserPool.cc b/src/um/UserPool.cc index 8375f7d143..c034443c2e 100644 --- a/src/um/UserPool.cc +++ b/src/um/UserPool.cc @@ -686,10 +686,16 @@ bool UserPool::authenticate_external(const string& username, string mad_name; string mad_pass; string error_str; + string tmp_str; Nebula& nd = Nebula::instance(); AuthManager * authm = nd.get_authm(); + GroupPool * gpool = nd.get_gpool(); + User* user; + Group* group; + + set::iterator it; set empty_set; AuthRequest ar(-1,empty_set); @@ -726,15 +732,64 @@ bool UserPool::authenticate_external(const string& username, if ( !is.fail() ) { - getline(is, mad_pass); + is >> mad_pass >> ws; + } + + if ( is.good() ) + { + is >> group_id >> ws; + + if ( is.fail() ) + { + error_str = "One or more group IDs do not exist"; + goto auth_failure_user; + } + + group_ids.insert( group_id ); + + group = gpool->get(group_id, true); + + if( group == 0 ) + { + error_str = "One or more group IDs do not exist"; + goto auth_failure_user; + } + + gname = group->get_name(); + + group->unlock(); + + int tmp_gid; + + while ( is.good() ) + { + is >> tmp_gid >> ws; + + if ( is.fail() ) + { + error_str = "One or more group IDs are malformed"; + goto auth_failure_user; + } + else + { + group_ids.insert( tmp_gid ); + } + } + } + else + { + group_id = GroupPool::USERS_ID; + gname = GroupPool::USERS_NAME; + + group_ids.insert( group_id ); } if ( !is.fail() ) { allocate(&user_id, - GroupPool::USERS_ID, + group_id, mad_name, - GroupPool::USERS_NAME, + gname, mad_pass, driver_name, true, @@ -746,16 +801,69 @@ bool UserPool::authenticate_external(const string& username, goto auth_failure_user; } - group_id = GroupPool::USERS_ID; - group_ids.insert( GroupPool::USERS_ID ); + // Add the User to the secondary groups + user = get(user_id,true); + + if ( user == 0 ) + { + error_str = "User object could not be retrieved"; + goto auth_failure_user; + } + + for(it = group_ids.begin(); it != group_ids.end(); it++) + { + group = gpool->get(*it, true); + + if( group == 0 ) + { + drop(user, tmp_str); + user->unlock(); + + error_str = "One or more group IDs do not exist"; + + goto auth_failure_group; + } + + group->add_user(user_id); + + gpool->update(group); + + group->unlock(); + + user->add_group(*it); + } + + update(user); + + user->unlock(); uname = mad_name; - gname = GroupPool::USERS_NAME; umask = User::get_default_umask(); return true; + +auth_failure_group: + oss << "Can't create user: " << error_str << ". Driver response: " + << ar.message; + NebulaLog::log("AuM",Log::ERROR,oss); + + for(it = group_ids.begin(); it != group_ids.end(); it++) + { + group = gpool->get(*it, true); + + if( group != 0 ) + { + group->del_user(user_id); + + gpool->update(group); + group->unlock(); + } + } + + goto auth_failure; + auth_failure_user: oss << "Can't create user: " << error_str << ". Driver response: " << ar.message; From b752dd6dfb0041cf0f00adeab4190efeb6e0ef92 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Wed, 3 Sep 2014 11:57:20 +0200 Subject: [PATCH 3/3] feature #3159: Added comments and replace rollback code --- src/um/UserPool.cc | 103 ++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 57 deletions(-) diff --git a/src/um/UserPool.cc b/src/um/UserPool.cc index c034443c2e..dbfc725b6e 100644 --- a/src/um/UserPool.cc +++ b/src/um/UserPool.cc @@ -695,6 +695,8 @@ bool UserPool::authenticate_external(const string& username, User* user; Group* group; + int gid = -1; + set::iterator it; set empty_set; @@ -720,6 +722,11 @@ bool UserPool::authenticate_external(const string& username, user_id = -1; + //-------------------------------------------------------------------------- + // Parse driver response format is: + // [gid...] + //-------------------------------------------------------------------------- + if ( is.good() ) { is >> driver_name >> ws; @@ -735,53 +742,58 @@ bool UserPool::authenticate_external(const string& username, is >> mad_pass >> ws; } - if ( is.good() ) + while ( is.good() ) { - is >> group_id >> ws; + int tmp_gid; + + is >> tmp_gid >> ws; if ( is.fail() ) + { + error_str = "One or more group IDs are malformed"; + goto auth_failure_user; + } + + if ( gpool->get(tmp_gid, false) == 0 ) { error_str = "One or more group IDs do not exist"; goto auth_failure_user; } + if ( gid == -1 ) //Keep the first id for primary group + { + gid = tmp_gid; + } + + group_ids.insert(tmp_gid); + } + + //-------------------------------------------------------------------------- + // Create the user, and set primary group + //-------------------------------------------------------------------------- + + if ( gid == -1 ) + { + group_id = GroupPool::USERS_ID; + gname = GroupPool::USERS_NAME; + group_ids.insert( group_id ); + } + else + { + group_id = gid; group = gpool->get(group_id, true); if( group == 0 ) { - error_str = "One or more group IDs do not exist"; + error_str = "Primary Group no longer exist"; goto auth_failure_user; } gname = group->get_name(); group->unlock(); - - int tmp_gid; - - while ( is.good() ) - { - is >> tmp_gid >> ws; - - if ( is.fail() ) - { - error_str = "One or more group IDs are malformed"; - goto auth_failure_user; - } - else - { - group_ids.insert( tmp_gid ); - } - } - } - else - { - group_id = GroupPool::USERS_ID; - gname = GroupPool::USERS_NAME; - - group_ids.insert( group_id ); } if ( !is.fail() ) @@ -801,7 +813,10 @@ bool UserPool::authenticate_external(const string& username, goto auth_failure_user; } - // Add the User to the secondary groups + //-------------------------------------------------------------------------- + // Add the user to the secondary groups + //-------------------------------------------------------------------------- + user = get(user_id,true); if ( user == 0 ) @@ -810,18 +825,13 @@ bool UserPool::authenticate_external(const string& username, goto auth_failure_user; } - for(it = group_ids.begin(); it != group_ids.end(); it++) + for(it = ++group_ids.begin(); it != group_ids.end(); it++) { group = gpool->get(*it, true); - if( group == 0 ) + if( group == 0 ) //Secondary group no longer exists { - drop(user, tmp_str); - user->unlock(); - - error_str = "One or more group IDs do not exist"; - - goto auth_failure_group; + continue; } group->add_user(user_id); @@ -843,27 +853,6 @@ bool UserPool::authenticate_external(const string& username, return true; - -auth_failure_group: - oss << "Can't create user: " << error_str << ". Driver response: " - << ar.message; - NebulaLog::log("AuM",Log::ERROR,oss); - - for(it = group_ids.begin(); it != group_ids.end(); it++) - { - group = gpool->get(*it, true); - - if( group != 0 ) - { - group->del_user(user_id); - - gpool->update(group); - group->unlock(); - } - } - - goto auth_failure; - auth_failure_user: oss << "Can't create user: " << error_str << ". Driver response: " << ar.message;