From 17b67aeda81dee2273ce1161ac7292a328c3efaa Mon Sep 17 00:00:00 2001 From: Azul Date: Thu, 15 May 2014 16:29:49 +0200 Subject: store cert fingerprint with main user identity --- app/models/identity.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'app/models/identity.rb') diff --git a/app/models/identity.rb b/app/models/identity.rb index ad8c01e..2f8d4eb 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -8,6 +8,7 @@ class Identity < CouchRest::Model::Base property :address, LocalEmail property :destination, Email property :keys, HashWithIndifferentAccess + property :cert_fingerprints, [String] validate :unique_forward validate :alias_available -- cgit v1.2.3 From 3a84578cf33685800c9216cfb4da12ea1fb0032f Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 19 May 2014 15:07:02 +0200 Subject: store fingerprints with timestamp Only storing the date as that should suffice for normal expiry and is less useful for identifying users by timestamps --- app/models/identity.rb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'app/models/identity.rb') diff --git a/app/models/identity.rb b/app/models/identity.rb index 2f8d4eb..a4225e7 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -8,7 +8,7 @@ class Identity < CouchRest::Model::Base property :address, LocalEmail property :destination, Email property :keys, HashWithIndifferentAccess - property :cert_fingerprints, [String] + property :cert_fingerprints, Hash validate :unique_forward validate :alias_available @@ -108,6 +108,16 @@ class Identity < CouchRest::Model::Base write_attribute('keys', keys.merge(type => key.to_s)) end + def cert_fingerprints + read_attribute('cert_fingerprints') || Hash.new + end + + def register_cert(cert) + today = DateTime.now.to_date.to_s + write_attribute 'cert_fingerprints', + cert_fingerprints.merge(cert.fingerprint => today) + end + # for LoginFormatValidation def login self.address.handle -- cgit v1.2.3 From 154d32bbc7cfe21d83141ff2c9a3d805165231b8 Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 28 May 2014 10:45:14 +0200 Subject: use Identity for testing login availability We create an identity alongside each user. Make sure the identity is valid when creating the user. This also ensures that the login picked is available because otherwise the identities address would not be available anymore. --- app/models/identity.rb | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) (limited to 'app/models/identity.rb') diff --git a/app/models/identity.rb b/app/models/identity.rb index a4225e7..2be396c 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -10,8 +10,9 @@ class Identity < CouchRest::Model::Base property :keys, HashWithIndifferentAccess property :cert_fingerprints, Hash - validate :unique_forward validate :alias_available + validates :destination, presence: true, + uniqueness: {scope: :address} validate :address_local_email validate :destination_email @@ -44,13 +45,12 @@ class Identity < CouchRest::Model::Base end - def self.for(user, attributes = {}) - find_for(user, attributes) || build_for(user, attributes) + def self.for(user) + find_for(user) || build_for(user) end - def self.find_for(user, attributes = {}) - attributes.reverse_merge! attributes_from_user(user) - find_by_address_and_destination [attributes[:address], attributes[:destination]] + def self.find_for(user) + find_by_user_id(user.id) if user && user.persisted? end def self.build_for(user, attributes = {}) @@ -125,23 +125,17 @@ class Identity < CouchRest::Model::Base 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" + same_address = Identity.by_address.key(address) + if same_address.detect { |other| other.user !=self.user } + errors.add :address, :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 + return if address.valid? + # we only hand on the first error for now. + self.errors.add(:address, address.errors.messages.values.first) end def destination_email -- cgit v1.2.3 From 6fea83763f07add7d3bd07e3843b75aaf61e19b4 Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 28 May 2014 12:20:49 +0200 Subject: bring back the alias functionality in Identities --- app/models/identity.rb | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'app/models/identity.rb') diff --git a/app/models/identity.rb b/app/models/identity.rb index 2be396c..0d25bae 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -11,8 +11,7 @@ class Identity < CouchRest::Model::Base property :cert_fingerprints, Hash validate :alias_available - validates :destination, presence: true, - uniqueness: {scope: :address} + validates :destination, uniqueness: {scope: :address} validate :address_local_email validate :destination_email @@ -45,12 +44,14 @@ class Identity < CouchRest::Model::Base end - def self.for(user) - find_for(user) || build_for(user) + def self.for(user, attributes = {}) + find_for(user, attributes) || build_for(user, attributes) end - def self.find_for(user) - find_by_user_id(user.id) if user && user.persisted? + def self.find_for(user, attributes = {}) + attributes.reverse_merge! attributes_from_user(user) + id = find_by_address_and_destination attributes.values_at(:address, :destination) + return id if id && id.user == user end def self.build_for(user, attributes = {}) @@ -67,7 +68,9 @@ class Identity < CouchRest::Model::Base def self.disable_all_for(user) Identity.by_user_id.key(user.id).each do |identity| identity.disable - identity.save + # if the identity is not unique anymore because the destination + # was reset to nil we destroy it. + identity.save || identity.destroy end end @@ -127,15 +130,15 @@ class Identity < CouchRest::Model::Base def alias_available same_address = Identity.by_address.key(address) - if same_address.detect { |other| other.user !=self.user } + if same_address.detect { |other| other.user != self.user } errors.add :address, :taken end end def address_local_email - return if address.valid? + return if address.valid? #this ensures it is a valid local email address # we only hand on the first error for now. - self.errors.add(:address, address.errors.messages.values.first) + self.errors.add(:address, address.errors.messages[:email].first) end def destination_email -- cgit v1.2.3 From 09dfa583eca69a3925c384c67c3d98cd8c69b360 Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 28 May 2014 12:28:07 +0200 Subject: allow changing the user_id on an identity we set it to nil when we disable it --- app/models/identity.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/models/identity.rb') diff --git a/app/models/identity.rb b/app/models/identity.rb index 0d25bae..a8eaba6 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -130,7 +130,7 @@ class Identity < CouchRest::Model::Base def alias_available same_address = Identity.by_address.key(address) - if same_address.detect { |other| other.user != self.user } + if same_address.detect { |other| other != self && other.user != self.user } errors.add :address, :taken end end -- cgit v1.2.3 From 016e61ce9ab44cf58355e843b0c0d0085d373fc7 Mon Sep 17 00:00:00 2001 From: Azul Date: Thu, 29 May 2014 09:38:53 +0200 Subject: catch corner cases of account creation Users now always check if their identity is valid. We need to make sure this works if the user is a new record and once it has been persisted. While the user is a new record the identity will have no user_id. Old identities that are left to block the login of a user who canceled their account also have a blank user_id. They still should render the new identity invalid so the user can't be saved with a login that has been reserved. Once the user has been persisted we set the user_id on the identity and save it too when creating an Account. This allows us to create a plain user and save it and it will still have an in memory identity only. But the default is to create the user by means of creating an account so an identity will be created as well. --- app/models/identity.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'app/models/identity.rb') diff --git a/app/models/identity.rb b/app/models/identity.rb index a8eaba6..25be971 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -129,8 +129,12 @@ class Identity < CouchRest::Model::Base protected def alias_available - same_address = Identity.by_address.key(address) - if same_address.detect { |other| other != self && other.user != self.user } + blocking_identities = Identity.by_address.key(address).all + blocking_identities.delete self + if self.user + blocking_identities.reject! { |other| other.user == self.user } + end + if blocking_identities.any? errors.add :address, :taken end end @@ -138,13 +142,14 @@ class Identity < CouchRest::Model::Base def address_local_email return if address.valid? #this ensures it is a valid local email address # we only hand on the first error for now. - self.errors.add(:address, address.errors.messages[:email].first) + self.errors.add(:address, address.errors.messages.values.first) 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 + # we only hand on the first error for now. + self.errors.add(:destination, destination.errors.messages.values.first) end end -- cgit v1.2.3 From e0d31118d6e4110d2c280afa9415cfe9def29deb Mon Sep 17 00:00:00 2001 From: Azul Date: Thu, 29 May 2014 10:04:07 +0200 Subject: hand on errors from Email to Identity to User errors.each iterates through all errors for all attrbibutes nicely. --- app/models/identity.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'app/models/identity.rb') diff --git a/app/models/identity.rb b/app/models/identity.rb index 25be971..f2727c8 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -141,15 +141,17 @@ class Identity < CouchRest::Model::Base def address_local_email return if address.valid? #this ensures it is a valid local email address - # we only hand on the first error for now. - self.errors.add(:address, address.errors.messages.values.first) + address.errors.each do |attribute, error| + self.errors.add(:address, error) + end end def destination_email return if destination.nil? # this identity is disabled return if destination.valid? # this ensures it is Email - # we only hand on the first error for now. - self.errors.add(:destination, destination.errors.messages.values.first) + destination.errors.each do |attribute, error| + self.errors.add(:destination, error) + end end end -- cgit v1.2.3 From bbe7b3b7deb2b44d34f7c39dda2c3db284e2bf10 Mon Sep 17 00:00:00 2001 From: Azul Date: Thu, 29 May 2014 11:19:21 +0200 Subject: clearify identity validations Identity.new.valid? should not crash. So validate presence where needed and skip the other validations if the value is absent. --- app/models/identity.rb | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'app/models/identity.rb') diff --git a/app/models/identity.rb b/app/models/identity.rb index f2727c8..2f6241c 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -10,7 +10,9 @@ class Identity < CouchRest::Model::Base property :keys, HashWithIndifferentAccess property :cert_fingerprints, Hash - validate :alias_available + validates :address, presence: true + validate :address_available + validates :destination, presence: true, if: :enabled? validates :destination, uniqueness: {scope: :address} validate :address_local_email validate :destination_email @@ -94,7 +96,11 @@ class Identity < CouchRest::Model::Base end def enabled? - self.destination && self.user_id + self.user_id + end + + def disabled? + !enabled? end def disable @@ -123,12 +129,12 @@ class Identity < CouchRest::Model::Base # for LoginFormatValidation def login - self.address.handle + address.handle if address.present? end protected - def alias_available + def address_available blocking_identities = Identity.by_address.key(address).all blocking_identities.delete self if self.user @@ -140,15 +146,18 @@ class Identity < CouchRest::Model::Base end def address_local_email - return if address.valid? #this ensures it is a valid local email address + # caught by presence validation + return if address.blank? + return if address.valid? address.errors.each do |attribute, error| self.errors.add(:address, error) end end def destination_email - return if destination.nil? # this identity is disabled - return if destination.valid? # this ensures it is Email + # caught by presence validation or this identity is disabled + return if destination.blank? + return if destination.valid? destination.errors.each do |attribute, error| self.errors.add(:destination, error) end -- cgit v1.2.3