1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
class Identity < CouchRest::Model::Base
include LoginFormatValidation
use_database :identities
belongs_to :user
property :address, LocalEmail
property :destination, Email
property :keys, HashWithIndifferentAccess
property :cert_fingerprints, Hash
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
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)
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 = {})
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
# if the identity is not unique anymore because the destination
# was reset to nil we destroy it.
identity.save || identity.destroy
end
end
def self.destroy_all_for(user)
Identity.by_user_id.key(user.id).each do |identity|
identity.destroy
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.user_id
end
def disabled?
!enabled?
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
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
address.handle if address.present?
end
protected
def address_available
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
def address_local_email
# 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
# 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
end
end
|