class User

User

User represents the human operator. User have the RSA key pair, which is stored in the database crypted by the users password. Password is not recoverable! - in case of lost the only way is to regenerate the users keys and rejoin the user to groups.

All operations on private keys, groups, items requires authorization - the given password. Newly created user is not considered authenticated until save (to be valid)

user = User.new(name: 'User', email: 'email@example.com', password: 'password')
user.authenticated?  #=> false
user.save
user.authenticated?  #=> true

Loaded user is authenticated only if the password is given:

user = User.last
user.authenticated?      #=> false
user.password = 'password'
user.authenticated?      #=> true
user.private_key.class   #=> OpenSSL::PKey::RSA

Users directories are the ones which belongs to the groups to which user belongs as well, directories method will list them all, regardless of level (the flat list).

Constants

VALID_EMAIL_REGEX

Public Instance Methods

<<(other) click to toggle source

Operator similar to add method. Requires authorize before:

user.authorize other_user
other << item
# File app/models/user.rb, line 147
def <<(other)
  o = add(other, authorization_user: @authorization_user)
  other.save!
  self.save!
  o
end
add(other, **options) click to toggle source

Creates an association between other object (Group or Item) and the current user. In case of adding the group to the user, it could be either new group, or existing one - in the second case there is a need to authorize this operation with the user, which already belongs to the group, as the group key must be read.

User.first.add Group.new(name: 'new group')   # adding new group doesn't require authorization

Adding existing group requires authorized user which belongs to this group:

other_user.add my_group, authorization_user: current_user
# File app/models/user.rb, line 121
    def add(other, **options)
            authorizator = options[:authorization_user]
@to_save = other
            case other
            when Group
                    if other.new_record?
                            other.add self
                            other
                    else
                            raise Tarkin::NotAuthorized, "This operation must be autorized by valid user" unless authorizator and authorizator.authenticated?
                            other.add self, authorization_user: authorizator
                            other
                    end
            end
    end
authenticate(passwd) click to toggle source

Returns user if password is OK or nil in other case

# File app/models/user.rb, line 106
def authenticate(passwd)
      self.password = passwd
      if authenticated? then self else nil end
end
authenticated?() click to toggle source

Returns true when user is authenticated (correct password given)

# File app/models/user.rb, line 97
def authenticated?
  begin
    !self.private_key_pem.nil? && !new_record?
  rescue OpenSSL::Cipher::CipherError, Tarkin::WrongPasswordException
    false
  end
end
authorize(authorizor) click to toggle source

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

# File app/models/user.rb, line 138
def authorize(authorizor)
  raise Tarkin::NotAuthorized, "Did you mean 'authenticate'?" unless authorizor.is_a? User
  @authorization_user = authorizor
end
change_password(new_password) click to toggle source

Change User password. Re-crypt the private key using new password. After this, user is still authenticated and can retrieve a private key

user = User.first
user.password = 'password'
user.change_password('new password')
user.private_key.class                #=> OpenSSL::PKey::RSA
# File app/models/user.rb, line 89
def change_password(new_password)
  cipher = OpenSSL::Cipher::AES256.new(:CBC)
  old_private_key = self.private_key
  @password = new_password
  self.private_key_pem_crypted = old_private_key.to_pem cipher, @password if new_password.length >= 4 # because OpenSSL requires at least 4 characters
end
favorite?(thing) click to toggle source

True, if the given Directory or Item is on the User shortlist.

# File app/models/user.rb, line 205
def favorite?(thing)
  case thing
  when Directory
    self.favorite_directories.where(id: thing.id).exists?
  when Item
    self.favorite_items.where(id: thing.id).exists?
  else
    false
  end
end
inspect() click to toggle source

Shorter view

# File app/models/user.rb, line 217
def inspect
  "#<User> '#{self.name}'  [id: #{self.id}, email: #{self.email}]"
end
items() click to toggle source

Returns array of items which belongs to this user, with intersection by Group

# File app/models/user.rb, line 155
def items
      # self.groups.map{|group| group.items}.flatten.uniq
  Item.joins(:groups).where(groups: { id: self.groups.select(:id) }).uniq
end
ls(dir = Directory.root, **options) click to toggle source

Returns the content (directory and items) of the given directory. Default is a root directory (to which everyone has access). Directory must belong to one of the users group. Returns all items and only the directories to which user has access.

# File app/models/user.rb, line 163
def ls(dir = Directory.root, **options)
      ls_dirs(dir, **options) + ls_items(dir, **options)
      end
ls_dirs(dir = Directory.root, **options) click to toggle source

Like ls, but returns only directories

# File app/models/user.rb, line 168
def ls_dirs(dir = Directory.root, **options)
  dirs = dir.directories.where(id: self.directories.map{ |d| d.id })
  if options[:pattern]
    dirs.where('path like ?', pattern_like(options[:pattern]))
  else
    dirs
  end
      end
ls_items(dir = Directory.root, **options) click to toggle source

Like ls, but returns only items

# File app/models/user.rb, line 178
def ls_items(dir = Directory.root, **options)
  items = dir.items.where(id: self.items.map{ |i| i.id })
  if options[:pattern]
    items.where('path like ?', pattern_like(options[:pattern]))
  else
    items
  end
      end
name() click to toggle source

Name is a combination of first name and a last name

# File app/models/user.rb, line 222
def name
  "#{(first_name || '').split(/\s+/).map(&:capitalize).join(' ')} #{(last_name || '').capitalize}".strip
end
name=(n) click to toggle source
# File app/models/user.rb, line 226
def name=(n)
  fullname = n.split(/\s+/)
  self.last_name = fullname.last.capitalize
  self.first_name = fullname.first(fullname.size - 1).map(&:capitalize).join(' ')
end
password() click to toggle source

This is only for validators, password should never be readable

# File app/models/user.rb, line 43
def password
  if @password
    '*' * @password.length  
  else
    '*' * 8 unless new_record?
  end
end
password=(pwd) click to toggle source

User authentication requires password

# File app/models/user.rb, line 52
def password=(pwd)
  @password = pwd
  if new_record?
    generate_keys
  end
end
private_key() click to toggle source
# File app/models/user.rb, line 78
def private_key
  OpenSSL::PKey::RSA.new self.private_key_pem
end
private_key_pem() click to toggle source

Returns user private key. It can be retrieved only when user is authenticated

user = User.first
user.password = 'password'
user.private_key_pem.class    #=> String
user.private_key.class        #=> OpenSSL::PKey::RSA
# File app/models/user.rb, line 70
def private_key_pem
  raise Tarkin::WrongPasswordException, "no password given for #{self.name}" if @password.nil?
  begin
    OpenSSL::PKey::RSA.new(self.private_key_pem_crypted, @password).to_pem
  rescue OpenSSL::PKey::RSAError, TypeError
    raise Tarkin::WrongPasswordException, "can't decrypt #{self.name}'s private key"
  end
end
public_key() click to toggle source

Returns user public key

# File app/models/user.rb, line 60
def public_key
  OpenSSL::PKey::RSA.new self.public_key_pem
end
search_dirs(pattern) click to toggle source

Search all the user directories for the path pattern You may use asterisk (*) in the pattern to replace any characters

# File app/models/user.rb, line 189
def search_dirs(pattern)
  self.directories.where('path like ?', pattern_like(pattern)).distinct
end
search_dirs_names(pattern) click to toggle source

Like search_dirs, but search for the directory name only

# File app/models/user.rb, line 194
def search_dirs_names(pattern)
  self.directories.where('directories.name like ?', pattern_like(pattern)).distinct
end
search_items(pattern) click to toggle source

Search all the user items for the given username pattern You may use asterisk (*) in the pattern to replace any characters

# File app/models/user.rb, line 200
def search_items(pattern)
  self.items.where('username like ?', pattern_like(pattern)).distinct
end