mortal-token 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +16 -10
- data/README.md +44 -0
- data/lib/mortal-token/config.rb +18 -0
- data/lib/mortal-token/mortal-token.rb +20 -22
- data/lib/mortal-token/token.rb +48 -48
- data/lib/mortal-token/version.rb +2 -2
- data/lib/mortal-token.rb +4 -2
- metadata +11 -10
- data/README.rdoc +0 -106
- data/lib/mortal-token/configuration.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b8a22fd9bc7c9038590081f95128581bdab8d28
|
4
|
+
data.tar.gz: 6d0812debf82f95e6211377aa06467209480ded2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7522365167e5e715a19a87c996e088c7cbcbee5f585a37a5a638167cb3f7a5fb263b97bc137d3f37f86dd73d5ebb9e26912644d357f6b3eb00fe158fb7572f0
|
7
|
+
data.tar.gz: 5e600b54b463f25eff00709b92b18652ecda7192bdb581ddb51c20a83d3c8e7a091f7f5bf36bb921bae91b8c10c2028d0686623da3140c3ac988a546ce090ea6
|
data/LICENSE
CHANGED
@@ -1,13 +1,19 @@
|
|
1
|
-
|
1
|
+
Copyright (c) 2015 Jordan Hollinger
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
6
9
|
|
7
|
-
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
8
12
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# MortalToken, because some tokens shouldn't live forever
|
2
|
+
|
3
|
+
MortalToken is a convenience wrapper for HMAC-based authentication. Tokens self-destruct after a specified time period; no need to store and look them up for verification. They may optionally contain a message, which is visible but tamper-proof.
|
4
|
+
|
5
|
+
## Simple token with validity check
|
6
|
+
|
7
|
+
require 'mortal-token'
|
8
|
+
MortalToken.secret = 'asdf092$78roasdjfjfaklmsdadASDFopijf98%2ejA#Df@sdf'
|
9
|
+
|
10
|
+
token = MortalToken.create(60 * 60) # 1 hr
|
11
|
+
give_to_client token.to_s
|
12
|
+
token_str = get_from_client
|
13
|
+
if MortalToken.valid? token_str
|
14
|
+
# it's valid
|
15
|
+
else
|
16
|
+
# it's invalid or expired
|
17
|
+
end
|
18
|
+
|
19
|
+
## Token with tamper-proof message
|
20
|
+
|
21
|
+
require 'mortal-token'
|
22
|
+
MortalToken.secret = 'asdf092$78roasdjfjfaklmsdadASDFopijf98%2ejA#Df@sdf'
|
23
|
+
|
24
|
+
token = MortalToken.create(60 * 60, 'some message')
|
25
|
+
give_to_client token.to_s
|
26
|
+
token_str = get_from_client
|
27
|
+
token, digest = MortalToken.recover(token_str)
|
28
|
+
if token == digest
|
29
|
+
# It's valid. But remember - the message could have been read by anyone with access to the token.
|
30
|
+
do_stuff_with token.message
|
31
|
+
else
|
32
|
+
# it's invalid or expired
|
33
|
+
end
|
34
|
+
|
35
|
+
## Tweak token parameters
|
36
|
+
|
37
|
+
You may tweak certain parameters of the library in order to make it more secure, less, faster, etc. These are the defaults:
|
38
|
+
|
39
|
+
MortalToken.digest = 'sha512' # The digest algorithm used by HMAC
|
40
|
+
MortalToken.salt_length = 8 # Number of bits in tokens' salts
|
41
|
+
|
42
|
+
## Testing
|
43
|
+
|
44
|
+
rake test
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MortalToken
|
2
|
+
class << self
|
3
|
+
# The master secret token (Keep it secret! Keep it safe!). Changing this will invalidate all existing tokens.
|
4
|
+
attr_accessor :secret
|
5
|
+
# The digest to use. Defaults to 'sha512'.
|
6
|
+
attr_reader :digest
|
7
|
+
# Salt length in bytes. Defaults to 8.
|
8
|
+
attr_accessor :salt_length
|
9
|
+
end
|
10
|
+
|
11
|
+
# Set a new digest type
|
12
|
+
def self.digest=(name)
|
13
|
+
@digest = OpenSSL::Digest.new name
|
14
|
+
end
|
15
|
+
|
16
|
+
self.digest = 'sha512'
|
17
|
+
self.salt_length = 8
|
18
|
+
end
|
@@ -1,26 +1,24 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
config.token(salt, expires)
|
9
|
-
end
|
10
|
-
|
11
|
-
# Returns a new or existing MortalToken::Configuration. If you pass a block, it will pass it the config object.
|
12
|
-
# If it's a new config, it will inherit the default settings.
|
13
|
-
def config(name=:default)
|
14
|
-
config = (CONFIGS[name] ||= Configuration.new(name))
|
15
|
-
yield config if block_given?
|
16
|
-
config
|
17
|
-
end
|
1
|
+
module MortalToken
|
2
|
+
# Create a new token that lasts for N seconds. Message is optional, but must be a string when present.
|
3
|
+
def self.create(seconds, message = nil)
|
4
|
+
expires = Time.now.utc.to_i + seconds
|
5
|
+
salt = SecureRandom.hex MortalToken.salt_length
|
6
|
+
Token.new expires, salt, message
|
7
|
+
end
|
18
8
|
|
19
|
-
|
9
|
+
# Recover a token and digest created with MortalToken#to_s. Returns [token, digest].
|
10
|
+
# You must then check their validity with "token == digest"
|
11
|
+
def self.recover(token_str)
|
12
|
+
h = JSON.parse Base64.urlsafe_decode64 token_str.to_s
|
13
|
+
token = Token.new h['expires'], h['salt'], h['message']
|
14
|
+
return token, h['digest']
|
15
|
+
rescue ArgumentError, JSON::ParserError
|
16
|
+
nil
|
17
|
+
end
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
# Check if a token created with MoralToken#to_s is valid.
|
20
|
+
def self.valid?(token_str)
|
21
|
+
token, digest = recover token_str
|
22
|
+
token == digest
|
25
23
|
end
|
26
24
|
end
|
data/lib/mortal-token/token.rb
CHANGED
@@ -1,70 +1,70 @@
|
|
1
|
-
|
1
|
+
module MortalToken
|
2
|
+
# Create a token and check if it's still valid:
|
3
|
+
#
|
4
|
+
# token = MortalToken.create(300) # 5 min
|
5
|
+
# give_to_client token.to_s
|
6
|
+
# token_str = get_from_client
|
7
|
+
# MoralToken.valid? token_str
|
8
|
+
#
|
9
|
+
# Create a message token. The client *will* be able to read the message, but they *won't* be able to tamper with it.
|
10
|
+
# If your message must aslo be read-proof, you'll have to encrypt it and decrypt it yourself.
|
11
|
+
#
|
12
|
+
# token = MortalToken.create(300, "message")
|
13
|
+
# give_to_client token.to_s
|
14
|
+
# token_str = get_from_client
|
15
|
+
# token, digest = MortalToken.recover token_str
|
16
|
+
# if token == digest
|
17
|
+
# # It's valid
|
18
|
+
# do_stuff_with token.message
|
19
|
+
# else
|
20
|
+
# # The token was invalid or expired
|
21
|
+
# end
|
22
|
+
#
|
2
23
|
class Token
|
3
|
-
UNITS = {days: {increment: 86400}, hours: {increment: 3600}, minutes: {increment: 60}} # :nodoc:
|
4
|
-
|
5
|
-
# The salt value
|
6
|
-
attr_reader :salt
|
7
24
|
# The expiry time as a Unix timestamp
|
8
25
|
attr_reader :expires
|
9
|
-
|
10
|
-
|
11
|
-
#
|
12
|
-
|
13
|
-
def initialize(salt=nil, expires=nil, config=nil)
|
14
|
-
@config = config || MortalToken.config
|
15
|
-
@salt = salt || self.config.salt
|
16
|
-
@expires = expires ? expires.to_i : calculate_expiry
|
17
|
-
end
|
18
|
-
|
19
|
-
# Returns the hash digest of the token
|
20
|
-
def digest
|
21
|
-
raise "MortalToken: you must set a secret!" if config.secret.nil?
|
22
|
-
@digest ||= OpenSSL::HMAC.digest(config._digest, config.secret, "#{expires.to_s}:#{salt}")
|
23
|
-
end
|
26
|
+
# String content of token (optional)
|
27
|
+
attr_reader :message
|
28
|
+
# The salt value
|
29
|
+
attr_reader :salt
|
24
30
|
|
25
|
-
#
|
26
|
-
def
|
27
|
-
|
31
|
+
# Initialize an existing token
|
32
|
+
def initialize(expires, salt, message = nil)
|
33
|
+
@expires = expires.to_i
|
34
|
+
@salt = salt
|
35
|
+
@message = message ? message.to_s : nil
|
28
36
|
end
|
29
37
|
|
30
|
-
# Returns
|
31
|
-
def
|
32
|
-
|
38
|
+
# Returns a URL-safe encoding of the token and its digest. Hand it out to users and check it with MoralToken.valid?
|
39
|
+
def to_s
|
40
|
+
h = to_h
|
41
|
+
h[:digest] = digest
|
42
|
+
Base64.urlsafe_encode64 h.to_json
|
33
43
|
end
|
34
44
|
|
35
|
-
# Returns
|
36
|
-
def
|
37
|
-
|
45
|
+
# Returns HMAC hexdigest of the token
|
46
|
+
def digest
|
47
|
+
raise "MortalToken: you must set a secret!" if MortalToken.secret.nil?
|
48
|
+
@digest ||= OpenSSL::HMAC.hexdigest(MortalToken.digest, MortalToken.secret, to_h.to_json)
|
38
49
|
end
|
39
50
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
# this token will be passed to the block.
|
44
|
-
def against(other_token_or_digest)
|
45
|
-
if self == other_token_or_digest
|
46
|
-
yield self if block_given?
|
47
|
-
true
|
48
|
-
else
|
49
|
-
false
|
50
|
-
end
|
51
|
+
# Number of seconds remaining
|
52
|
+
def ttl
|
53
|
+
expires - Time.now.utc.to_i
|
51
54
|
end
|
52
55
|
|
53
|
-
# Tests this token against another token or token hash. Even if it matches, returns false if
|
54
|
-
# the expire time is past.
|
56
|
+
# Tests this token against another token or token hash. Even if it matches, returns false if the expire time is past.
|
55
57
|
def ==(other_token_or_digest)
|
56
|
-
other = other_token_or_digest.
|
57
|
-
|
58
|
+
other = other_token_or_digest.respond_to?(:digest) ? other_token_or_digest.digest : other_token_or_digest
|
59
|
+
self.digest == other && self.ttl > 0
|
58
60
|
end
|
59
61
|
|
60
62
|
alias_method :===, :==
|
61
63
|
|
62
64
|
private
|
63
65
|
|
64
|
-
|
65
|
-
|
66
|
-
expire_time = Time.now.utc.to_i + ((config.valid_for) * UNITS[config.units][:increment])
|
67
|
-
expire_time.to_i
|
66
|
+
def to_h
|
67
|
+
{salt: salt, expires: expires, message: message}
|
68
68
|
end
|
69
69
|
end
|
70
70
|
end
|
data/lib/mortal-token/version.rb
CHANGED
data/lib/mortal-token.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'time'
|
2
|
+
require 'json'
|
2
3
|
require 'base64'
|
3
4
|
require 'openssl'
|
5
|
+
require 'securerandom'
|
4
6
|
|
7
|
+
require 'mortal-token/mortal-token'
|
5
8
|
require 'mortal-token/version'
|
9
|
+
require 'mortal-token/config'
|
6
10
|
require 'mortal-token/token'
|
7
|
-
require 'mortal-token/configuration'
|
8
|
-
require 'mortal-token/mortal-token'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mortal-token
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jordan Hollinger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: An wrapper library for generating self-contained, self-destructing tokens
|
14
14
|
with HMAC
|
@@ -17,15 +17,16 @@ executables: []
|
|
17
17
|
extensions: []
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
|
+
- LICENSE
|
21
|
+
- README.md
|
20
22
|
- lib/mortal-token.rb
|
23
|
+
- lib/mortal-token/config.rb
|
21
24
|
- lib/mortal-token/mortal-token.rb
|
22
25
|
- lib/mortal-token/token.rb
|
23
|
-
- lib/mortal-token/configuration.rb
|
24
26
|
- lib/mortal-token/version.rb
|
25
|
-
- README.rdoc
|
26
|
-
- LICENSE
|
27
27
|
homepage: http://github.com/jhollinger/mortal-token
|
28
|
-
licenses:
|
28
|
+
licenses:
|
29
|
+
- MIT
|
29
30
|
metadata: {}
|
30
31
|
post_install_message:
|
31
32
|
rdoc_options: []
|
@@ -33,17 +34,17 @@ require_paths:
|
|
33
34
|
- lib
|
34
35
|
required_ruby_version: !ruby/object:Gem::Requirement
|
35
36
|
requirements:
|
36
|
-
- -
|
37
|
+
- - ">="
|
37
38
|
- !ruby/object:Gem::Version
|
38
|
-
version:
|
39
|
+
version: 2.0.0
|
39
40
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
41
|
requirements:
|
41
|
-
- -
|
42
|
+
- - ">="
|
42
43
|
- !ruby/object:Gem::Version
|
43
44
|
version: '0'
|
44
45
|
requirements: []
|
45
46
|
rubyforge_project:
|
46
|
-
rubygems_version: 2.
|
47
|
+
rubygems_version: 2.4.5.1
|
47
48
|
signing_key:
|
48
49
|
specification_version: 4
|
49
50
|
summary: Generate self-destructing tokens
|
data/README.rdoc
DELETED
@@ -1,106 +0,0 @@
|
|
1
|
-
== MortalToken, because some tokens shouldn't live forever
|
2
|
-
|
3
|
-
MortalToken is a convenience wrapper for HMAC-based authentication. The tokens self-destruct after a specified time
|
4
|
-
period; no need to store and look them up for verification.
|
5
|
-
|
6
|
-
The default lifespan is one hour. For a Web-based application, you might extend this to many hours, or you
|
7
|
-
might cut it back to only a few minutes, issuing a new token for each request/response cycle.
|
8
|
-
|
9
|
-
My original use case was login auth tokens for some simple Sinatra apps. I can also see it being useful for API auth.
|
10
|
-
Of course there are potential uses outside of HTTP, too.
|
11
|
-
|
12
|
-
Steps:
|
13
|
-
|
14
|
-
1. Generate a new token
|
15
|
-
2. Give the client the resulting digest, salt, and expiry timestamp
|
16
|
-
3. Time passes
|
17
|
-
4. Receive a digest, salt, and expiry timestamp from client
|
18
|
-
5. Reconstitute the token from salt and timestamp, then see if the digests match. If not, the token has expired (or was forged).
|
19
|
-
|
20
|
-
== Warning
|
21
|
-
|
22
|
-
It is up to *you* to transmit the digest, salt, and expiry timestamp securely. If it's intercepted, someone could impersonate
|
23
|
-
your client until the token expires. In other words, this is only *part* of an authentication solution. Use with caution. May
|
24
|
-
contain traces of peanut.
|
25
|
-
|
26
|
-
== Install
|
27
|
-
|
28
|
-
[sudo] gem install mortal-token
|
29
|
-
|
30
|
-
Or add it to your Gemfile
|
31
|
-
gem "mortal-token"
|
32
|
-
|
33
|
-
== Example use with Sinatra
|
34
|
-
|
35
|
-
require 'sinatra'
|
36
|
-
require 'mortal-token'
|
37
|
-
|
38
|
-
MortalToken.secret = 'asdf092$78roasdjfjfaklmsdadASDFopijf98%2ejA#Df@sdf'
|
39
|
-
|
40
|
-
post '/login' do
|
41
|
-
if login_ok?
|
42
|
-
token = MortalToken.new
|
43
|
-
# Or "token = MortalToken.new(current_user.id)" to set your own salt and verify who owns the session.
|
44
|
-
session[:salt] = token.salt
|
45
|
-
session[:expires] = token.expires
|
46
|
-
session[:digest] = token.digest
|
47
|
-
redirect '/secret'
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
get '/secret' do
|
52
|
-
if MortalToken.check(session[:salt], session[:expires]).against(session[:digest])
|
53
|
-
'Nice token!'
|
54
|
-
else
|
55
|
-
'Your token is expired or forged!'
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
== Automatically re-issue nearly expired tokens
|
60
|
-
|
61
|
-
MortalToken.check(session[:salt], session[:expires]).against(session[:digest]) do |token|
|
62
|
-
session[:salt], session[:expires], session[:digest] = MortalToken.new.get if token.expires_soon?
|
63
|
-
end
|
64
|
-
|
65
|
-
== Checking token validity explained
|
66
|
-
|
67
|
-
MortalToken.check(salt, expires).against(digest)
|
68
|
-
|
69
|
-
is syntactic sugar for
|
70
|
-
|
71
|
-
reconstituted_token = MortalToken.new(salt, expires)
|
72
|
-
reconstituted_token == digest
|
73
|
-
|
74
|
-
A token's == and === methods accept another token or a digest. In the example above, an attempt has been made to reconstitute
|
75
|
-
the original token using it's salt and expiry timestamp. To be considered "equal", the reconstituted token's digest must
|
76
|
-
match the original digest AND the timestamp must be in the future. Unless both of those conditions are met, then token is
|
77
|
-
considered invalid or expired.
|
78
|
-
|
79
|
-
== Tweak token parameters
|
80
|
-
|
81
|
-
You may tweak certain parameters of the library in order to make it more secure, less, faster, etc.
|
82
|
-
These are the defaults (see the MortalToken class for documentation about each parameter):
|
83
|
-
|
84
|
-
MortalToken.valid_for = 1 # tokens are valid for N units
|
85
|
-
MortalToken.units = :hours # or :days, :minutes
|
86
|
-
MortalToken.digest = 'sha256' # The digest algorithm used by HMAC
|
87
|
-
MortalToken.max_salt_length = 50 # Maximum token salt length
|
88
|
-
MortalToken.min_salt_length = 10 # Minimum token salt length
|
89
|
-
|
90
|
-
== Multiple configurations
|
91
|
-
|
92
|
-
Your application may want to use MortalTokens in various contexts, where the same parameters may not make sense (probably units and valid_for).
|
93
|
-
You may define different scopes and give them each their own config. Always define the default scope first (above). Other scopes will inherit
|
94
|
-
its secret unless you specify another.
|
95
|
-
|
96
|
-
MortalToken.config(:foo) do |config|
|
97
|
-
config.units = :minutes
|
98
|
-
config.valid_for = 10
|
99
|
-
end
|
100
|
-
|
101
|
-
token = MortalToken.use(:foo).token
|
102
|
-
|
103
|
-
== License
|
104
|
-
Copyright 2012 Jordan Hollinger
|
105
|
-
|
106
|
-
Licensed under the Apache License
|
@@ -1,59 +0,0 @@
|
|
1
|
-
class MortalToken
|
2
|
-
# Holds a specific a configuration of parameters
|
3
|
-
class Configuration
|
4
|
-
RAND_SEEDS = [(0..9), ('a'..'z'), ('A'..'Z')].map(&:to_a).flatten # :nodoc;
|
5
|
-
|
6
|
-
# The configuration's name
|
7
|
-
attr_reader :name
|
8
|
-
# The master secret token (Keep it secret! Keep it safe!). Changing this will invalidate all existing tokens.
|
9
|
-
attr_accessor :secret
|
10
|
-
# The digest to use (passed to OpenSSL::Digest.new). Defaults to 'sha256'.
|
11
|
-
attr_accessor :digest
|
12
|
-
# The units that life is measured in - :days, :hours or :minutes (defaults to :hours)
|
13
|
-
attr_reader :units
|
14
|
-
# The number of units tokens will be valid across. Defaults to 1. Changing this will invalidate existing tokens.
|
15
|
-
attr_accessor :valid_for
|
16
|
-
# The maximum salt length, defaults to 50
|
17
|
-
attr_accessor :max_salt_length
|
18
|
-
# The minimum salt length, defaults to 10
|
19
|
-
attr_accessor :min_salt_length
|
20
|
-
|
21
|
-
# Instantiates a new Configuration object default values
|
22
|
-
def initialize(name)
|
23
|
-
@name = name
|
24
|
-
@digest = 'sha256'
|
25
|
-
@units = :hours
|
26
|
-
@valid_for = 1
|
27
|
-
@max_salt_length = 50
|
28
|
-
@min_salt_length = 10
|
29
|
-
@secret = CONFIGS[:default].secret if CONFIGS[:default]
|
30
|
-
end
|
31
|
-
|
32
|
-
# Return a new token using this configuration
|
33
|
-
def token(salt=nil, expires=nil)
|
34
|
-
Token.new(salt, expires, self)
|
35
|
-
end
|
36
|
-
|
37
|
-
# Returns a token reconstitued from the timestamp and salt
|
38
|
-
def check(salt, expires)
|
39
|
-
Token.new(salt, expires, self)
|
40
|
-
end
|
41
|
-
|
42
|
-
# Set the units that life is measured in - :days, :hours or :minutes
|
43
|
-
def units=(unit)
|
44
|
-
raise ArgumentError, "MortalToken.units must be one of #{Token::UNITS.keys.join(', ')}" unless Token::UNITS.keys.include? unit
|
45
|
-
@units = unit
|
46
|
-
end
|
47
|
-
|
48
|
-
# Returns a random string of between min_salt_length and max_salt_length alphanumeric charachters
|
49
|
-
def salt
|
50
|
-
max_length = [self.min_salt_length, rand(self.max_salt_length + 1)].max
|
51
|
-
pool_size = RAND_SEEDS.size
|
52
|
-
(0..max_length).map { RAND_SEEDS[rand(pool_size)] }.join('')
|
53
|
-
end
|
54
|
-
|
55
|
-
def _digest # :nodoc:
|
56
|
-
@_digest ||= OpenSSL::Digest.new(self.digest)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|