Index: app/helpers/auth_sources_ids_helper.rb =================================================================== --- app/helpers/auth_sources_ids_helper.rb (revision 0) +++ app/helpers/auth_sources_ids_helper.rb (revision 0) @@ -0,0 +1,19 @@ + +require 'osx/cocoa' + +OSX.require_framework 'Collaboration' + +module AuthSourcesIdsHelper + + # localized to the server, not the web app user + def authorities + @@authorities ||= [ OSX::CBIdentityAuthority.defaultIdentityAuthority.localizedName, + OSX::CBIdentityAuthority.managedIdentityAuthority.localizedName, + OSX::CBIdentityAuthority.localIdentityAuthority.localizedName ] + end + + def ids_authority_select(auth_source) + select("auth_source","host",authorities.collect { |x| [ x, x ] }) + end + +end Index: app/models/auth_source_ids.rb =================================================================== --- app/models/auth_source_ids.rb (revision 0) +++ app/models/auth_source_ids.rb (revision 0) @@ -0,0 +1,158 @@ +# Support for Mac OS X v10.5 Identity Services + +require 'osx/cocoa' +OSX.require_framework 'Collaboration' + +class AuthSourceIds < AuthSource + validates_presence_of :host + validates_length_of :name, :attr_login, :maximum => 60, :allow_nil => true + + def authenticate(login, password) + return nil if login.blank? || password.blank? + attrs = [] + # get authority + authority = authority_for_name!(self.host) + # search for user + identity = OSX::CBIdentity.identityWithName_authority(login, authority) + return nil if identity.nil? || !identity.is_a?(OSX::CBUserIdentity) + # get attributes if creating new user + if onthefly_register? + first, last = fullname_to_first_last(fullname_for_identity(identity)) + attrs = [:firstname => first, + :lastname => last, + :mail => mail_for_identity(identity), + :auth_source_id => self.id] + end + # authenticate user + return nil unless identity.authenticateWithPassword(password) + # filter users + return nil unless self.attr_login.empty? || identity.isMemberOfGroup(group_for_name!(self.attr_login, authority)) + # return attributes + attrs + end + + def auth_method_name + "Identity Services" + end + + def test_connection + authority = authority_for_name!(self.host) + # also check filtered group + group_for_name!(self.attr_login, authority) unless self.attr_login.empty? + end + + private + + def group_for_name!(group_name, authority) + group = group_for_name(group_name, authority) + raise "Identity Services Group \"#{group_name}\" not found" if group.nil? + group + end + + def group_for_name(group_name, authority) + group = OSX::CBIdentity.identityWithName_authority(group_name, authority) + group.is_a?(OSX::CBGroupIdentity) ? group : nil + end + + def authority_for_name!(auth_name) + authority = authority_for_name(auth_name) + raise "Identity Services Authority \"#{auth_name}\" not found" if authority.nil? + authority + end + + def authority_for_name(auth_name) + case auth_name + when OSX::CBIdentityAuthority.defaultIdentityAuthority.localizedName: OSX::CBIdentityAuthority.defaultIdentityAuthority + when OSX::CBIdentityAuthority.managedIdentityAuthority.localizedName: OSX::CBIdentityAuthority.managedIdentityAuthority + when OSX::CBIdentityAuthority.localIdentityAuthority.localizedName: OSX::CBIdentityAuthority.localIdentityAuthority + else nil + end + end + + def fullname_for_identity(identity) + fullname = identity.fullName + fullname && fullname.to_ruby + end + + def mail_for_identity(identity) + email = identity.emailAddress + return nil if email.nil? + preferred_email = email.to_ruby + if self.tls # try for local address if possible + user_aliases = identity.aliases.to_ruby + host_parts = Socket.gethostname.split(".") + while host_parts.size > 1 + suffix = "@" + host_parts.join(".") + user_aliases.each do |a| + if a[-suffix.length, suffix.length] == suffix + preferred_email = a + break + end + end + host_parts.delete_at(0) + end + end + preferred_email + end + + def part_is_suffix(part) + @@suffixes ||= [ "JR.", "SR.", "JR", "SR", "II", "III", "3RD","IV", "V", "VI", "VII" ] + @@suffixes.include?(part.upcase) + end + + def part_is_lastname(part) + @@lastnames ||= [ "DE", "DER", "DI", "LA", "LE", "MAC", "MC", "VAN", "VON", "PONCE" ] + @@lastnames.include?(part.upcase) + end + + def fullname_to_first_last(fullname) + return [nil, nil] if fullname.nil? + + suffix = nil + firstname = "" + lastname = "" + + # split on comma + parts = fullname.split(",").map { |i| i.strip } + + # got at least one comma - check for suffix + if parts.size > 1 && part_is_suffix(parts[-1]) + suffix = ", " + parts.slice!(-1) + end + + if parts.size == 1 + firstparts = [] + lastparts = [] + # need to determine where first and last name splits + parts = parts.first.split(" ") + # take care of suffix (if any) + if parts.size > 1 && part_is_suffix(parts[-1]) + suffix = " " + parts.slice!(-1) + end + # last name + if parts.size > 0 + lastparts.unshift(parts.slice!(-1)) + end + # first name + if parts.size > 0 + firstparts.push(parts.slice!(0)) + end + # all the rest + while parts.size > 0 && part_is_lastname(parts.slice(-1)) + lastparts.unshift(parts.slice!(-1)) + end + # make strings + firstparts.push(parts) if parts.size > 0 + firstname = firstparts.join(" ") + firstname += suffix unless suffix.nil? + lastname = lastparts.join(" ") + elsif parts.size > 1 + # now have last, first + lastname = parts.slice!(0) + firstname = parts.join(", ") + firstname += suffix unless suffix.nil? + end + [firstname, lastname] + end + +end Index: app/controllers/auth_sources_controller.rb =================================================================== --- app/controllers/auth_sources_controller.rb (revision 1907) +++ app/controllers/auth_sources_controller.rb (working copy) @@ -17,6 +17,7 @@ class AuthSourcesController < ApplicationController before_filter :require_admin + helper :auth_sources_ids def index list @@ -33,11 +34,17 @@ end def new - @auth_source = AuthSourceLdap.new + @auth_source = case params[:type] # parameter passed in url + when "AuthSourceIds": AuthSourceIds.new + else AuthSourceLdap.new + end end def create - @auth_source = AuthSourceLdap.new(params[:auth_source]) + @auth_source = case params[:auth_source][:type] + when "AuthSourceIds": AuthSourceIds.new(params[:auth_source]) + else AuthSourceLdap.new(params[:auth_source]) + end if @auth_source.save flash[:notice] = l(:notice_successful_create) redirect_to :action => 'list' Index: app/views/auth_sources/list.rhtml =================================================================== --- app/views/auth_sources/list.rhtml (revision 1907) +++ app/views/auth_sources/list.rhtml (working copy) @@ -1,5 +1,6 @@
-<%= text_field 'auth_source', 'name' %>
- --<%= text_field 'auth_source', 'host' %>
- --<%= text_field 'auth_source', 'port', :size => 6 %> <%= check_box 'auth_source', 'tls' %> LDAPS
- --<%= text_field 'auth_source', 'account' %>
- --<%= password_field 'auth_source', 'account_password', :name => 'ignore', - :value => ((@auth_source.new_record? || @auth_source.account_password.blank?) ? '' : ('x'*15)), - :onfocus => "this.value=''; this.name='auth_source[account_password]';", - :onchange => "this.name='auth_source[account_password]';" %>
- --<%= text_field 'auth_source', 'base_dn', :size => 60 %>
- --<%= check_box 'auth_source', 'onthefly_register' %>
-+<%= text_field 'auth_source', 'name' %>
+ ++<%= ids_authority_select(@auth_source.host) %>
+ ++<%= check_box 'auth_source', 'onthefly_register' %>
+<%= text_field 'auth_source', 'name' %>
Index: app/views/auth_sources/new.rhtml =================================================================== --- app/views/auth_sources/new.rhtml (revision 1907) +++ app/views/auth_sources/new.rhtml (working copy) @@ -1,6 +1,6 @@