Class: Masq::Account
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Masq::Account
- Defined in:
- app/models/masq/account.rb
Defined Under Namespace
Classes: ActivationCodeNotFound, AlreadyActivated
Instance Attribute Summary collapse
-
#password ⇒ Object
attr_accessible :login, :email, :password, :password_confirmation, :public_persona_id, :yubikey_mandatory.
Class Method Summary collapse
-
.authenticate(login, password, basic_auth_used = false) ⇒ Object
Authenticates a user by their login name and password.
-
.encrypt(password, salt) ⇒ Object
Encrypts some data with the salt.
-
.extract_yubico_identity_from_otp(yubico_otp) ⇒ Object
Returns the first twelve chars from the Yubico OTP, which are used to identify a Yubikey.
-
.find_and_activate!(activation_code) ⇒ Object
Finds the user with the corresponding activation code, activates their account and returns the user.
-
.split_password_and_yubico_otp(token) ⇒ Object
Receives a login token which consists of the users password and a Yubico one time password (the otp is always 44 characters long).
-
.verify_yubico_otp(otp) ⇒ Object
Utilizes the Yubico library to verify a one time password.
Instance Method Summary collapse
- #activate! ⇒ Object
-
#active? ⇒ Boolean
The existence of an activation code means they have not activated yet.
- #associate_with_yubikey(otp) ⇒ Object
- #authenticated?(password) ⇒ Boolean
- #authenticated_with_yubikey? ⇒ Boolean
- #disable! ⇒ Object
-
#encrypt(password) ⇒ Object
Encrypts the password with the user salt.
- #forget_me ⇒ Object
- #forgot_password! ⇒ Object
-
#has_otp_device? ⇒ Boolean
Does the user have the possibility to authenticate with a one time password?.
-
#pending? ⇒ Boolean
True if the user has just been activated.
- #recently_forgot_password? ⇒ Boolean
- #recently_reset_password? ⇒ Boolean
-
#remember_me ⇒ Object
These create and unset the fields required for remembering users between browser closes.
- #remember_me_for(time) ⇒ Object
- #remember_me_until(time) ⇒ Object
- #remember_token? ⇒ Boolean
-
#reset_password ⇒ Object
First update the password_reset_code before setting the reset_password flag to avoid duplicate email notifications.
- #to_param ⇒ Object
-
#yubikey_authenticated?(otp) ⇒ Boolean
Is the Yubico OTP valid and belongs to this account?.
Instance Attribute Details
#password ⇒ Object
attr_accessible :login, :email, :password, :password_confirmation, :public_persona_id, :yubikey_mandatory
27 28 29 |
# File 'app/models/masq/account.rb', line 27 def password @password end |
Class Method Details
.authenticate(login, password, basic_auth_used = false) ⇒ Object
Authenticates a user by their login name and password. Returns the user or nil.
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 |
# File 'app/models/masq/account.rb', line 55 def authenticate(login, password, basic_auth_used = false) a = find_by(login: login) if a.nil? && Masq::Engine.config.masq["create_auth_ondemand"]["enabled"] # Need to set some password - but is never used pw = if Masq::Engine.config.masq["create_auth_ondemand"]["random_password"] SecureRandom.hex(13) else password end signup = Masq::Signup.create_account!( login: login, password: pw, password_confirmation: pw, email: "#{login}@#{Masq::Engine.config.masq["create_auth_ondemand"]["default_mail_domain"]}", ) a = signup.account if signup.succeeded? end if !a.nil? && a.active? && a.enabled if a.authenticated?(password) || (Masq::Engine.config.masq["trust_basic_auth"] && basic_auth_used) a.last_authenticated_at = Time.now.utc a.last_authenticated_by_yubikey = a.authenticated_with_yubikey? a.save(validate: false) a end end end |
.encrypt(password, salt) ⇒ Object
Encrypts some data with the salt.
84 85 86 |
# File 'app/models/masq/account.rb', line 84 def encrypt(password, salt) Digest::SHA1.hexdigest("--#{salt}--#{password}--") end |
.extract_yubico_identity_from_otp(yubico_otp) ⇒ Object
Returns the first twelve chars from the Yubico OTP, which are used to identify a Yubikey
99 100 101 |
# File 'app/models/masq/account.rb', line 99 def extract_yubico_identity_from_otp(yubico_otp) yubico_otp[0..11] end |
.find_and_activate!(activation_code) ⇒ Object
Finds the user with the corresponding activation code, activates their account and returns the user.
Raises:
- Account::ActivationCodeNotFound
-
if there is no user with the corresponding activation code
- Account::AlreadyActivated
-
if the user with the corresponding activation code has already activated their account
44 45 46 47 48 49 50 51 |
# File 'app/models/masq/account.rb', line 44 def find_and_activate!(activation_code) raise ArgumentError if activation_code.nil? user = find_by(activation_code: activation_code) raise ActivationCodeNotFound unless user raise AlreadyActivated.new(user) if user.active? user.send(:activate!) user end |
.split_password_and_yubico_otp(token) ⇒ Object
Receives a login token which consists of the users password and a Yubico one time password (the otp is always 44 characters long)
90 91 92 93 94 95 |
# File 'app/models/masq/account.rb', line 90 def split_password_and_yubico_otp(token) token.reverse! yubico_otp = token.slice!(0..43).reverse password = token.reverse [password, yubico_otp] end |
.verify_yubico_otp(otp) ⇒ Object
Utilizes the Yubico library to verify a one time password
104 105 106 107 108 |
# File 'app/models/masq/account.rb', line 104 def verify_yubico_otp(otp) Yubikey::OTP::Verify.new(otp).valid? rescue Yubikey::OTP::InvalidOTPError false end |
Instance Method Details
#activate! ⇒ Object
120 121 122 123 124 125 |
# File 'app/models/masq/account.rb', line 120 def activate! @activated = true self.activated_at = Time.now.utc self.activation_code = nil save end |
#active? ⇒ Boolean
The existence of an activation code means they have not activated yet
116 117 118 |
# File 'app/models/masq/account.rb', line 116 def active? activation_code.nil? end |
#associate_with_yubikey(otp) ⇒ Object
166 167 168 169 170 171 172 173 |
# File 'app/models/masq/account.rb', line 166 def associate_with_yubikey(otp) if self.class.verify_yubico_otp(otp) self.yubico_identity = self.class.extract_yubico_identity_from_otp(otp) save(validate: false) else false end end |
#authenticated?(password) ⇒ Boolean
142 143 144 145 146 147 148 149 150 151 |
# File 'app/models/masq/account.rb', line 142 def authenticated?(password) if password.nil? false elsif password.length < 50 && !(yubico_identity? && yubikey_mandatory?) encrypt(password) == crypted_password elsif Masq::Engine.config.masq["can_use_yubikey"] password, yubico_otp = self.class.split_password_and_yubico_otp(password) @authenticated_with_yubikey = yubikey_authenticated?(yubico_otp) if encrypt(password) == crypted_password end end |
#authenticated_with_yubikey? ⇒ Boolean
162 163 164 |
# File 'app/models/masq/account.rb', line 162 def authenticated_with_yubikey? @authenticated_with_yubikey ||= false end |
#disable! ⇒ Object
221 222 223 |
# File 'app/models/masq/account.rb', line 221 def disable! update_attribute(:enabled, false) end |
#encrypt(password) ⇒ Object
Encrypts the password with the user salt
138 139 140 |
# File 'app/models/masq/account.rb', line 138 def encrypt(password) self.class.encrypt(password, salt) end |
#forget_me ⇒ Object
194 195 196 197 198 |
# File 'app/models/masq/account.rb', line 194 def forget_me self.remember_token_expires_at = nil self.remember_token = nil save(validate: false) end |
#forgot_password! ⇒ Object
200 201 202 203 204 |
# File 'app/models/masq/account.rb', line 200 def forgot_password! @forgotten_password = true make_password_reset_code save end |
#has_otp_device? ⇒ Boolean
Does the user have the possibility to authenticate with a one time password?
133 134 135 |
# File 'app/models/masq/account.rb', line 133 def has_otp_device? !yubico_identity.nil? end |
#pending? ⇒ Boolean
True if the user has just been activated
128 129 130 |
# File 'app/models/masq/account.rb', line 128 def pending? @activated ||= false end |
#recently_forgot_password? ⇒ Boolean
213 214 215 |
# File 'app/models/masq/account.rb', line 213 def recently_forgot_password? @forgotten_password ||= false end |
#recently_reset_password? ⇒ Boolean
217 218 219 |
# File 'app/models/masq/account.rb', line 217 def recently_reset_password? @reset_password ||= false end |
#remember_me ⇒ Object
These create and unset the fields required for remembering users between browser closes
180 181 182 |
# File 'app/models/masq/account.rb', line 180 def remember_me remember_me_for(2.weeks) end |
#remember_me_for(time) ⇒ Object
184 185 186 |
# File 'app/models/masq/account.rb', line 184 def remember_me_for(time) remember_me_until(time.from_now.utc) end |
#remember_me_until(time) ⇒ Object
188 189 190 191 192 |
# File 'app/models/masq/account.rb', line 188 def remember_me_until(time) self.remember_token_expires_at = time self.remember_token = encrypt("#{email}--#{remember_token_expires_at}") save(validate: false) end |
#remember_token? ⇒ Boolean
175 176 177 |
# File 'app/models/masq/account.rb', line 175 def remember_token? remember_token_expires_at && Time.now.utc < remember_token_expires_at end |
#reset_password ⇒ Object
First update the password_reset_code before setting the reset_password flag to avoid duplicate email notifications.
208 209 210 211 |
# File 'app/models/masq/account.rb', line 208 def reset_password update_attribute(:password_reset_code, nil) @reset_password = true end |
#to_param ⇒ Object
111 112 113 |
# File 'app/models/masq/account.rb', line 111 def to_param login end |
#yubikey_authenticated?(otp) ⇒ Boolean
Is the Yubico OTP valid and belongs to this account?
154 155 156 157 158 159 160 |
# File 'app/models/masq/account.rb', line 154 def yubikey_authenticated?(otp) if yubico_identity? && self.class.verify_yubico_otp(otp) (self.class.extract_yubico_identity_from_otp(otp) == yubico_identity) else false end end |