Class: OpenC3::AuthModel
Constant Summary collapse
- ARGON2_PROFILE =
ENV["OPENC3_ARGON2_PROFILE"]&.to_sym || :rfc_9106_low_memory
- PRIMARY_KEY =
Redis keys
'OPENC3__TOKEN'- SESSIONS_KEY =
for argon2 password hash
'OPENC3__SESSIONS'- PW_HASH_CACHE_TIMEOUT =
The length of time in minutes to keep redis values in memory
5- SESSION_CACHE_TIMEOUT =
5- MIN_PASSWORD_LENGTH =
8- SESSION_PREFIX =
"ses_"- OTP_PREFIX =
"otp_"- @@pw_hash_cache =
Cached argon2 password hash
nil- @@pw_hash_cache_time =
nil- @@session_cache =
Cached session tokens
nil- @@session_cache_time =
nil
Class Method Summary collapse
-
.generate_session(otp: false) ⇒ String
Creates a new session token.
-
.logout ⇒ Object
Terminates every session.
- .set(password, old_password, key = PRIMARY_KEY) ⇒ Object
- .set?(key = PRIMARY_KEY) ⇒ Boolean
-
.terminate(token) ⇒ Object
Terminates the given session token.
-
.terminate_otp(token) ⇒ Object
Terminates the session if the token is an OTP.
-
.verify(token, no_password: true, service_only: false) ⇒ Boolean
Checks whether the provided token is a valid user password, service password, or session token.
-
.verify_no_service(token, mode: :token) ⇒ Boolean
Checks whether the provided token is a valid user password or session token.
Class Method Details
.generate_session(otp: false) ⇒ String
Creates a new session token. DO NOT CALL BEFORE VERIFYING.
127 128 129 130 131 132 133 134 135 136 |
# File 'lib/openc3/models/auth_model.rb', line 127 def self.generate_session(otp: false) token = SecureRandom.urlsafe_base64(nil, false) if otp token = OTP_PREFIX + token else token = SESSION_PREFIX + token end Store.hset(SESSIONS_KEY, token, Time.now.iso8601) return token end |
.logout ⇒ Object
Terminates every session.
139 140 141 142 143 |
# File 'lib/openc3/models/auth_model.rb', line 139 def self.logout Store.del(SESSIONS_KEY) @@session_cache = nil @@session_cache_time = nil end |
.set(password, old_password, key = PRIMARY_KEY) ⇒ Object
109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/openc3/models/auth_model.rb', line 109 def self.set(password, old_password, key = PRIMARY_KEY) raise "password must not be nil or empty" if password.nil? or password.empty? raise "password must be at least 8 characters" if password.length < MIN_PASSWORD_LENGTH if set?(key) raise "old_password must not be nil or empty" if old_password.nil? or old_password.empty? raise "old_password incorrect" unless verify_no_service(old_password, mode: :password) end pw_hash = Argon2::Password.create(password, profile: ARGON2_PROFILE) Store.set(key, pw_hash) @@pw_hash_cache = nil @@pw_hash_cache_time = nil logout end |
.set?(key = PRIMARY_KEY) ⇒ Boolean
47 48 49 |
# File 'lib/openc3/models/auth_model.rb', line 47 def self.set?(key = PRIMARY_KEY) Store.exists(key) == 1 end |
.terminate(token) ⇒ Object
Terminates the given session token.
146 147 148 149 |
# File 'lib/openc3/models/auth_model.rb', line 146 def self.terminate(token) Store.hdel(SESSIONS_KEY, token) @@session_cache.delete(token) if @@session_cache end |
.terminate_otp(token) ⇒ Object
Terminates the session if the token is an OTP.
152 153 154 |
# File 'lib/openc3/models/auth_model.rb', line 152 def self.terminate_otp(token) terminate(token) if token.start_with?(OTP_PREFIX) end |
.verify(token, no_password: true, service_only: false) ⇒ Boolean
Checks whether the provided token is a valid user password, service password, or session token.
56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/openc3/models/auth_model.rb', line 56 def self.verify(token, no_password: true, service_only: false) # Handle a service password - Generally only used by ScriptRunner # TODO: Replace this with temporary service tokens service_password = ENV['OPENC3_SERVICE_PASSWORD'] return true if service_password and service_password == token return false if service_only mode = no_password ? :token : :any return verify_no_service(token, mode: mode) end |
.verify_no_service(token, mode: :token) ⇒ Boolean
Checks whether the provided token is a valid user password or session token.
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/openc3/models/auth_model.rb', line 72 def self.verify_no_service(token, mode: :token) modes = [:password, :token, :any] raise ArgumentError, "Invalid mode '#{mode}': must be one of #{modes}" unless modes.include?(mode) return false if token.nil? or token.empty? # Check cached session tokens and password hash time = Time.now unless mode == :password if @@session_cache and (time - @@session_cache_time) < SESSION_CACHE_TIMEOUT and @@session_cache[token] terminate_otp(token) return true end # Check stored session tokens @@session_cache = Store.hgetall(SESSIONS_KEY) @@session_cache_time = time if @@session_cache[token] terminate_otp(token) return true end end unless mode == :token return true if @@pw_hash_cache and (time - @@pw_hash_cache_time) < PW_HASH_CACHE_TIMEOUT and Argon2::Password.verify_password(token, @@pw_hash_cache) # Check stored password hash pw_hash = Store.get(PRIMARY_KEY) raise "invalid password hash" if pw_hash.nil? || !pw_hash.start_with?("$argon2") # Catch users who didn't run the migration utility when upgrading to COSMOS 7 @@pw_hash_cache = pw_hash @@pw_hash_cache_time = time return true if Argon2::Password.verify_password(token, @@pw_hash_cache) end return false end |