class Group

Group

Group associates User with Item. Must contain unique name and at least one user connected.

group = Group.new name: 'group 1'
group.valid?  #=> false
group.errors.messages
#=> {:private_key_pem_crypted=>["can't be blank"], :group=>["'group 1' must contain at least one user"]}

Key pair of the group generates during initialize. The private key of the group is saved in MetaKey which belongs to the group and existing user.

Public Instance Methods

<<(other) click to toggle source

Operator similar to add method. Requires authorize before. Unless add, it saves.

group.authorize user
group << item
# File app/models/group.rb, line 165
def <<(other)
  raise Tarkin::NotAuthorized, "This operation must be autorized by valid user" unless @authorization_user
  o = add(other, authorization_user: @authorization_user)
  self.save!
  o.save!
  o
end
add(other, **options) click to toggle source

Associate other object (User or Item) to the group. Option authorized_by must be given unless adding the existing

user to new group:

group = Group.new(name: 'group 1')
group.add user   # user must exist and be saved

While adding user to the new group, it is being saved. Returns self, so can be chained. Adding user to the group requires authorization:

group.add User.find_by(email: 'test@gdc.com'), authorization_user: user

Association item with group must be done with autorization, as it requires group private key to be read.

group.add Item.new(password: 'secret'), authorization_user: user
# File app/models/group.rb, line 87
def add(other, **options)
  authenticator = options[:authorization_user] || @authorization_user
  cipher = OpenSSL::Cipher::AES256.new(:CBC)
  case other
  when User
    if new_record?
      # creating new group key
      if other.authenticated?
        # generate key and iv to be used to encrypt the group private key
        new_group_key, new_group_iv = cipher.random_key, cipher.random_iv
        # cipher the group private key PEM with a new key and iv
        self.private_key_pem_crypted = encrypt(self.private_key_pem, new_group_key, new_group_iv)
        self.users << other                               # add the user to the group
        meta = self.meta_keys.find {|x| x.user == other}  # can't use find_by or where, as it is not saved yet
                                                          # it should be a new record, so contains just one meta_key
        meta.key_crypted, meta.iv_crypted = other.public_key.public_encrypt(new_group_key), 
                                            other.public_key.public_encrypt(new_group_iv)
        # self.save!
      else
        raise Tarkin::GroupNotAccessibleException, "Group #{self.name} can't be accessed by #{other.name}"
      end
    else
      raise Tarkin::NotAuthorized, "This operation must be autorized by valid user" unless authenticator
      meta = authenticator.meta_keys.find_by(group: self)
      if meta 
        # decipher the group key and iv using authorizing user private key
        group_key, group_iv = authenticator.private_key.private_decrypt(meta.key_crypted),
                              authenticator.private_key.private_decrypt(meta.iv_crypted)
        # save it with other user public key 
        self.meta_keys.new user: other, key_crypted: other.public_key.public_encrypt(group_key),
                                            iv_crypted: other.public_key.public_encrypt(group_iv)
        # self.users(true)     # reload the users after creating MetaKey manually
        # other.groups(true)   # reload the groups for user after adding it
        @must_reload = true
        @to_reload = other
      else
        raise Tarkin::GroupNotAccessibleException, "Group #{self.name} does not belongs to #{authenticator.name}"
      end
      other
    end
  when Item
    raise Tarkin::NotAuthorized, "This operation must be autorized by valid user" unless authenticator.authenticated?
    # generate key and iv to be used to encrypt the item password
    if other.new_record? && self.items.empty?
      new_item_key, new_item_iv = cipher.random_key, cipher.random_iv
      key_crypted, iv_crypted = self.public_key.public_encrypt(new_item_key), self.public_key.public_encrypt(new_item_iv)
      other.password_crypted = encrypt(other.password, new_item_key, new_item_iv)
      other.groups << self
      meta = other.meta_keys.find {|x| x.group == self}
      raise "Couldn't find the corresponding meta" unless meta
      meta.key_crypted, meta.iv_crypted = key_crypted, iv_crypted
      # other.save!
    else
      authenticator_meta, authenticator_group = meta_and_group_for_user_and_item authenticator, other
      authenticator_group_private_key = authenticator_group.private_key(authorization_user: authenticator)
      item_key, item_iv = authenticator_group_private_key.private_decrypt(authenticator_meta.key_crypted),
                          authenticator_group_private_key.private_decrypt(authenticator_meta.iv_crypted)
      key_crypted, iv_crypted = self.public_key.public_encrypt(item_key), self.public_key.public_encrypt(item_iv)
      self.meta_keys.new item: other, key_crypted: key_crypted, iv_crypted: iv_crypted
      # other.meta_keys.new group: self, key_crypted: key_crypted, iv_crypted: iv_crypted
      @must_reload = true
      @to_reload = other
      # self.items(true)
      # other.groups(true)
    end
    other
  end
end
authorize(authorizor) click to toggle source

Set up user to perform next action with. See << operator

# File app/models/group.rb, line 157
def authorize(authorizor)
  @authorization_user = authorizor
end
inspect() click to toggle source

Shorter view - to prevent rails console to show all the encrypted data

# File app/models/group.rb, line 174
def inspect
  "#<Group> '#{self.name}'  [id: #{self.id}]"
end
private_key(**options) click to toggle source

The private key of the group can be accessed directly when it is a new group:

group = Group.new(name: 'group1')
group.private_key   #=> #<OpenSSL::PKey::RSA:0x007faa65b33938>

Otherwise requires authorization user to be set in options. This user must be valid and must belong to the group:

group = Group.first
group.private_key(authorization_user: user)
#=> #<OpenSSL::PKey::RSA:0x007faa650db918>
# File app/models/group.rb, line 44
def private_key(**options)
  OpenSSL::PKey::RSA.new self.private_key_pem(**options)
end
private_key_pem(**options) click to toggle source

The private key PEM. Like private_key, but returns the PEM string

# File app/models/group.rb, line 49
def private_key_pem(**options)
  authenticator = options[:authorization_user]
  raise Tarkin::PrivateKeyNotAccessibleException, 
    "Private key can't be accessed at this moment" if !new_record? && authenticator.nil? 
  if new_record?
    @private_key_pem
  else
    #cipher = OpenSSL::Cipher::AES256.new(:CBC)
    meta = authenticator.meta_keys.find_by(group: self)
    if meta
      # cipher.decrypt
      # cipher.key = authenticator.private_key.private_decrypt meta.key_crypted
      # cipher.iv = authenticator.private_key.private_decrypt meta.iv_crypted
      # cipher.update(self.private_key_pem_crypted) + cipher.final
      decrypt self.private_key_pem_crypted, authenticator.private_key.private_decrypt(meta.key_crypted),
                                             authenticator.private_key.private_decrypt(meta.iv_crypted)
    else
      raise Tarkin::GroupNotAccessibleException, "Group #{self.name} does not belongs to #{authenticator.name}"
    end
  end
end
public_key() click to toggle source

Public key and its PEM is always visible and accessible to any

# File app/models/group.rb, line 29
def public_key
  OpenSSL::PKey::RSA.new self.public_key_pem
end
user_names() click to toggle source

List user names of the group, ordered by the last name

# File app/models/group.rb, line 179
def user_names
  self.users.order(:last_name).collect(&:name)
end