class R509::Subject

The primary subject class. Used for building subject DNs in a sane fashion. @example

subject = R509::Subject.new
subject.CN= "test.test"
subject.organization= "r509 LLC"

@example

subject = R509::Subject.new([['CN','test.test'],['O','r509 LLC']])

@example

subject = R509::Subject.new(:CN => 'test.test', :O => 'r509 LLC')

@example

# you can also use the friendly getter/setters with custom OIDs
R509::OIDMapper.register("1.2.3.4.5.6.7.8","COI","customOID")
subject = R509::Subject.new
subject.COI="test"
# or
subject.customOID="test"
# or
subject.custom_oid="test"

Public Class Methods

new(arg = nil) click to toggle source

@param [Array, OpenSSL::X509::Name, R509::Subject, DER, Hash, nil] arg

# File lib/r509/subject.rb, line 24
    def initialize(arg = nil)
      if arg.is_a?(Array)
        @array = arg
      elsif arg.is_a?(Hash)
        @array = arg.map { |k, v| [k.to_s.upcase, v] }
      elsif arg.is_a?(OpenSSL::X509::Name)
        sanitizer = R509::NameSanitizer.new
        @array = sanitizer.sanitize(arg)
      elsif arg.is_a?(R509::Subject)
        @array = arg.to_a
      else
        @array = []
        unless (begin OpenSSL::ASN1.decode(arg) rescue nil end).nil?
          parse_asn1(arg)
        end
      end

      # see if X509 thinks this is okay
      name
    end

    # @return [OpenSSL::X509::Name]
    def name
      OpenSSL::X509::Name.new(@array)
    end

    # @return [Boolean]
    def empty?
      @array.empty?
    end

    # get value for key
    def [](key)
      @array.each do |item|
        if key == item[0]
          return item[1]
        end
      end
      nil
    end

    # set key and value
    def []=(key, value)
      added = false
      @array = @array.map do |item|
        if key == item[0]
          added = true
          [key, value]
        else
          item
        end
      end

      unless added
        @array << [key, value]
      end

      # see if X509 thinks this is okay
      name

      @array
    end

    # @param [String] key item you want deleted
    def delete(key)
      @array = @array.select do |item|
        item[0] != key
      end
    end

    # @return [String] string of form /CN=something.com/O=whatever/L=Locality
    def to_s
      name.to_s
    end

    # @return [Array] Array of form [['CN','langui.sh']]
    def to_a
      @array
    end

    # @return [Hash]
    def to_h
      hash = {}
      @array.each do |el|
        hash[el[0].to_sym] = el[1]
      end
      hash
    end

    # @return [YAML]
    def to_yaml
      self.to_h.to_yaml
    end

    # @private
    def respond_to?(method_sym, include_private = false)
      method_sym.to_s =~ /([^=]*)/
      oid = oid_check(Regexp.last_match[1])
      if oid
        true
      else
        super(method_sym, include_private)
      end
    end

    private

    # Try to build methods for getting/setting various subject attributes
    # dynamically. this will also cache methods that get built via instance_eval.
    # This code will also allow you to set subject items for custom oids
    # defined via R509::OIDMapper
    #
    # @example
    #  subject = R509::Subject.new
    #  subject.CN = 'test' # method built via method missing.
    #
    def method_missing(method_sym, *args, &block)
      if method_sym.to_s =~ /(.*)=$/
        sn = oid_check(Regexp.last_match[1])
        if sn
          define_dynamic_setter(method_sym, sn)
          send(method_sym, args.first)
        else
          return super(method_sym, *args, &block)
        end
      else
        sn = oid_check(method_sym)
        if sn
          define_dynamic_getter(method_sym, sn)
          send(method_sym)
        else
          return super(method_sym, *args, &block)
        end
      end
    end

    def define_dynamic_setter(name, sn)
      instance_eval <<-RUBY
        def #{name}(value)
          self["#{sn}"]= value
        end
      RUBY
    end

    def define_dynamic_getter(name, sn)
      instance_eval <<-RUBY
        def #{name}
          self["#{sn}"]
        end
      RUBY
    end

    def oid_check(name)
      oid = OpenSSL::ASN1::ObjectId.new(camelize(name))
      oid.short_name
    end

    def camelize(sym)
      sym.to_s.split('_').reduce([]) { |a, e| a.push(a.empty? ? e : e.capitalize) }.join
    end

    def parse_asn1(asn)
      asn = OpenSSL::ASN1.decode asn
      # parsing a subject DN
      # We have to iterate a sequence, which holds sets. Each set has one value: a sequence, which has 2 values
      # So it's effectively an array of arrays which each have only one element, which is an array of 2 values.
      asn.value.each do |set|
        sn = set.value.first.value.first.value
        val = set.value.first.value.last.value
        self[sn] = val
      end
    end
  end

Public Instance Methods

[](key) click to toggle source

get value for key

# File lib/r509/subject.rb, line 56
def [](key)
  @array.each do |item|
    if key == item[0]
      return item[1]
    end
  end
  nil
end
[]=(key, value) click to toggle source

set key and value

# File lib/r509/subject.rb, line 66
def []=(key, value)
  added = false
  @array = @array.map do |item|
    if key == item[0]
      added = true
      [key, value]
    else
      item
    end
  end

  unless added
    @array << [key, value]
  end

  # see if X509 thinks this is okay
  name

  @array
end
camelize(sym) click to toggle source
# File lib/r509/subject.rb, line 181
def camelize(sym)
  sym.to_s.split('_').reduce([]) { |a, e| a.push(a.empty? ? e : e.capitalize) }.join
end
define_dynamic_getter(name, sn) click to toggle source
# File lib/r509/subject.rb, line 168
    def define_dynamic_getter(name, sn)
      instance_eval <<-RUBY
        def #{name}
          self["#{sn}"]
        end
      RUBY
    end
define_dynamic_setter(name, sn) click to toggle source
# File lib/r509/subject.rb, line 160
    def define_dynamic_setter(name, sn)
      instance_eval <<-RUBY
        def #{name}(value)
          self["#{sn}"]= value
        end
      RUBY
    end
delete(key) click to toggle source

@param [String] key item you want deleted

# File lib/r509/subject.rb, line 88
def delete(key)
  @array = @array.select do |item|
    item[0] != key
  end
end
empty?() click to toggle source

@return [Boolean]

# File lib/r509/subject.rb, line 51
def empty?
  @array.empty?
end
method_missing(method_sym, *args, &block) click to toggle source

Try to build methods for getting/setting various subject attributes dynamically. this will also cache methods that get built via instance_eval. This code will also allow you to set subject items for custom oids defined via R509::OIDMapper

@example

subject = R509::Subject.new
subject.CN = 'test' # method built via method missing.
Calls superclass method
# File lib/r509/subject.rb, line 140
def method_missing(method_sym, *args, &block)
  if method_sym.to_s =~ /(.*)=$/
    sn = oid_check(Regexp.last_match[1])
    if sn
      define_dynamic_setter(method_sym, sn)
      send(method_sym, args.first)
    else
      return super(method_sym, *args, &block)
    end
  else
    sn = oid_check(method_sym)
    if sn
      define_dynamic_getter(method_sym, sn)
      send(method_sym)
    else
      return super(method_sym, *args, &block)
    end
  end
end
name() click to toggle source

@return [OpenSSL::X509::Name]

# File lib/r509/subject.rb, line 46
def name
  OpenSSL::X509::Name.new(@array)
end
oid_check(name) click to toggle source
# File lib/r509/subject.rb, line 176
def oid_check(name)
  oid = OpenSSL::ASN1::ObjectId.new(camelize(name))
  oid.short_name
end
parse_asn1(asn) click to toggle source
# File lib/r509/subject.rb, line 185
def parse_asn1(asn)
  asn = OpenSSL::ASN1.decode asn
  # parsing a subject DN
  # We have to iterate a sequence, which holds sets. Each set has one value: a sequence, which has 2 values
  # So it's effectively an array of arrays which each have only one element, which is an array of 2 values.
  asn.value.each do |set|
    sn = set.value.first.value.first.value
    val = set.value.first.value.last.value
    self[sn] = val
  end
end
respond_to?(method_sym, include_private = false) click to toggle source

@private

Calls superclass method
# File lib/r509/subject.rb, line 119
def respond_to?(method_sym, include_private = false)
  method_sym.to_s =~ /([^=]*)/
  oid = oid_check(Regexp.last_match[1])
  if oid
    true
  else
    super(method_sym, include_private)
  end
end
to_a() click to toggle source

@return [Array] Array of form [['CN','langui.sh']]

# File lib/r509/subject.rb, line 100
def to_a
  @array
end
to_h() click to toggle source

@return [Hash]

# File lib/r509/subject.rb, line 105
def to_h
  hash = {}
  @array.each do |el|
    hash[el[0].to_sym] = el[1]
  end
  hash
end
to_s() click to toggle source

@return [String] string of form /CN=something.com/O=whatever/L=Locality

# File lib/r509/subject.rb, line 95
def to_s
  name.to_s
end
to_yaml() click to toggle source

@return [YAML]

# File lib/r509/subject.rb, line 114
def to_yaml
  self.to_h.to_yaml
end