diff --git a/fixtures/base_authentication_src_template/expected/config/authentic.cr b/fixtures/base_authentication_src_template/expected/config/authentic.cr new file mode 100644 index 00000000..b9efc318 --- /dev/null +++ b/fixtures/base_authentication_src_template/expected/config/authentic.cr @@ -0,0 +1,11 @@ +require "./server" + +Authentic.configure do |settings| + settings.secret_key = Lucky::Server.settings.secret_key_base + + unless LuckyEnv.production? + # This value can be between 4 and 31 + fastest_encryption_possible = 4 + settings.encryption_cost = fastest_encryption_possible + end +end diff --git a/fixtures/base_authentication_src_template/expected/db/migrations/.keep b/fixtures/base_authentication_src_template/expected/db/migrations/.keep new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/base_authentication_src_template/expected/db/migrations/00000000000001_create_users.cr b/fixtures/base_authentication_src_template/expected/db/migrations/00000000000001_create_users.cr new file mode 100644 index 00000000..96283bfa --- /dev/null +++ b/fixtures/base_authentication_src_template/expected/db/migrations/00000000000001_create_users.cr @@ -0,0 +1,17 @@ +class CreateUsers::V00000000000001 < Avram::Migrator::Migration::V1 + def migrate + enable_extension "citext" + + create table_for(User) do + primary_key id : Int64 + add_timestamps + add email : String, unique: true, case_sensitive: false + add encrypted_password : String + end + end + + def rollback + drop table_for(User) + disable_extension "citext" + end +end diff --git a/fixtures/base_authentication_src_template/expected/spec/support/.keep b/fixtures/base_authentication_src_template/expected/spec/support/.keep new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/base_authentication_src_template/expected/spec/support/factories/user_factory.cr b/fixtures/base_authentication_src_template/expected/spec/support/factories/user_factory.cr new file mode 100644 index 00000000..bb837ee1 --- /dev/null +++ b/fixtures/base_authentication_src_template/expected/spec/support/factories/user_factory.cr @@ -0,0 +1,6 @@ +class UserFactory < Avram::Factory + def initialize + email "#{sequence("test-email")}@example.com" + encrypted_password Authentic.generate_encrypted_password("password") + end +end diff --git a/fixtures/base_authentication_src_template/expected/src/models/user.cr b/fixtures/base_authentication_src_template/expected/src/models/user.cr new file mode 100644 index 00000000..39729bb2 --- /dev/null +++ b/fixtures/base_authentication_src_template/expected/src/models/user.cr @@ -0,0 +1,13 @@ +class User < BaseModel + include Carbon::Emailable + include Authentic::PasswordAuthenticatable + + table do + column email : String + column encrypted_password : String + end + + def emailable : Carbon::Address + Carbon::Address.new(email) + end +end diff --git a/fixtures/base_authentication_src_template/expected/src/operations/.keep b/fixtures/base_authentication_src_template/expected/src/operations/.keep new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/base_authentication_src_template/expected/src/operations/mixins/.keep b/fixtures/base_authentication_src_template/expected/src/operations/mixins/.keep new file mode 100644 index 00000000..e69de29b diff --git a/fixtures/base_authentication_src_template/expected/src/operations/mixins/password_validations.cr b/fixtures/base_authentication_src_template/expected/src/operations/mixins/password_validations.cr new file mode 100644 index 00000000..c56b9750 --- /dev/null +++ b/fixtures/base_authentication_src_template/expected/src/operations/mixins/password_validations.cr @@ -0,0 +1,12 @@ +module PasswordValidations + macro included + before_save run_password_validations + end + + private def run_password_validations + validate_required password, password_confirmation + validate_confirmation_of password, with: password_confirmation + # 72 is a limitation of BCrypt + validate_size_of password, min: 6, max: 72 + end +end diff --git a/fixtures/base_authentication_src_template/expected/src/operations/mixins/user_from_email.cr b/fixtures/base_authentication_src_template/expected/src/operations/mixins/user_from_email.cr new file mode 100644 index 00000000..862fa9ac --- /dev/null +++ b/fixtures/base_authentication_src_template/expected/src/operations/mixins/user_from_email.cr @@ -0,0 +1,7 @@ +module UserFromEmail + private def user_from_email : User? + email.value.try do |value| + UserQuery.new.email(value).first? + end + end +end diff --git a/fixtures/base_authentication_src_template/expected/src/operations/request_password_reset.cr b/fixtures/base_authentication_src_template/expected/src/operations/request_password_reset.cr new file mode 100644 index 00000000..4941aa7f --- /dev/null +++ b/fixtures/base_authentication_src_template/expected/src/operations/request_password_reset.cr @@ -0,0 +1,25 @@ +class RequestPasswordReset < Avram::Operation + # You can modify this in src/operations/mixins/user_from_email.cr + include UserFromEmail + + attribute email : String + + # Run validations and yield the operation and the user if valid + def run + user = user_from_email + validate(user) + + if valid? + user + else + nil + end + end + + def validate(user : User?) + validate_required email + if user.nil? + email.add_error "is not in our system" + end + end +end diff --git a/fixtures/base_authentication_src_template/expected/src/operations/reset_password.cr b/fixtures/base_authentication_src_template/expected/src/operations/reset_password.cr new file mode 100644 index 00000000..3bdd3c89 --- /dev/null +++ b/fixtures/base_authentication_src_template/expected/src/operations/reset_password.cr @@ -0,0 +1,11 @@ +class ResetPassword < User::SaveOperation + # Change password validations in src/operations/mixins/password_validations.cr + include PasswordValidations + + attribute password : String + attribute password_confirmation : String + + before_save do + Authentic.copy_and_encrypt password, to: encrypted_password + end +end diff --git a/fixtures/base_authentication_src_template/expected/src/operations/sign_in_user.cr b/fixtures/base_authentication_src_template/expected/src/operations/sign_in_user.cr new file mode 100644 index 00000000..de80342e --- /dev/null +++ b/fixtures/base_authentication_src_template/expected/src/operations/sign_in_user.cr @@ -0,0 +1,40 @@ +class SignInUser < Avram::Operation + param_key :user + # You can modify this in src/operations/mixins/user_from_email.cr + include UserFromEmail + + attribute email : String + attribute password : String + + # Run validations and yields the operation and the user if valid + def run + user = user_from_email + validate_credentials(user) + + if valid? + user + else + nil + end + end + + # `validate_credentials` determines if a user can sign in. + # + # If desired, you can add additional checks in this method, e.g. + # + # if user.locked? + # email.add_error "is locked out" + # end + private def validate_credentials(user) + if user + unless Authentic.correct_password?(user, password.value.to_s) + password.add_error "is wrong" + end + else + # Usually ok to say that an email is not in the system: + # https://kev.inburke.com/kevin/invalid-username-or-password-useless/ + # https://github.com/luckyframework/lucky_cli/issues/192 + email.add_error "is not in our system" + end + end +end diff --git a/fixtures/base_authentication_src_template/expected/src/operations/sign_up_user.cr b/fixtures/base_authentication_src_template/expected/src/operations/sign_up_user.cr new file mode 100644 index 00000000..8c46fadc --- /dev/null +++ b/fixtures/base_authentication_src_template/expected/src/operations/sign_up_user.cr @@ -0,0 +1,14 @@ +class SignUpUser < User::SaveOperation + param_key :user + # Change password validations in src/operations/mixins/password_validations.cr + include PasswordValidations + + permit_columns email + attribute password : String + attribute password_confirmation : String + + before_save do + validate_uniqueness_of email + Authentic.copy_and_encrypt(password, to: encrypted_password) if password.valid? + end +end diff --git a/fixtures/base_authentication_src_template/expected/src/queries/user_query.cr b/fixtures/base_authentication_src_template/expected/src/queries/user_query.cr new file mode 100644 index 00000000..8a7e9a7f --- /dev/null +++ b/fixtures/base_authentication_src_template/expected/src/queries/user_query.cr @@ -0,0 +1,2 @@ +class UserQuery < User::BaseQuery +end diff --git a/spec/generators_spec.cr b/spec/generators_spec.cr index 87f11570..f32b8ac5 100644 --- a/spec/generators_spec.cr +++ b/spec/generators_spec.cr @@ -162,4 +162,12 @@ describe "Generators" do end end end + + describe BaseAuthenticationSrcTemplate do + it "generates base authentication src template" do + generate_snapshot("base_authentication_src_template") do + BaseAuthenticationSrcTemplate.new + end + end + end end