diff options
author | Azul <azul@leap.se> | 2014-04-08 11:49:14 +0200 |
---|---|---|
committer | Azul <azul@leap.se> | 2014-04-08 11:49:14 +0200 |
commit | b6d14dc19dd350a807826e3e097738a36613e083 (patch) | |
tree | 093dc5f2f1e773e3ad009d28d1fd24667d3c0ba6 /users/app/models | |
parent | 2e11e3ca2c7b02fdb5ff54f0bcd766cc5fa39975 (diff) |
moving users: app and test files
Diffstat (limited to 'users/app/models')
-rw-r--r-- | users/app/models/.gitkeep | 0 | ||||
-rw-r--r-- | users/app/models/account.rb | 68 | ||||
-rw-r--r-- | users/app/models/email.rb | 26 | ||||
-rw-r--r-- | users/app/models/identity.rb | 136 | ||||
-rw-r--r-- | users/app/models/local_email.rb | 68 | ||||
-rw-r--r-- | users/app/models/login_format_validation.rb | 21 | ||||
-rw-r--r-- | users/app/models/message.rb | 29 | ||||
-rw-r--r-- | users/app/models/pgp_key.rb | 48 | ||||
-rw-r--r-- | users/app/models/service_level.rb | 19 | ||||
-rw-r--r-- | users/app/models/session.rb | 32 | ||||
-rw-r--r-- | users/app/models/token.rb | 69 | ||||
-rw-r--r-- | users/app/models/unauthenticated_user.rb | 6 | ||||
-rw-r--r-- | users/app/models/user.rb | 179 |
13 files changed, 0 insertions, 701 deletions
diff --git a/users/app/models/.gitkeep b/users/app/models/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/users/app/models/.gitkeep +++ /dev/null diff --git a/users/app/models/account.rb b/users/app/models/account.rb deleted file mode 100644 index cf998e4..0000000 --- a/users/app/models/account.rb +++ /dev/null @@ -1,68 +0,0 @@ -# -# The Account model takes care of the livecycle of a user. -# It composes a User record and it's identity records. -# It also allows for other engines to hook into the livecycle by -# monkeypatching the create, update and destroy methods. -# There's an ActiveSupport load_hook at the end of this file to -# make this more easy. -# -class Account - - attr_reader :user - - def initialize(user = nil) - @user = user - end - - # Returns the user record so it can be used in views. - def self.create(attrs) - @user = User.create(attrs).tap do |user| - Identity.create_for user - end - end - - def update(attrs) - if attrs[:password_verifier].present? - update_login(attrs[:login]) - @user.update_attributes attrs.slice(:password_verifier, :password_salt) - end - # TODO: move into identity controller - key = update_pgp_key(attrs[:public_key]) - @user.errors.set :public_key, key.errors.full_messages - @user.save && save_identities - @user.refresh_identity - end - - def destroy - return unless @user - Identity.disable_all_for(@user) - @user.destroy - end - - protected - - def update_login(login) - return unless login.present? - @old_identity = Identity.for(@user) - @user.login = login - @new_identity = Identity.for(@user) # based on the new login - @old_identity.destination = @user.email_address # alias old -> new - end - - def update_pgp_key(key) - PgpKey.new(key).tap do |key| - if key.present? && key.valid? - @new_identity ||= Identity.for(@user) - @new_identity.set_key(:pgp, key) - end - end - end - - def save_identities - @new_identity.try(:save) && @old_identity.try(:save) - end - - # You can hook into the account lifecycle from different engines using - # ActiveSupport.on_load(:account) do ... - ActiveSupport.run_load_hooks(:account, self) -end diff --git a/users/app/models/email.rb b/users/app/models/email.rb deleted file mode 100644 index a9a503f..0000000 --- a/users/app/models/email.rb +++ /dev/null @@ -1,26 +0,0 @@ -class Email < String - include ActiveModel::Validations - - validates :email, - :format => { - :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/, #local part of email is case-sensitive, so allow uppercase letter. - :message => "needs to be a valid email address" - } - - def to_partial_path - "emails/email" - end - - def to_param - to_s - end - - def email - self - end - - def handle - self.split('@').first - end - -end diff --git a/users/app/models/identity.rb b/users/app/models/identity.rb deleted file mode 100644 index 9b97b51..0000000 --- a/users/app/models/identity.rb +++ /dev/null @@ -1,136 +0,0 @@ -class Identity < CouchRest::Model::Base - include LoginFormatValidation - - use_database :identities - - belongs_to :user - - property :address, LocalEmail - property :destination, Email - property :keys, HashWithIndifferentAccess - - validate :unique_forward - validate :alias_available - validate :address_local_email - validate :destination_email - - design do - view :by_user_id - view :by_address_and_destination - view :by_address - view :pgp_key_by_email, - map: <<-EOJS - function(doc) { - if (doc.type != 'Identity') { - return; - } - if (typeof doc.keys === "object") { - emit(doc.address, doc.keys["pgp"]); - } - } - EOJS - view :disabled, - map: <<-EOJS - function(doc) { - if (doc.type != 'Identity') { - return; - } - if (typeof doc.user_id === "undefined") { - emit(doc._id, 1); - } - } - EOJS - - end - - def self.for(user, attributes = {}) - find_for(user, attributes) || build_for(user, attributes) - end - - def self.find_for(user, attributes = {}) - attributes.reverse_merge! attributes_from_user(user) - find_by_address_and_destination [attributes[:address], attributes[:destination]] - end - - def self.build_for(user, attributes = {}) - attributes.reverse_merge! attributes_from_user(user) - Identity.new(attributes) - end - - def self.create_for(user, attributes = {}) - identity = build_for(user, attributes) - identity.save - identity - end - - def self.disable_all_for(user) - Identity.by_user_id.key(user.id).each do |identity| - identity.disable - identity.save - end - end - - def self.destroy_all_disabled - Identity.disabled.each do |identity| - identity.destroy - end - end - - def self.attributes_from_user(user) - { user_id: user.id, - address: user.email_address, - destination: user.email_address - } - end - - def enabled? - self.destination && self.user_id - end - - def disable - self.destination = nil - self.user_id = nil - end - - def keys - read_attribute('keys') || HashWithIndifferentAccess.new - end - - def set_key(type, key) - return if keys[type] == key.to_s - write_attribute('keys', keys.merge(type => key.to_s)) - end - - # for LoginFormatValidation - def login - self.address.handle - end - - protected - - def unique_forward - same = Identity.find_by_address_and_destination([address, destination]) - if same && same != self - errors.add :base, "This alias already exists" - end - end - - def alias_available - same = Identity.find_by_address(address) - if same && same.user != self.user - errors.add :base, "This email has already been taken" - end - end - - def address_local_email - return if address.valid? #this ensures it is LocalEmail - self.errors.add(:address, address.errors.messages[:email].first) #assumes only one error - end - - def destination_email - return if destination.nil? # this identity is disabled - return if destination.valid? # this ensures it is Email - self.errors.add(:destination, destination.errors.messages[:email].first) #assumes only one error #TODO - end - -end diff --git a/users/app/models/local_email.rb b/users/app/models/local_email.rb deleted file mode 100644 index 2b4c65e..0000000 --- a/users/app/models/local_email.rb +++ /dev/null @@ -1,68 +0,0 @@ -class LocalEmail < Email - - BLACKLIST_FROM_RFC2142 = [ - 'postmaster', 'hostmaster', 'domainadmin', 'webmaster', 'www', - 'abuse', 'noc', 'security', 'usenet', 'news', 'uucp', - 'ftp', 'sales', 'marketing', 'support', 'info' - ] - - def self.domain - APP_CONFIG[:domain] - end - - validates :email, - :format => { - :with => /@#{domain}\Z/i, - :message => "needs to end in @#{domain}" - } - - validate :handle_allowed - - def initialize(s) - super - append_domain_if_needed - end - - def to_key - [handle] - end - - def domain - LocalEmail.domain - end - - protected - - def append_domain_if_needed - unless self.index('@') - self << '@' + domain - end - end - - def handle_allowed - errors.add(:handle, "is reserved.") if handle_reserved? - end - - def handle_reserved? - # *ARRAY in a case statement tests if ARRAY includes the handle. - case handle - when *APP_CONFIG[:handle_blacklist] - true - when *APP_CONFIG[:handle_whitelist] - false - when *BLACKLIST_FROM_RFC2142 - true - else - handle_in_passwd? - end - end - - def handle_in_passwd? - begin - !!Etc.getpwnam(handle) - rescue ArgumentError - # handle was not found - return false - end - end -end diff --git a/users/app/models/login_format_validation.rb b/users/app/models/login_format_validation.rb deleted file mode 100644 index c1fcf70..0000000 --- a/users/app/models/login_format_validation.rb +++ /dev/null @@ -1,21 +0,0 @@ -module LoginFormatValidation - extend ActiveSupport::Concern - - #TODO: Probably will replace this. Playing with using it for aliases too, but won't want it connected to login field. - - included do - # Have multiple regular expression validations so we can get specific error messages: - validates :login, - :format => { :with => /\A.{2,}\z/, - :message => "Must have at least two characters"} - validates :login, - :format => { :with => /\A[a-z\d_\.-]+\z/, - :message => "Only lowercase letters, digits, . - and _ allowed."} - validates :login, - :format => { :with => /\A[a-z].*\z/, - :message => "Must begin with a lowercase letter"} - validates :login, - :format => { :with => /\A.*[a-z\d]\z/, - :message => "Must end with a letter or digit"} - end -end diff --git a/users/app/models/message.rb b/users/app/models/message.rb deleted file mode 100644 index 424f094..0000000 --- a/users/app/models/message.rb +++ /dev/null @@ -1,29 +0,0 @@ -class Message < CouchRest::Model::Base - - use_database :messages - - property :text, String - property :user_ids_to_show, [String] - property :user_ids_have_shown, [String] # is this necessary to store? - - timestamps! - - design do - own_path = Pathname.new(File.dirname(__FILE__)) - load_views(own_path.join('..', 'designs', 'message')) - end - - def mark_as_read_by(user) - user_ids_to_show.delete(user.id) - # is it necessary to keep track of what users have already seen it? - user_ids_have_shown << user.id unless read_by?(user) - end - - def read_by?(user) - user_ids_have_shown.include?(user.id) - end - - def unread_by?(user) - user_ids_to_show.include?(user.id) - end -end diff --git a/users/app/models/pgp_key.rb b/users/app/models/pgp_key.rb deleted file mode 100644 index 66f8660..0000000 --- a/users/app/models/pgp_key.rb +++ /dev/null @@ -1,48 +0,0 @@ -class PgpKey - include ActiveModel::Validations - - KEYBLOCK_IDENTIFIERS = [ - '-----BEGIN PGP PUBLIC KEY BLOCK-----', - '-----END PGP PUBLIC KEY BLOCK-----', - ] - - # mostly for testing. - attr_accessor :keyblock - - validate :validate_keyblock_format - - def initialize(keyblock = nil) - @keyblock = keyblock - end - - def to_s - @keyblock - end - - def present? - @keyblock.present? - end - - # allow comparison with plain keyblock strings. - def ==(other) - self.equal?(other) or - # relax the comparison on line ends. - self.to_s.tr_s("\n\r", '') == other.tr_s("\r\n", '') - end - - protected - - def validate_keyblock_format - if keyblock_identifier_missing? - errors.add :public_key_block, - "does not look like an armored pgp public key block" - end - end - - def keyblock_identifier_missing? - KEYBLOCK_IDENTIFIERS.find do |identify| - !@keyblock.include?(identify) - end - end - -end diff --git a/users/app/models/service_level.rb b/users/app/models/service_level.rb deleted file mode 100644 index 299aaf1..0000000 --- a/users/app/models/service_level.rb +++ /dev/null @@ -1,19 +0,0 @@ -class ServiceLevel - - def initialize(attributes = {}) - @id = attributes[:id] || APP_CONFIG[:default_service_level] - end - - def self.authenticated_select_options - APP_CONFIG[:service_levels].map { |id,config_hash| [config_hash[:description], id] if config_hash[:name] != 'anonymous'}.compact - end - - def id - @id - end - - def config_hash - APP_CONFIG[:service_levels][@id] - end - -end diff --git a/users/app/models/session.rb b/users/app/models/session.rb deleted file mode 100644 index 0d7e10e..0000000 --- a/users/app/models/session.rb +++ /dev/null @@ -1,32 +0,0 @@ -class Session < SRP::Session - include ActiveModel::Validations - include LoginFormatValidation - - attr_accessor :login - - validates :login, :presence => true - - def initialize(user = nil, aa = nil) - super(user, aa) if user - end - - def persisted? - false - end - - def new_record? - true - end - - def to_model - self - end - - def to_key - [object_id] - end - - def to_param - nil - end -end diff --git a/users/app/models/token.rb b/users/app/models/token.rb deleted file mode 100644 index 4856c31..0000000 --- a/users/app/models/token.rb +++ /dev/null @@ -1,69 +0,0 @@ -class Token < CouchRest::Model::Base - - use_database :tokens - - belongs_to :user - - # timestamps! does not create setters and only sets updated_at - # if the object has changed and been saved. Instead of triggering - # that we rather use our own property we have control over: - property :last_seen_at, Time, accessible: false - - validates :user_id, presence: true - - design do - view :by_last_seen_at - end - - def self.expires_after - APP_CONFIG[:auth] && APP_CONFIG[:auth][:token_expires_after] - end - - def self.expired - return [] unless expires_after - by_last_seen_at.endkey(expires_after.minutes.ago) - end - - def self.destroy_all_expired - self.expired.each do |token| - token.destroy - end - end - - def authenticate - if expired? - destroy - return nil - else - touch - return user - end - end - - # Tokens can be cleaned up in different ways. - # So let's make sure we don't crash if they disappeared - def destroy_with_rescue - destroy_without_rescue - rescue RestClient::ResourceNotFound - end - alias_method_chain :destroy, :rescue - - def touch - self.last_seen_at = Time.now - save - end - - def expired? - Token.expires_after and - last_seen_at < Token.expires_after.minutes.ago - end - - def initialize(*args) - super - if new_record? - self.id = SecureRandom.urlsafe_base64(32).gsub(/^_*/, '') - self.last_seen_at = Time.now - end - end -end - diff --git a/users/app/models/unauthenticated_user.rb b/users/app/models/unauthenticated_user.rb deleted file mode 100644 index 0fc17d2..0000000 --- a/users/app/models/unauthenticated_user.rb +++ /dev/null @@ -1,6 +0,0 @@ -# The nil object for the user class -class UnauthenticatedUser < Object - - # will probably want something here to return service level as APP_CONFIG[:service_levels][0] but not sure how will be accessing. - -end diff --git a/users/app/models/user.rb b/users/app/models/user.rb deleted file mode 100644 index c297ac8..0000000 --- a/users/app/models/user.rb +++ /dev/null @@ -1,179 +0,0 @@ -class User < CouchRest::Model::Base - include LoginFormatValidation - - use_database :users - - property :login, String, :accessible => true - property :password_verifier, String, :accessible => true - property :password_salt, String, :accessible => true - - property :enabled, TrueClass, :default => true - - # these will be null by default but we shouldn't ever pull them directly, but only via the methods that will return the full ServiceLevel - property :desired_service_level_code, Integer, :accessible => true - property :effective_service_level_code, Integer, :accessible => true - - property :one_month_warning_sent, TrueClass - - before_save :update_effective_service_level - - validates :login, :password_salt, :password_verifier, - :presence => true - - validates :login, - :uniqueness => true, - :if => :serverside? - - validate :login_is_unique_alias - - validates :password_salt, :password_verifier, - :format => { :with => /\A[\dA-Fa-f]+\z/, :message => "Only hex numbers allowed" } - - validates :password, :presence => true, - :confirmation => true, - :format => { :with => /.{8}.*/, :message => "needs to be at least 8 characters long" } - - timestamps! - - design do - own_path = Pathname.new(File.dirname(__FILE__)) - load_views(own_path.join('..', 'designs', 'user')) - view :by_login - view :by_created_at - end # end of design - - def to_json(options={}) - { - :login => login, - :ok => valid? - }.to_json(options) - end - - def salt - password_salt.hex - end - - def verifier - password_verifier.hex - end - - def username - login - end - - def email_address - LocalEmail.new(login) - end - - # Since we are storing admins by login, we cannot allow admins to change their login. - def is_admin? - APP_CONFIG['admins'].include? self.login - end - - def most_recent_tickets(count=3) - Ticket.for_user(self).limit(count).all #defaults to having most recent updated first - end - - def messages(unseen = true) - #TODO for now this only shows unseen messages. Will we ever want seen ones? Is it necessary to store? - - # we don't want to emit all the userids associated with a message, so only emit id and text. - Message.by_user_ids_to_show.key(self.id).map { |message| [message.id, message.text] } - - end - - # DEPRECATED - # - # Please set the key on the identity directly - # WARNING: This will not be serialized with the user record! - # It is only a workaround for the key form. - def public_key=(value) - identity.set_key(:pgp, value) - end - - # DEPRECATED - # - # Please access identity.keys[:pgp] directly - def public_key - identity.keys[:pgp] - end - - def account - Account.new(self) - end - - def identity - @identity ||= Identity.for(self) - end - - def refresh_identity - @identity = Identity.for(self) - end - - def desired_service_level - code = self.desired_service_level_code || APP_CONFIG[:default_service_level] - ServiceLevel.new({id: code}) - end - - def effective_service_level - code = self.effective_service_level_code || self.desired_service_level.id - ServiceLevel.new({id: code}) - end - - - def self.send_one_month_warnings - - # To determine warnings to send, need to get all users where one_month_warning_sent is not set, and where it was created greater than or equal to 1 month ago. - # TODO: might want to further limit to enabled accounts, and, based on provider's service level configuration, for particular service levels. - users_to_warn = User.by_created_at_and_one_month_warning_not_sent.endkey(Time.now-1.month) - - users_to_warn.each do |user| - # instead of loop could use something like: - # message.user_ids_to_show = users_to_warn.map(&:id) - # but would still need to loop through users to store one_month_warning_sent - - if !@message - # create a message for today's date - # only want to create once, and only if it will be used. - @message = Message.new(:text => I18n.t(:payment_one_month_warning, :date_in_one_month => (Time.now+1.month).strftime("%Y-%d-%m"))) - end - - @message.user_ids_to_show << user.id - user.one_month_warning_sent = true - user.save - end - @message.save if @message - - end - - protected - - ## - # Validation Functions - ## - - def login_is_unique_alias - alias_identity = Identity.find_by_address(self.email_address) - return if alias_identity.blank? - if alias_identity.user != self - errors.add(:login, "has already been taken") - end - end - - def password - password_verifier - end - - # used as a condition for validations that are server side only - def serverside? - true - end - - def update_effective_service_level - # TODO: Is this always the case? Might there be a situation where the admin has set the effective service level and we don't want it changed to match the desired one? - if self.desired_service_level_code_changed? - self.effective_service_level_code = self.desired_service_level_code - end - end - -end |