Arcath/Adauth

View on GitHub
lib/adauth/ad_object.rb

Summary

Maintainability
A
3 hrs
Test Coverage
require "active_support/core_ext/object/try"

module Adauth
    # Container for Objects which inherit from Adauth::AdObject
    module AdObjects
    end

    # Add a field to the specified model
    def self.add_field(object, adauth_method, ldap_method)
        Adauth.logger.info(object.inspect) { "Adding field \"#{ldap_method}\"" }
        object::Fields[adauth_method] = ldap_method
    end

    # Active Directory Interface Object
    #
    # Objects inherit from this class.
    #
    # Provides all the common functions for Active Directory.
    class AdObject
        include Expects

        def self.method_missing(method, *args)
          return super unless method =~ /^find_by_/
          method_field = method.to_s.split("_").last
          field = self::Fields[method_field.to_sym]
          return super unless field
          self.where(field, args.first)
        end

        def method_missing(method, *args)
          field = self.class::Fields[method]
          return handle_field(field) if field
          return super
        end

        def self.reverse_field(search)
          hash = {}
          self::Fields.each do |k, v|
            hash[v] = k
          end
          return hash[search]
        end

        # Returns all objects which have the ObjectClass of the inherited class
        def self.all
            Adauth.logger.info(self.class.inspect) { "Searching for all objects matching filter \"#{self::ObjectFilter}\"" }
            Adauth::SearchResults.new(self.filter(self::ObjectFilter))
        end

        # Returns all the objects which match the supplied query
        #
        # Uses ObjectFilter to restrict to the current object
        def self.where(field, value)
            search_filter = Net::LDAP::Filter.eq(field, value)
            Adauth.logger.info(self.class.inspect) { "Searching for all \"#{self::ObjectFilter}\" where #{field} = #{value}" }
            Adauth::SearchResults.new(filter(add_object_filter(search_filter)))
        end

        # Returns all LDAP objects that match the given filter
        #
        # Use with add_object_filter to make sure that you only get objects that match the object you are querying though
        def self.filter(filter)
          results = []

          result = Adauth.connection.search(:filter => filter)

          raise 'Search returned NIL' if result == nil

          result.each do |entry|
            results << self.new(entry)
          end

          results
        end

        # Adds the object filter to the passed filter
        def self.add_object_filter(filter)
          filter & self::ObjectFilter
        end

        # Creates a new instance of the object and sets @ldap_object to the passed Net::LDAP entity
        def initialize(ldap_object)
            expects ldap_object, Net::LDAP::Entry
            @ldap_object = ldap_object
        end

        # Allows direct access to @ldap_object
        def ldap_object
            @ldap_object
        end

        # Handle the output for the given field
        def handle_field(field)
          case field
            when Symbol then return return_symbol_value(field)
            when Array then return  @ldap_object.try(field.first).try(:collect, &field.last)
          end
        end

        # Returns all the groups the object is a member of
        def groups
            unless @groups
                @groups = convert_to_objects(cn_groups)
            end
            @groups
        end

        # The same as cn_groups, but with the parent groups included
        def cn_groups_nested
          @cn_groups_nested = cn_groups
          cn_groups.each do |group|
            ado = Adauth::AdObjects::Group.where('name', group).first
            if ado
              groups = convert_to_objects ado.cn_groups
              groups.each do |g|
                @cn_groups_nested.push g if !(@cn_groups_nested.include?(g))
              end
            end
          end
          return @cn_groups_nested
        end

        # Returns all the ous the object is in
        def ous
            unless @ous
                @ous = []
                @ldap_object.dn.split(/,/).each do |entry|
                    @ous.push Adauth::AdObjects::OU.where('name', entry.gsub(/OU=/, '')).first if entry =~ /OU=/
                end
            end
            @ous
        end

        # CSV Version of the ous list (can't be pulled over from AD)
        def dn_ous
            unless @dn_ous
                @dn_ous = []
                @ldap_object.dn.split(/,/).each do |entry|
                    @dn_ous.push entry.gsub(/OU=/, '').gsub(/CN=/,'') if entry =~ /OU=/ or entry == "CN=Users"
                end
            end
            @dn_ous
        end

        # Runs a modify action on the current object, takes an aray of operations
        def modify(operations)
          Adauth.logger.info(self.class.inspect) { "Attempting modify operation" }
          unless Adauth.connection.modify :dn => @ldap_object.dn, :operations => operations
            Adauth.logger.fatal(self.class.inspect) { "Modify Operation Failed! Code: #{Adauth.connection.get_operation_result.code} Message: #{Adauth.connection.get_operation_result.message}" }
            raise 'Modify Operation Failed (see log for details)'
          end
        end

        # Returns an array of member objects for this object
        def members
            unless @members
                @members = []
                [Adauth::AdObjects::Computer, Adauth::AdObjects::OU, Adauth::AdObjects::User, Adauth::AdObjects::Group].each do |object|
                    object.all.each do |entity|
                        @members.push entity if entity.is_a_member?(self)
                    end
                end
            end
            @members
        end

        # Checks to see if the object is a member of a given parent (though DN)
        def is_a_member?(parent)
          my_split_dn = @ldap_object.dn.split(",")
          parent_split_dn = parent.ldap_object.dn.split(",")
          if (my_split_dn.count - 1) == parent_split_dn.count
            return true if my_split_dn[1] == parent_split_dn[0]
          end
          return false
        end

        # Delete the object
        def delete
          Adauth.connection.delete(dn: @ldap_object.dn)
        end

        private

        def convert_to_objects(array)
            out = []
            array.each do |entity|
                out.push convert_to_object(entity)
            end
            out
        end

        def convert_to_object(entity)
            user = Adauth::AdObjects::User.where('sAMAccountName', entity).first
            group = Adauth::AdObjects::Group.where('sAMAccountName', entity).first
            (user || group)
        end

        def return_symbol_value(field)
          value = @ldap_object.try(field)

          case value
            when Net::BER::BerIdentifiedArray then return value.first
            else return value
          end
        end
    end
end