devise_meteor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +4 -0
  4. data/CODE_OF_CONDUCT.md +13 -0
  5. data/Gemfile +16 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +78 -0
  8. data/Rakefile +7 -0
  9. data/app/models/devise_meteor/meteor_profile.rb +12 -0
  10. data/app/models/devise_meteor/meteor_service.rb +16 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +7 -0
  13. data/devise_meteor.gemspec +37 -0
  14. data/lib/devise/strategies/meteor.rb +92 -0
  15. data/lib/devise_meteor/concerns/meteor_user_model.rb +148 -0
  16. data/lib/devise_meteor/engine.rb +10 -0
  17. data/lib/devise_meteor/railtie.rb +7 -0
  18. data/lib/devise_meteor/strategies/encrypter.rb +15 -0
  19. data/lib/devise_meteor/strategies/hasher.rb +91 -0
  20. data/lib/devise_meteor/strategies/strategy.rb +92 -0
  21. data/lib/devise_meteor/version.rb +3 -0
  22. data/lib/devise_meteor.rb +37 -0
  23. data/lib/generators/devise_meteor/install_generator.rb +16 -0
  24. data/lib/generators/templates/meteor_initializer.rb +12 -0
  25. data/spec/factories/users.rb +19 -0
  26. data/spec/models/meteor_authentication_spec.rb +261 -0
  27. data/spec/rails_helper.rb +72 -0
  28. data/spec/spec_helper.rb +92 -0
  29. data/spec/support/api_macros.rb +30 -0
  30. data/spec/support/controller_macros.rb +18 -0
  31. data/spec/support/devise_macros.rb +58 -0
  32. data/spec/support/omniauth_macros.rb +44 -0
  33. data/spec/support/request_macros.rb +13 -0
  34. data/spec/test_app/README.rdoc +3 -0
  35. data/spec/test_app/Rakefile +6 -0
  36. data/spec/test_app/app/assets/javascripts/application.js +13 -0
  37. data/spec/test_app/app/assets/stylesheets/application.css +15 -0
  38. data/spec/test_app/app/controllers/application_controller.rb +5 -0
  39. data/spec/test_app/app/controllers/products_controller.rb +89 -0
  40. data/spec/test_app/app/helpers/application_helper.rb +2 -0
  41. data/spec/test_app/app/models/user.rb +43 -0
  42. data/spec/test_app/app/views/layouts/application.html.erb +14 -0
  43. data/spec/test_app/app/views/products/_form.html.erb +29 -0
  44. data/spec/test_app/app/views/products/edit.html.erb +6 -0
  45. data/spec/test_app/app/views/products/index.html.erb +33 -0
  46. data/spec/test_app/app/views/products/new.html.erb +5 -0
  47. data/spec/test_app/app/views/products/show.html.erb +25 -0
  48. data/spec/test_app/bin/bundle +3 -0
  49. data/spec/test_app/bin/rails +4 -0
  50. data/spec/test_app/bin/rake +4 -0
  51. data/spec/test_app/bin/setup +29 -0
  52. data/spec/test_app/config/application.rb +28 -0
  53. data/spec/test_app/config/boot.rb +5 -0
  54. data/spec/test_app/config/environment.rb +5 -0
  55. data/spec/test_app/config/environments/development.rb +38 -0
  56. data/spec/test_app/config/environments/production.rb +77 -0
  57. data/spec/test_app/config/environments/test.rb +45 -0
  58. data/spec/test_app/config/initializers/assets.rb +11 -0
  59. data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
  60. data/spec/test_app/config/initializers/cookies_serializer.rb +3 -0
  61. data/spec/test_app/config/initializers/devise.rb +268 -0
  62. data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  63. data/spec/test_app/config/initializers/inflections.rb +16 -0
  64. data/spec/test_app/config/initializers/mime_types.rb +4 -0
  65. data/spec/test_app/config/initializers/secret_token.rb +3 -0
  66. data/spec/test_app/config/initializers/session_store.rb +3 -0
  67. data/spec/test_app/config/initializers/wrap_parameters.rb +9 -0
  68. data/spec/test_app/config/locales/en.yml +23 -0
  69. data/spec/test_app/config/mongoid.yml +80 -0
  70. data/spec/test_app/config/routes.rb +5 -0
  71. data/spec/test_app/config/secrets.yml +22 -0
  72. data/spec/test_app/config.ru +4 -0
  73. data/spec/test_app/lib/tasks/cucumber.rake +65 -0
  74. data/spec/test_app/log/test.log +23511 -0
  75. data/spec/test_app/public/404.html +67 -0
  76. data/spec/test_app/public/422.html +67 -0
  77. data/spec/test_app/public/500.html +66 -0
  78. data/spec/test_app/public/favicon.ico +0 -0
  79. metadata +350 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a0d562215a30614c1da588c4290a506c77df9fef
4
+ data.tar.gz: 6ebdf968382eb4b6b533b2d91f36121a59a62bf3
5
+ SHA512:
6
+ metadata.gz: bdf04c8e1df027241a6eead6ba19c98f523483d346335132376d4ddee959fb465a6fabf4275ba1c2c4280d045c945eb9a75495fe8c6db674ec5a7778573c35d3
7
+ data.tar.gz: 88b6ccbd2eeb70290a4a62b1621b9f5b70f4c1a1eef589097cf6f8977d54e1e008b7ef5e1419986d18f496e886f920ad81c346ddf907dfde11359f8323ad4717
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.10.6
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in devise_meteor.gemspec
4
+ gemspec
5
+
6
+ # Declare any dependencies that are still in development here instead of in
7
+ # your gemspec. These might include edge Rails or gems from your path or
8
+ # Git. Remember to move these dependencies to your gemspec before releasing
9
+ # your gem to rubygems.org.
10
+
11
+ # To use a debugger
12
+ # gem 'byebug', group: [:development, :test]
13
+
14
+ gem 'mongoid'
15
+ gem 'devise'
16
+ gem 'cucumber-rails', :require => false, group: [:development, :test]
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Jan Jezek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # Mongoid Devise For Meteor
2
+
3
+ This gem provides a convenient way to connect your RailsApp to an existing Meteor app.
4
+
5
+ ### What it does:
6
+
7
+ It saves the first matching `email` from meteors `emails`-Array into the `email` field which is used by devise.
8
+ It saves the `hash` from meteors `services.password.bcrypt` into the `encrypted_password`-field which is used by devise.
9
+ It confirms the devise user if the matching `email` was confirmed already in `meteor`.
10
+
11
+ ## HELP NEEDED
12
+
13
+ ### What it does not:
14
+
15
+ At the moment it is not possible to use the password generated by app `one` on app `two` and vice versa. I dont know why.
16
+ As you can see in the source, I have built a custom `Sha256`-class which should act like the meteor encrypt/decrypt process.
17
+ For any reason the comparison between the meteor generated bcrypt/sha256 password and the custom/devise generated one differs.
18
+
19
+ But for now, it's possible to generate a new one by using the default methods provided by devise.
20
+
21
+
22
+ ## Requirements
23
+
24
+ 1. You need an running Meteor server with the `accounts-password` library, ready setup and tested
25
+ 2. You need an running Rails app with mongoid as the default database up and running with `database_a_uthenticatable`
26
+
27
+ ## Installation
28
+
29
+ Add this line to your application's Gemfile:
30
+
31
+ ```ruby
32
+ gem 'devise_meteor'
33
+ ```
34
+
35
+ And then execute:
36
+
37
+ $ bundle install
38
+
39
+ Add change/add the new authentication strategy to your `initializers/devise.rb`.
40
+ It is already a placeholder near the bottom of the file:
41
+
42
+ ```ruby
43
+ # ==> Warden configuration
44
+ # If you want to use other strategies, that are not supported by Devise, or
45
+ # change the failure app, you can configure them inside the config.warden block.
46
+ #
47
+ config.warden do |manager|
48
+ manager.strategies.add(:meteor, Devise::Strategies::Meteor)
49
+ manager.default_strategies(scope: :user).unshift :meteor
50
+ end
51
+ ```
52
+
53
+
54
+ ## Usage
55
+
56
+ Add the following after your field definitions in your `resource` (mostly the User model).
57
+
58
+ ```ruby
59
+ include DeviseMeteor::MeteorUserModel
60
+
61
+ # disable the mapping for the password simply use this line (not working right now)
62
+ disable_meteor_mapping
63
+
64
+ ```
65
+
66
+ This adds the profile and services relations are used by every meteor app which uses `accounts`.
67
+ Also the handling for password updates are made in this class.
68
+
69
+
70
+ ## Contributing
71
+
72
+ Bug reports and pull requests are welcome on GitHub at https://github.com/mediatainment/devise_meteor This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
73
+
74
+
75
+ ## License
76
+
77
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
78
+
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :test => :spec
@@ -0,0 +1,12 @@
1
+ module DeviseMeteor
2
+ class MeteorProfile
3
+ include Mongoid::Document
4
+
5
+ embedded_in :user
6
+
7
+ field :name, type: String, default: ""
8
+ field :username, type: String, default: ""
9
+ field :firstName, type: String, default: ""
10
+ field :lastName, type: String, default: ""
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module DeviseMeteor
2
+ class MeteorService
3
+ include Mongoid::Document
4
+
5
+ embedded_in :user
6
+ # to allow dynamic fields for the different authentication systems
7
+ # otherwise the providers can be defined as field with type Hash
8
+ # field :facebook, type: Hash
9
+ # include Mongoid::Attributes::Dynamic
10
+ field :facebook, type: Hash
11
+ index({facebook: 1, 'facebook.authToken' => 1})
12
+
13
+ field :resume, type: Hash
14
+ field :password, type: Hash
15
+ end
16
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "devise_meteor"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'devise_meteor/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "devise_meteor"
8
+ spec.version = DeviseMeteor::VERSION
9
+ spec.authors = ["Jan Jezek"]
10
+ spec.email = ["mail@mediatainment-productions.com"]
11
+
12
+ spec.summary = "Enables to authenticate devise on a devise_meteor hosted mongoid server"
13
+ spec.description = "This app closes the gap between the huge world of ruby and devise_meteor. Simply install, configure and use it to connect to the desired devise_meteor server."
14
+ spec.homepage = "https://www.github.com/mediatainment/devise_meteor"
15
+ spec.license = "MIT"
16
+
17
+ spec.add_dependency "rails", ">=4"
18
+ spec.add_dependency 'devise'
19
+ spec.add_dependency 'mongoid', ">=5.0"
20
+ spec.add_dependency 'bcrypt'
21
+
22
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ spec.test_files = Dir["spec/**/*"]
24
+
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_development_dependency "bundler", "~> 1.10"
30
+ spec.add_development_dependency "factory_girl_rails"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency 'rspec-rails'
33
+ spec.add_development_dependency "capybara"
34
+ spec.add_development_dependency "faker"
35
+ spec.add_development_dependency "database_cleaner"
36
+ spec.add_development_dependency 'json_spec', '~> 1.1', '>= 1.1.4'
37
+ end
@@ -0,0 +1,92 @@
1
+ require 'devise/strategies/authenticatable'
2
+
3
+ module Devise
4
+ module Strategies
5
+ class Meteor < Authenticatable
6
+
7
+ def valid?
8
+ valid_for_params_auth? || valid_for_http_auth?
9
+ end
10
+
11
+ def authenticate!
12
+ self.password = params_auth_hash[:password]
13
+
14
+ # search for user email
15
+ devise_resource = valid_for_params_auth? && mapping.to.find_for_database_authentication(authentication_hash)
16
+ meteor_resource = User.where(:emails.elem_match => {address: params_auth_hash[:email]}).first unless devise_resource
17
+ resource = devise_resource || meteor_resource
18
+
19
+ return fail!(:not_found_in_database) unless resource
20
+
21
+ # before continuing authentication process
22
+ # check existing passwords and synchronize them
23
+ if meteor_auth_missing?(resource)
24
+ # when already devise credentials stored
25
+ # assign them to devise_meteor fields
26
+ new_hashed_password = User.new(:password => password).encrypted_password
27
+ resource.services.set(password: {bcrypt: new_hashed_password})
28
+
29
+ elsif devise_auth_missing?(resource)
30
+ # when user registered through devise_meteor
31
+ # and credentials for devise not present
32
+ email = params_auth_hash[:email]
33
+ crypt = resource.services.password[:bcrypt]
34
+
35
+ unless resource.update_attributes(encrypted_password: crypt, email: email)
36
+ fail(resource.unauthenticated_message)
37
+ end
38
+ resource.reload
39
+
40
+ elsif both_auth_present?(resource)
41
+ #when both passwords already set
42
+ else
43
+ return pass
44
+ end
45
+
46
+ # now do validation
47
+ # this code is copied from Devise::Strategies::DatabaseAuthenticable
48
+ encrypted = false
49
+ if validate(resource) { encrypted = true; resource.valid_password?(password) }
50
+ remember_me(resource)
51
+ resource.after_database_authentication
52
+ success!(resource)
53
+ end
54
+
55
+ mapping.to.new.password = password if !encrypted && Devise.paranoid
56
+ fail(:not_found_in_database) unless resource
57
+ end
58
+
59
+ private
60
+
61
+ def both_auth_present?(resource)
62
+ has_a_meteor_password?(resource) && has_a_devise_password?(resource)
63
+ end
64
+
65
+ def devise_auth_missing?(resource)
66
+ has_a_meteor_password?(resource) && !has_a_devise_password?(resource)
67
+ end
68
+
69
+ def meteor_auth_missing?(resource)
70
+ has_a_devise_password?(resource) && !has_a_meteor_password?(resource)
71
+ end
72
+
73
+ def has_a_devise_password?(resource)
74
+ resource.encrypted_password.present?
75
+ end
76
+
77
+ def has_a_meteor_password?(resource)
78
+ resource.services.password.present?
79
+ end
80
+
81
+ # if you migrate from some other strategies than bcrypt
82
+ def legacy_authenticates?(resource, password)
83
+ # resource.encrypted_password == encrypt(resource, password)
84
+ end
85
+
86
+ def encrypt(resource, password)
87
+ # some old strategies code
88
+ end
89
+
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,148 @@
1
+ module DeviseMeteor
2
+ module MeteorUserModel
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ # createdAt is needed, so we take regular timestamps and remap them
7
+ include Mongoid::Timestamps
8
+
9
+ # is used by devise_meteor
10
+ field :createdAt, type: Time
11
+ # aliasing devise created_at to be able
12
+ # to use it with meteors createdAt
13
+ alias_attribute :created_at, :createdAt
14
+
15
+ # An Array with hashes for emails
16
+ field :emails, type: Array, default: []
17
+ field :firstName, type: String, as: :first_name
18
+ field :lastName, type: String, as: :last_name
19
+ # profile relation
20
+ embeds_one :profile, autobuild: true, class_name: "DeviseMeteor::MeteorProfile"
21
+ embeds_one :services, autobuild: true, class_name: "DeviseMeteor::MeteorService"
22
+
23
+ before_create :meteor_init
24
+ before_validation :meteor_map_attributes
25
+
26
+ end
27
+
28
+ module ClassMethods
29
+
30
+ end
31
+
32
+ def name
33
+ profile.name
34
+ end
35
+
36
+ def name=(name)
37
+ profile.update_attribute(:name, name)
38
+ end
39
+
40
+ def username
41
+ profile.username
42
+ end
43
+
44
+ def username=(username)
45
+ profile.update_attribute(:username, username)
46
+ end
47
+
48
+ def meteor_get_hash_by_email(given_mail)
49
+ used_mail = given_mail.nil? ? email : given_mail
50
+ emails.detect { |mail| mail[:address] == used_mail }
51
+ end
52
+
53
+ # called by devise after confirmation is set
54
+ def after_confirmation
55
+ emails_temp = emails
56
+ # we have to do this, by pop and re-adding because
57
+ # MongoDb does not save when update within a nested array when is called by $set
58
+ # So we pop our email out and overwrite the array completely
59
+ # This approach does not work like expected which should update AND PERSIST, which does not work
60
+ # User.where("emails.address" => email)
61
+ # .set('emails.$.verified': true)
62
+ hash = emails.pop { |m| m[:address] == email }
63
+ hash[:verified] = true
64
+ emails_temp << hash
65
+ set(emails: emails_temp)
66
+ end
67
+
68
+ # Verifies whether a password (ie from sign in) is the user password.
69
+ def valid_password?(password)
70
+ DeviseMeteor::Encrypter.compare(password, encrypted_password)
71
+ end
72
+
73
+ def password_digest(password)
74
+ DeviseMeteor::Encrypter.digest(password)
75
+ end
76
+
77
+ private
78
+
79
+ # build a default password hash
80
+ def build_meteor_password_hash(encrypted_password)
81
+ {bcrypt: encrypted_password}
82
+ end
83
+
84
+ # maps current email to devise_meteor emails: :address
85
+ def meteor_map_email
86
+ if attribute_changed?(:email) && email.present?
87
+ existing_meteor_mail = meteor_get_hash_by_email(email)
88
+ if existing_meteor_mail
89
+ emails.delete(existing_meteor_mail)
90
+ if existing_meteor_mail[:verified] == true
91
+ new_mail = build_meteor_mail_hash(email, true)
92
+ skip_confirmation!
93
+ else
94
+ new_mail = build_meteor_mail_hash(email, false)
95
+ end
96
+ else
97
+ new_mail = build_meteor_mail_hash(email, false)
98
+ end
99
+
100
+ add_to_set(emails: new_mail)
101
+ end
102
+ end
103
+
104
+ def meteor_map_name
105
+ if attribute_changed?(:name) && name.present?
106
+ name = first_name + " " + last_name
107
+ profile.update_attribute(:name, name)
108
+ end
109
+ end
110
+
111
+ # build a default email hash
112
+ def build_meteor_mail_hash(email, verified_state)
113
+ {address: email, verified: verified_state}
114
+ end
115
+
116
+ # remaps devise for devise_meteor
117
+ def meteor_map_attributes
118
+ meteor_map_email
119
+ meteor_map_name
120
+ meteor_map_password
121
+ end
122
+
123
+ def meteor_init
124
+ meteor_create_initial_email
125
+ end
126
+
127
+ # called when user is created
128
+ def meteor_create_initial_email
129
+ add_to_set(emails: build_meteor_mail_hash(email, confirmed?))
130
+ end
131
+
132
+ def meteor_map_password
133
+ if (attribute_changed?(:encrypted_password) && encrypted_password.present?) ||
134
+ (encrypted_password.nil? && !sessions.password[:bcrypt].nil?)
135
+
136
+ password_hash = build_meteor_password_hash(encrypted_password)
137
+
138
+ services.set(password: password_hash)
139
+
140
+ elsif encrypted_password.nil?
141
+ update_attribute!(:encrypted_password, services.password[:bcrypt])
142
+
143
+ end
144
+ end
145
+
146
+
147
+ end
148
+ end