-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
321 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# Authentication | ||
|
||
To add authentication to your user model just use `Jennifer::Model::Authentication` module's `with_authentication` macro: | ||
|
||
```crystal | ||
require "jennifer/model/authentication" | ||
class User < Jennifer::Model::Base | ||
include Jennifer::Model::Authentication | ||
with_authentication | ||
mapping( | ||
id: Primary32, | ||
email: {type: String, default: ""}, | ||
password_digest: {type: String, default: ""}, | ||
password: Password, | ||
password_confirmation: { type: String?, virtual: true } | ||
) | ||
end | ||
``` | ||
|
||
`Password` in the `password` field definition is actually `Jennifer::Model::Authentication::Password` constant which includes definition for virtual password attribute. It looks like: | ||
|
||
```crystal | ||
Password = { | ||
type: String?, | ||
virtual: true, | ||
setter: false | ||
} | ||
``` | ||
|
||
Mapping automatically resolves it to it's definition. At the moment only top level non generic definition could be used, e.g. `password: { type: Password }` and `password: Password?` are not supported. | ||
|
||
For authentication `Crypto::Bcrypt::Password` is used. This mechanism requires you to have a `password_digest`, `password`, `password_confirmation` attributes defined in your mapping. This attribute can be customized - `with_authentication` macro accepts next arguments: | ||
|
||
- `password` - represents string based raw password attribute name; | ||
- `password_digest` - represents string based encrypted password. | ||
|
||
> NOTE: `password_confirmation` attribute name is generated based on the `password` value + `_confirmation`. | ||
The following validations are added automatically: | ||
|
||
- password must be present on creation; | ||
- password length should be less than or equal to 51 characters; | ||
- confirmation of password (using a password_confirmation attribute). | ||
|
||
If password confirmation validation is not needed, simply leave out the value for password_confirmation (i.e. don't provide a form field for it). When this attribute has a nil value, the validation will not be triggered. | ||
|
||
```crystal | ||
user = User.build(name: "david") | ||
user.password = "" | ||
user.password_confirmation = "nomatch" | ||
user.save # => false, password required | ||
user.password = "mUc3m00RsqyRe" | ||
user.save # => false, confirmation doesn't match | ||
user.password_confirmation = 'mUc3m00RsqyRe' | ||
user.save # => true | ||
user.authenticate("notright") # => false | ||
user.authenticate("mUc3m00RsqyRe") # => user | ||
User.find_by(name: "david").try(&.authenticate("notright")) # nil | ||
User.find_by(name: "david").try(&.authenticate("mUc3m00RsqyRe")) # => user | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,25 @@ | ||
# Jennifer Documentation | ||
|
||
* [Latest API Documentation](https://imdrasil.github.io/jennifer.cr/latest/) | ||
|
||
* [Other API versions](https://imdrasil.github.io/jennifer.cr/versions) | ||
|
||
## Content | ||
|
||
* [Configuration](./configuration.md) | ||
|
||
* [Migration](./migration.md) | ||
|
||
* [Model. Mapping](./model_mapping.md) | ||
|
||
* [Model. STI](./model_sti.md) | ||
|
||
* [View](./view.md) | ||
|
||
* [Callbacks](./callbacks.md) | ||
|
||
* [Validation](./validation.md) | ||
|
||
* [Timestamps](./timestamps.md) | ||
* [Authentication](./authentication.md) | ||
* [Relations](./relations.md) | ||
|
||
* [CRUD](./crud.md) | ||
|
||
* [Jennifer::Record](./record.md) | ||
|
||
* [Query DSL](./query_dsl.md) | ||
|
||
* [Eager loading](./eager_loading.md) | ||
|
||
* [Pagination and Ordering](./pagination_and_ordering.md) | ||
|
||
* [Model. Scopes](./model_scopes.md) | ||
|
||
* [Aggregation](./aggregation.md) | ||
|
||
* [Transaction and Lock](./transaction_and_lock.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
class CreateUsers < Jennifer::Migration::Base | ||
def up | ||
create_table(:users) do |t| | ||
t.string :email, {:null => false} | ||
t.string :password_digest, {:null => false} | ||
t.string :name | ||
end | ||
|
||
change_table(:contacts) do |t| | ||
t.add_column :user_id, :integer | ||
end | ||
refresh_male_contacts_view | ||
end | ||
|
||
def down | ||
drop_table :users | ||
change_table(:contacts) do |t| | ||
t.drop_column :user_id | ||
end | ||
refresh_male_contacts_view | ||
end | ||
|
||
private def refresh_male_contacts_view | ||
drop_view(:male_contacts) | ||
create_view(:male_contacts, Jennifer::Query["contacts"].where { sql("gender = 'male'") }) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
f1="./src/jennifer.cr " | ||
f2="./src/jennifer/adapter/mysql.cr " # for mysql | ||
f3="./src/jennifer/adapter/postgres.cr " # for postgres | ||
f4="./src/jennifer/model/authentication.cr " | ||
|
||
echo $f1$f2$f3 | xargs crystal doc | ||
echo $f1$f2$f3$f4 | xargs crystal doc -odoc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
require "../spec_helper" | ||
|
||
describe Jennifer::Model::Authentication do | ||
describe "%with_authentication" do | ||
context "with default field names" do | ||
default_user = Factory.build_user | ||
|
||
describe "validations" do | ||
it do | ||
user = Factory.build_user | ||
user.password = "1" * 52 | ||
user.should validate(:password).with("is too long (maximum is 51 characters)") | ||
end | ||
|
||
it { Factory.build_user([:with_invalid_password_confirmation]).should validate(:password).with("doesn't match Password") } | ||
it { Factory.build_user.should validate(:password).with("can't be blank") } | ||
it { Factory.build_user([:with_valid_password]).should be_valid } | ||
|
||
it do | ||
user = Factory.build_user | ||
user.password_digest = Crypto::Bcrypt::Password.create("password").to_s | ||
user.should be_valid | ||
end | ||
it do | ||
Factory.create_user([:with_password_digest]) | ||
user = User.all.last! | ||
user.should be_valid | ||
end | ||
end | ||
|
||
describe "::password_digest_cost" do | ||
it { User.password_digest_cost.should eq(Crypto::Bcrypt::DEFAULT_COST) } | ||
end | ||
|
||
describe "#password=" do | ||
it do | ||
user = Factory.build_user | ||
user.password = nil | ||
user.password_digest.should eq("") | ||
end | ||
|
||
it do | ||
user = Factory.build_user | ||
user.password = "" | ||
user.password_digest.should eq("") | ||
end | ||
|
||
it do | ||
user = Factory.build_user | ||
user.password = "1" * 53 | ||
user.password_digest.should eq("") | ||
end | ||
|
||
it do | ||
user = Factory.build_user | ||
user.password = "password" | ||
user.password_digest.empty?.should_not be_true | ||
end | ||
end | ||
|
||
describe "#authenticate" do | ||
it { Factory.build_user([:with_password_digest]).authenticate("gibberish").should be_nil } | ||
it do | ||
user = Factory.build_user([:with_password_digest]) | ||
user.authenticate("password").should eq(user) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.