user_plane 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +30 -0
- data/app/assets/javascripts/user_plane/application.js +13 -0
- data/app/assets/stylesheets/user_plane/application.css +15 -0
- data/app/concerns/null_object_persistable.rb +62 -0
- data/app/controllers/user/confirm_email_addresses_controller.rb +17 -0
- data/app/controllers/user/details_controller.rb +48 -0
- data/app/controllers/user/invites_controller.rb +69 -0
- data/app/controllers/user/reset_passwords_controller.rb +50 -0
- data/app/controllers/user/sign_ins_controller.rb +53 -0
- data/app/controllers/user/sign_ups_controller.rb +47 -0
- data/app/controllers/user_plane/application_controller.rb +5 -0
- data/app/helpers/user_plane/application_helper.rb +4 -0
- data/app/mailers/user_plane/application_mailer.rb +8 -0
- data/app/mailers/user_plane/invite_mailer.rb +14 -0
- data/app/mailers/user_plane/verification_mailer.rb +25 -0
- data/app/models/session_manager.rb +100 -0
- data/app/models/user.rb +5 -0
- data/app/models/user/account.rb +26 -0
- data/app/models/user/confirm_email_address.rb +55 -0
- data/app/models/user/guest.rb +18 -0
- data/app/models/user/identities.rb +5 -0
- data/app/models/user/identities/email.rb +98 -0
- data/app/models/user/identities/email_verification.rb +70 -0
- data/app/models/user/identities/facebook.rb +5 -0
- data/app/models/user/identities/github.rb +5 -0
- data/app/models/user/identities/id_token.rb +7 -0
- data/app/models/user/identities/o_auth.rb +67 -0
- data/app/models/user/identities/o_auth_endpoint.rb +28 -0
- data/app/models/user/identities/twitter.rb +5 -0
- data/app/models/user/identity.rb +26 -0
- data/app/models/user/reset_password.rb +59 -0
- data/app/models/user/send_password_reset.rb +25 -0
- data/app/models/user/send_sign_up_invite.rb +27 -0
- data/app/models/user/sign_in.rb +42 -0
- data/app/models/user/sign_up.rb +47 -0
- data/app/models/user/sign_up_invites.rb +5 -0
- data/app/models/user/sign_up_invites/invite.rb +46 -0
- data/app/models/user/sign_up_invites/stack.rb +22 -0
- data/app/models/user/sign_up_with_invite.rb +45 -0
- data/app/models/user/suspension.rb +7 -0
- data/app/models/user/update_details.rb +103 -0
- data/app/views/layouts/user_plane/application.html.erb +14 -0
- data/app/views/user/details/edit.html.erb +37 -0
- data/app/views/user/invites/edit.html.erb +34 -0
- data/app/views/user/invites/new.html.erb +21 -0
- data/app/views/user/reset_passwords/edit.html.erb +26 -0
- data/app/views/user/reset_passwords/new.html.erb +21 -0
- data/app/views/user/sign_ins/new.html.erb +25 -0
- data/app/views/user/sign_ups/new.html.erb +33 -0
- data/app/views/user_plane/invite_mailer/invite.html.erb +2 -0
- data/app/views/user_plane/invite_mailer/invite.text.erb +3 -0
- data/app/views/user_plane/verification_mailer/address_verification.html.erb +2 -0
- data/app/views/user_plane/verification_mailer/address_verification.text.erb +4 -0
- data/app/views/user_plane/verification_mailer/password_reset.html.erb +2 -0
- data/app/views/user_plane/verification_mailer/password_reset.text.erb +4 -0
- data/config/initializers/inflections.rb +3 -0
- data/config/locales/en.yml +63 -0
- data/config/locales/it.yml +62 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20121128143404_create_user_accounts.rb +11 -0
- data/db/migrate/20121226202553_create_user_identities_o_auths.rb +12 -0
- data/db/migrate/20121226203032_create_user_identities_emails.rb +13 -0
- data/db/migrate/20121227144617_create_user_identities_email_verifications.rb +15 -0
- data/db/migrate/20130113120152_create_user_identities_id_tokens.rb +12 -0
- data/db/migrate/20141025230304_create_user_sign_up_invites_stacks.rb +10 -0
- data/db/migrate/20141025230500_create_user_sign_up_invites_invites.rb +13 -0
- data/db/migrate/20141026230208_create_user_suspensions.rb +13 -0
- data/lib/generators/user_plane/view/details_generator.rb +22 -0
- data/lib/generators/user_plane/view/helpers.rb +68 -0
- data/lib/generators/user_plane/view/invites_generator.rb +23 -0
- data/lib/generators/user_plane/view/reset_passwords_generator.rb +22 -0
- data/lib/generators/user_plane/view/sign_ins_generator.rb +18 -0
- data/lib/generators/user_plane/view/sign_ups_generator.rb +22 -0
- data/lib/generators/user_plane/views_generator.rb +32 -0
- data/lib/tasks/user_plane_tasks.rake +4 -0
- data/lib/user_plane.rb +43 -0
- data/lib/user_plane/command.rb +24 -0
- data/lib/user_plane/engine.rb +27 -0
- data/lib/user_plane/fresh_validator.rb +9 -0
- data/lib/user_plane/omniauth.rb +50 -0
- data/lib/user_plane/redirect_to_sign_in.rb +22 -0
- data/lib/user_plane/route_concerns.rb +167 -0
- data/lib/user_plane/session_manager_concern.rb +9 -0
- data/lib/user_plane/signed_in_constraint.rb +11 -0
- data/lib/user_plane/token_segment.rb +52 -0
- data/lib/user_plane/version.rb +3 -0
- data/spec/controllers/user/confirm_email_addresses_controller_spec.rb +5 -0
- data/spec/controllers/user/details_controller_spec.rb +5 -0
- data/spec/controllers/user/invites_controller_spec.rb +19 -0
- data/spec/controllers/user/reset_passwords_controller_spec.rb +5 -0
- data/spec/controllers/user/sign_ins_controller_spec.rb +34 -0
- data/spec/controllers/user/sign_ups_controller_spec.rb +5 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +7 -0
- data/spec/dummy/app/controllers/welcome_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +30 -0
- data/spec/dummy/app/views/welcome/index.html.erb +1 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +30 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +49 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +78 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/assets.rb +8 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/user_plane.rb +5 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +43 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/schema.rb +101 -0
- data/spec/dummy/log/development.log +0 -0
- data/spec/dummy/log/test.log +20185 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/-pOuxJZhYk_qXqMNKgm23KfvzyUW71NynNLlcNBOubE.cache +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/3AV9ywHBH56Leqey5LeznxK9vu4HD8fF3zSTk4MiDJA.cache +1 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/5fzR1G0D8ukHkPkLXsUu6rP6qV82aIdx3hugKkDy6nM.cache +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/9bKtJ2lkHPqtboGfbyknZ1OyH4xYO-aml7U3qhv-3kk.cache +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/HjDmE9SFP2wimdNHU8Nff9cm3vFZ5soO1iw7Jdlb6z8.cache +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/J9j6OdatarYW7VzVCVttmGphOhJKL0QXasdheyrgsTE.cache +2 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/KqrOQSlg0Th0N3XXx-h4p5BVJCfN0D8rRLoA9VxvXrc.cache +1 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/UCB1W65KwVU8ttOY8jnPRDp8HyyYYEjeTwwPD6R4qy8.cache +1 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/YmMSaaBmIcNZWPVF9jXcGBi-kwEzMuxzwPT_Zrcj1Bo.cache +2 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/g3wU8ajFWb5ZLPvujEt5l9DesbFCiAwqjx1WQgwTtHA.cache +1 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/i5pp88VHKoqlxQJdgmQd_lkgX1-4em_uHqNDjQ4nyHA.cache +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/jL74yXjxf8cb6Olkjbw1C28MH_HbZe221l8AI6WVeH0.cache +3 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/puh7X4rfS3eDN9oHTXoQdAgqxivonrwAAdYZ4UB3GIg.cache +1 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/qxPWFIWnE6gOCY-SsdBJe7Cgm5D3YUwaEne78Y7XdRg.cache +1 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/rM9s67WgzKMZ1bRhUdA0yhPZDlyRE5a1kmdt7cS6m4c.cache +3 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/v3.0/rVoG8EHlrCOgY4ZkzOj64f0jiTcbteQ_SYNzq9RqY0I.cache +0 -0
- data/spec/fabricators/user_account_fabricator.rb +11 -0
- data/spec/fabricators/user_guest_fabricator.rb +2 -0
- data/spec/fabricators/user_identities_email_fabricator.rb +8 -0
- data/spec/fabricators/user_identities_email_verification_fabricator.rb +5 -0
- data/spec/fabricators/user_identities_id_token_fabricator.rb +2 -0
- data/spec/fabricators/user_sign_up_fabricator.rb +51 -0
- data/spec/fabricators/user_sign_up_invites_invite_fabricator.rb +3 -0
- data/spec/fabricators/user_sign_up_invites_stack_fabricator.rb +4 -0
- data/spec/fabricators/user_suspension_fabricator.rb +4 -0
- data/spec/fabricators/user_update_detail_fabricator.rb +2 -0
- data/spec/features/user_plane/user_plane_invites_spec.rb +31 -0
- data/spec/features/user_plane/user_plane_reset_passwords_spec.rb +31 -0
- data/spec/features/user_plane/user_plane_sign_ins_spec.rb +44 -0
- data/spec/features/user_plane/user_plane_signed_in_only_spec.rb +31 -0
- data/spec/features/user_plane/user_plane_update_details_spec.rb +43 -0
- data/spec/fixtures/user_plane/invite_mailer/invite +3 -0
- data/spec/fixtures/user_plane/verification_mailer/address_verification +3 -0
- data/spec/fixtures/user_plane/verification_mailer/password_reset +3 -0
- data/spec/lib/generators/views_generator_spec.rb +16 -0
- data/spec/lib/route_concerns_spec.rb +54 -0
- data/spec/mailers/previews/user_plane/invite_mailer_preview.rb +11 -0
- data/spec/mailers/previews/user_plane/verification_mailer_preview.rb +16 -0
- data/spec/mailers/user_plane/invite_mailer_spec.rb +25 -0
- data/spec/mailers/user_plane/verification_mailer_spec.rb +52 -0
- data/spec/models/session_manager_spec.rb +28 -0
- data/spec/models/user/account_spec.rb +26 -0
- data/spec/models/user/confirm_email_address_spec.rb +101 -0
- data/spec/models/user/guest_spec.rb +5 -0
- data/spec/models/user/identities/email_spec.rb +5 -0
- data/spec/models/user/identities/email_verification_spec.rb +42 -0
- data/spec/models/user/identities/facebook_spec.rb +5 -0
- data/spec/models/user/identities/github_spec.rb +5 -0
- data/spec/models/user/identities/id_token_spec.rb +5 -0
- data/spec/models/user/identities/o_auth_spec.rb +12 -0
- data/spec/models/user/identities/twitter_spec.rb +5 -0
- data/spec/models/user/reset_password_spec.rb +141 -0
- data/spec/models/user/send_password_reset_spec.rb +44 -0
- data/spec/models/user/send_sign_up_invite_spec.rb +30 -0
- data/spec/models/user/sign_in_spec.rb +31 -0
- data/spec/models/user/sign_up_invites/invite_spec.rb +13 -0
- data/spec/models/user/sign_up_invites/stack_spec.rb +21 -0
- data/spec/models/user/sign_up_spec.rb +58 -0
- data/spec/models/user/sign_up_with_invite_spec.rb +83 -0
- data/spec/models/user/suspension_spec.rb +5 -0
- data/spec/models/user/update_details_spec.rb +98 -0
- data/spec/routing/invites_spec.rb +49 -0
- data/spec/routing/reset_passwords_spec.rb +31 -0
- data/spec/routing/sign_ins_spec.rb +36 -0
- data/spec/routing/update_details_spec.rb +30 -0
- data/spec/shared_contexts/feature_helpers.rb +12 -0
- data/spec/shared_contexts/routing.rb +8 -0
- data/spec/shared_contexts/user.rb +67 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/fabrication.rb +7 -0
- data/spec/support/omniauth.rb +4 -0
- metadata +770 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
module User
|
2
|
+
module Identities
|
3
|
+
|
4
|
+
# The class methods have been moved into a separate module so that they
|
5
|
+
# could be added as association extensions if necessary.
|
6
|
+
module OauthBuildCreateAndFind
|
7
|
+
|
8
|
+
def find_identity sign_in
|
9
|
+
find_from_omniauth(sign_in[:oauth_data])
|
10
|
+
end
|
11
|
+
|
12
|
+
def build_identity sign_up
|
13
|
+
identity = initialize_from_omniauth(sign_up[:oauth_data])
|
14
|
+
sign_up.account.oauth_identities << identity
|
15
|
+
|
16
|
+
identity
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize_from_omniauth ominauth_data
|
20
|
+
identity_provider = provider_from_ominauth(ominauth_data)
|
21
|
+
identity_provider.new(ominauth_data: ominauth_data)
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_from_omniauth ominauth_data
|
25
|
+
identity_provider = provider_from_ominauth(ominauth_data)
|
26
|
+
identity_provider.find_by(uid: ominauth_data[:uid])
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_or_build_from_omniauth ominauth_data
|
30
|
+
find_from_omniauth(ominauth_data) || initialize_from_omniauth(ominauth_data)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def provider_from_ominauth ominauth_data
|
35
|
+
User::Identities.const_get(ominauth_data[:provider].camelize)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class OAuth < Identity
|
40
|
+
# Base class for omniauth-based user authentication
|
41
|
+
|
42
|
+
self.inheritance_column = :provider
|
43
|
+
include SupportSegment::StiHelpers
|
44
|
+
extend OauthBuildCreateAndFind
|
45
|
+
|
46
|
+
belongs_to :account, class_name: 'User::Account'
|
47
|
+
has_one :id_token, as: :identity
|
48
|
+
|
49
|
+
validates :uid, presence: true,
|
50
|
+
uniqueness: {scope: :provider}
|
51
|
+
|
52
|
+
def self.callback
|
53
|
+
OAuthCallback.new(self.name.underscore.to_sym)
|
54
|
+
end
|
55
|
+
|
56
|
+
before_validation do
|
57
|
+
self.build_id_token
|
58
|
+
end
|
59
|
+
|
60
|
+
def ominauth_data= new_ominauth_data
|
61
|
+
self.uid = new_ominauth_data[:uid]
|
62
|
+
self.handle = new_ominauth_data[:info][:name]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module User::Identities
|
2
|
+
class OAuthEndpoint
|
3
|
+
extend ActiveModel::Naming
|
4
|
+
|
5
|
+
attr_accessor :provider
|
6
|
+
|
7
|
+
def initialize provider
|
8
|
+
@provider = provider
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_param
|
12
|
+
provider
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_model
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def persisted?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.model_name
|
24
|
+
ActiveModel::Name.new(self, nil, "OAuthEndpoint")
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module User
|
2
|
+
class Identity < ActiveRecord::Base
|
3
|
+
self.abstract_class = true
|
4
|
+
|
5
|
+
def serialize
|
6
|
+
# TODO: create the token when it's missing
|
7
|
+
id_token.key
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.deserialize id_token
|
11
|
+
token = Identities::IdToken.find_by_key(id_token)
|
12
|
+
token ? token.identity : nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.deserialize! id_token
|
16
|
+
identity = deserialize(id_token)
|
17
|
+
if identity
|
18
|
+
raise User::AccountSuspended unless identity.account.suspensions.empty?
|
19
|
+
identity
|
20
|
+
else
|
21
|
+
raise ActiveRecord::RecordNotFound
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module User
|
2
|
+
class ResetPassword < UserPlane::Command
|
3
|
+
include ActiveModel::Validations::Callbacks
|
4
|
+
|
5
|
+
attribute :password
|
6
|
+
attribute :password_confirmation
|
7
|
+
|
8
|
+
attr_accessor :code
|
9
|
+
attr_accessor :verification
|
10
|
+
attr_accessor :identity
|
11
|
+
|
12
|
+
validates :password, :password_confirmation, presence: true
|
13
|
+
validate {|r| r.errors.add(:code, 'is not valid') unless r.verification}
|
14
|
+
validates :verification, presence: true,
|
15
|
+
receiver: {map_attributes: {created_at: :code,
|
16
|
+
base: :code,
|
17
|
+
spent_at: :code}}
|
18
|
+
validates :identity, receiver: {map_attributes: {password: :password,
|
19
|
+
password_confirmation: :password_confirmation}}
|
20
|
+
|
21
|
+
def to_param
|
22
|
+
self.code
|
23
|
+
end
|
24
|
+
|
25
|
+
def persisted?
|
26
|
+
verification && verification.persisted?
|
27
|
+
end
|
28
|
+
|
29
|
+
def code= token
|
30
|
+
@code = token
|
31
|
+
|
32
|
+
password_reset_query = User::Identities::EmailVerification.password_reset.where(token: code)
|
33
|
+
if @identity = User::Identities::Email.joins(:verifications).merge(password_reset_query).first
|
34
|
+
@verification = identity.verifications.detect {|v| v.token == code}
|
35
|
+
else
|
36
|
+
raise ActiveRecord::RecordNotFound
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def verification= verification
|
41
|
+
return unless verification
|
42
|
+
@code = verification.token
|
43
|
+
@verification = verification
|
44
|
+
@identity = verification.email
|
45
|
+
end
|
46
|
+
|
47
|
+
before_validation do
|
48
|
+
if verification
|
49
|
+
identity.attributes = {password: password,
|
50
|
+
password_confirmation: password_confirmation}
|
51
|
+
verification.spend
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
action do
|
56
|
+
identity.save
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module User
|
2
|
+
class SendPasswordReset < UserPlane::Command
|
3
|
+
attribute :email
|
4
|
+
attr_accessor :code
|
5
|
+
attr_accessor :identity
|
6
|
+
attr_accessor :verification
|
7
|
+
|
8
|
+
def email= address
|
9
|
+
@identity = User::Identities::Email.find_by_address(address)
|
10
|
+
@email = address
|
11
|
+
end
|
12
|
+
|
13
|
+
def persisited?
|
14
|
+
verification ? verification.persisted? : false
|
15
|
+
end
|
16
|
+
|
17
|
+
action do
|
18
|
+
if identity
|
19
|
+
@verification ||= identity.reset_password!
|
20
|
+
@code ||= verification.token
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module User
|
2
|
+
class SendSignUpInvite < UserPlane::Command
|
3
|
+
include ActiveModel::Validations::Callbacks
|
4
|
+
|
5
|
+
attribute :recipient
|
6
|
+
attribute :invite
|
7
|
+
attribute :stack
|
8
|
+
attribute :sender
|
9
|
+
|
10
|
+
validates :invite, receiver: {map_attributes: {recipient: :recipient}}
|
11
|
+
validates :stack, receiver: {map_attributes: {remaining_invites: :remaining_invites}}
|
12
|
+
|
13
|
+
before_validation do
|
14
|
+
# The stack can be provided directly if other models have a stack of invites
|
15
|
+
@stack ||= sender.invites_stack
|
16
|
+
@invite ||= stack.invites.build(recipient: recipient, sender: sender)
|
17
|
+
end
|
18
|
+
|
19
|
+
def persisited?
|
20
|
+
invite ? invite.persisted? : false
|
21
|
+
end
|
22
|
+
|
23
|
+
action do
|
24
|
+
invite.save
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module User
|
2
|
+
class SignIn < UserPlane::Command
|
3
|
+
include ActiveModel::Validations::Callbacks
|
4
|
+
|
5
|
+
model_name.instance_variable_set(:@route_key, 'user_sign_in')
|
6
|
+
|
7
|
+
attribute :email
|
8
|
+
attribute :password
|
9
|
+
attribute :oauth_data
|
10
|
+
attribute :strategy
|
11
|
+
attr_accessor :ominauth_error
|
12
|
+
|
13
|
+
attribute :identity
|
14
|
+
|
15
|
+
validates :ominauth_error, absence: true
|
16
|
+
|
17
|
+
|
18
|
+
validate do |command|
|
19
|
+
if identity = command.identity
|
20
|
+
unless identity.account.suspensions.empty?
|
21
|
+
command.errors.add(:base, :suspended)
|
22
|
+
end
|
23
|
+
elsif strategy = command.strategy
|
24
|
+
sign_in_error = :"unknown_#{strategy.model_name.singular}"
|
25
|
+
command.errors.add(:base, sign_in_error)
|
26
|
+
else
|
27
|
+
raise "Please choose a strategy to perform the sign in"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def sign_in_with strategy
|
32
|
+
@strategy = strategy
|
33
|
+
@identity = strategy.find_identity(self) if identity.nil?
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
action do
|
38
|
+
@identity
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module User
|
2
|
+
class SignUp < UserPlane::Command
|
3
|
+
include ActiveModel::Validations::Callbacks
|
4
|
+
|
5
|
+
attribute :user_name
|
6
|
+
|
7
|
+
attribute :email
|
8
|
+
attribute :password
|
9
|
+
attribute :password_confirmation
|
10
|
+
|
11
|
+
attribute :account
|
12
|
+
attribute :identity
|
13
|
+
|
14
|
+
attribute :oauth_data
|
15
|
+
|
16
|
+
# Validate command receivers
|
17
|
+
validates :identity, receiver: {map_attributes: {address: :email,
|
18
|
+
password: :password,
|
19
|
+
password_confirmation: :password_confirmation,
|
20
|
+
uid: :account}}
|
21
|
+
validates :account, receiver: {map_attributes: {name: :user_name,
|
22
|
+
identities: :email_or_omniauth}}
|
23
|
+
|
24
|
+
def sign_up_with sign_up_strategy
|
25
|
+
@identity = sign_up_strategy.build_identity(self) if identity.nil?
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def account
|
30
|
+
@account ||= new_account
|
31
|
+
end
|
32
|
+
|
33
|
+
action do
|
34
|
+
ActiveRecord::Base.transaction do
|
35
|
+
raise ActiveRecord::Rollback unless account.save
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def new_account
|
42
|
+
Account.new(name: user_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module User::SignUpInvites
|
2
|
+
class Invite < ActiveRecord::Base
|
3
|
+
include TokenSegment
|
4
|
+
|
5
|
+
|
6
|
+
belongs_to :stack
|
7
|
+
belongs_to :sender, class_name: 'User::Account'
|
8
|
+
belongs_to :account, class_name: 'User::Account'
|
9
|
+
has_token :code
|
10
|
+
|
11
|
+
validates :recipient, email: true
|
12
|
+
|
13
|
+
# Once an invite has been associated with an account it cannot be reused
|
14
|
+
validate on: :update do |r|
|
15
|
+
if account_id_changed?
|
16
|
+
previous, current = account_id_change
|
17
|
+
r.errors.add(:base, :redeemed) unless previous.nil?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns a Null invite
|
22
|
+
def self.find_by_code code
|
23
|
+
find_by(code: code) || Null.new(code)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
# A Null invite is an invite-like object created when the invite code is not
|
29
|
+
# valid. It will
|
30
|
+
Null = Struct.new(:code) do
|
31
|
+
include ActiveModel::Validations
|
32
|
+
include NullObjectPersistable
|
33
|
+
|
34
|
+
mimics_persistence_from Invite
|
35
|
+
validate {|r| r.errors.add(:base, :invalid) }
|
36
|
+
|
37
|
+
def method_missing method_name, *arguments
|
38
|
+
return nil if [:stack, :sender, :recipient].include? method_name
|
39
|
+
super
|
40
|
+
end
|
41
|
+
|
42
|
+
def persisted?
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class User::SignUpInvites::Stack < ActiveRecord::Base
|
2
|
+
belongs_to :owner, polymorphic: true
|
3
|
+
has_many :invites, before_add: :decrement_remaining_invites
|
4
|
+
after_initialize :set_remaining_invites
|
5
|
+
|
6
|
+
# FIXME: the validation above should be enough, but doesn't seem to take effect
|
7
|
+
# validates :remaining_invites, numericality: {greater_than_or_equal: 0}
|
8
|
+
validate do |record|
|
9
|
+
record.errors.add(:remaining_invites, :greater_than_or_equal, value: 0) unless (record.remaining_invites >= 0)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def decrement_remaining_invites invite
|
15
|
+
self.remaining_invites -= 1
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_remaining_invites
|
19
|
+
self.remaining_invites ||= 0
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# set_callback :invite_set, :after, :set_email_from_invite
|
2
|
+
module User
|
3
|
+
class SignUpWithInvite < SignUp
|
4
|
+
attribute :invite
|
5
|
+
attr_accessor :code
|
6
|
+
|
7
|
+
|
8
|
+
define_callbacks :invite_set
|
9
|
+
|
10
|
+
validates :invite, receiver: {map_attributes: {created_at: :invite,
|
11
|
+
base: :invite,
|
12
|
+
spent: :invite}}
|
13
|
+
|
14
|
+
validate do |command|
|
15
|
+
# Enforces the need of an invite if the account does not exist
|
16
|
+
if command.account.new_record?
|
17
|
+
command.errors.add_on_blank(:invite) if command.invite.nil?
|
18
|
+
else
|
19
|
+
command.errors.add(:base, :exists)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_param
|
24
|
+
self.code
|
25
|
+
end
|
26
|
+
|
27
|
+
def persisted?
|
28
|
+
invite && invite.persisted?
|
29
|
+
end
|
30
|
+
|
31
|
+
def code= code
|
32
|
+
self.invite = SignUpInvites::Invite.find_by_code(code)
|
33
|
+
end
|
34
|
+
|
35
|
+
def invite= invite
|
36
|
+
@code = invite.code
|
37
|
+
run_callbacks(:invite_set) {@invite = invite}
|
38
|
+
end
|
39
|
+
|
40
|
+
before_validation do
|
41
|
+
account.invite = invite
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|