Skip to content

Commit

Permalink
add password reset token expiration
Browse files Browse the repository at this point in the history
  • Loading branch information
alexskr committed Feb 6, 2024
1 parent 955268b commit e78397b
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 25 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ gem 'goo', github: 'ncbo/goo', branch: 'develop'
gem 'ncbo_annotator', github: 'ncbo/ncbo_annotator', branch: 'develop'
gem 'ncbo_cron', github: 'ncbo/ncbo_cron', branch: 'develop'
gem 'ncbo_ontology_recommender', github: 'ncbo/ncbo_ontology_recommender', branch: 'develop'
gem 'ontologies_linked_data', github: 'ncbo/ontologies_linked_data', branch: 'develop'
gem 'ontologies_linked_data', github: 'ncbo/ontologies_linked_data', branch: 'expire_reset_passwd_tokens'
gem 'sparql-client', github: 'ncbo/sparql-client', branch: 'develop'

group :development do
Expand Down
40 changes: 20 additions & 20 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
GIT
remote: https://github.com/ncbo/goo.git
revision: db2b330fb6c5fd4ea9ee17d5b58ca997f304a340
revision: 7a897a8e9c01d3a412d9011df8e26d770d58cd7d
branch: develop
specs:
goo (0.0.2)
Expand All @@ -15,7 +15,7 @@ GIT

GIT
remote: https://github.com/ncbo/ncbo_annotator.git
revision: 9a037b955b1d6c3c9955250d7499afdd10d4bbd3
revision: f60341e8426adcad447a6fc3adb5b80207c5987a
branch: develop
specs:
ncbo_annotator (0.0.1)
Expand All @@ -26,7 +26,7 @@ GIT

GIT
remote: https://github.com/ncbo/ncbo_cron.git
revision: a733888915a6c188e636ff605027490c381d72a0
revision: 8e3d46fccdefef0415cb710bcedc9d65c72f9bd2
branch: develop
specs:
ncbo_cron (0.0.1)
Expand All @@ -53,8 +53,8 @@ GIT

GIT
remote: https://github.com/ncbo/ontologies_linked_data.git
revision: 4b6b7f42882b9ad815ff5e90c354212aca085fda
branch: develop
revision: 073f79d9d1b5fe9dda4e77c52a60fdc1bd898558
branch: expire_reset_passwd_tokens
specs:
ontologies_linked_data (0.0.1)
activesupport
Expand Down Expand Up @@ -127,7 +127,7 @@ GEM
capistrano (~> 3.1)
sshkit (~> 1.3)
coderay (1.1.3)
concurrent-ruby (1.2.2)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
cube-ruby (0.0.3)
dante (0.2.0)
Expand All @@ -153,16 +153,16 @@ GEM
grpc (~> 1.59)
get_process_mem (0.2.7)
ffi (~> 1.0)
google-analytics-data (0.4.0)
google-analytics-data-v1beta (>= 0.7, < 2.a)
google-analytics-data (0.5.0)
google-analytics-data-v1beta (>= 0.11, < 2.a)
google-cloud-core (~> 1.6)
google-analytics-data-v1beta (0.11.1)
google-analytics-data-v1beta (0.11.2)
gapic-common (>= 0.21.1, < 2.a)
google-cloud-errors (~> 1.0)
google-cloud-core (1.6.1)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (2.1.0)
google-cloud-env (2.1.1)
faraday (>= 1.0, < 3.a)
google-cloud-errors (1.3.1)
google-protobuf (3.25.2-aarch64-linux)
Expand All @@ -175,23 +175,23 @@ GEM
grpc (~> 1.27)
googleapis-common-protos-types (1.11.0)
google-protobuf (~> 3.18)
googleauth (1.9.1)
googleauth (1.9.2)
faraday (>= 1.0, < 3.a)
google-cloud-env (~> 2.1)
jwt (>= 1.4, < 3.0)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
grpc (1.60.0-aarch64-linux)
grpc (1.61.0)
google-protobuf (~> 3.25)
googleapis-common-protos-types (~> 1.0)
grpc (1.60.0-arm64-darwin)
grpc (1.61.0-arm64-darwin)
google-protobuf (~> 3.25)
googleapis-common-protos-types (~> 1.0)
grpc (1.60.0-x86_64-darwin)
grpc (1.61.0-x86_64-darwin)
google-protobuf (~> 3.25)
googleapis-common-protos-types (~> 1.0)
grpc (1.60.0-x86_64-linux)
grpc (1.61.0-x86_64-linux)
google-protobuf (~> 3.25)
googleapis-common-protos-types (~> 1.0)
haml (5.2.2)
Expand Down Expand Up @@ -230,7 +230,7 @@ GEM
multi_json (1.15.0)
mutex_m (0.2.0)
net-http-persistent (2.9.4)
net-imap (0.4.9.1)
net-imap (0.4.10)
date
net-protocol
net-pop (0.1.2)
Expand All @@ -245,14 +245,14 @@ GEM
net-protocol
net-ssh (7.2.1)
netrc (0.11.0)
newrelic_rpm (9.7.0)
newrelic_rpm (9.7.1)
oj (3.16.1)
omni_logger (0.1.4)
logger
os (1.1.4)
parallel (1.24.0)
parseconfig (1.1.2)
parser (3.3.0.3)
parser (3.3.0.5)
ast (~> 2.4.1)
racc
pony (1.13.1)
Expand Down Expand Up @@ -303,11 +303,11 @@ GEM
rsolr (2.5.0)
builder (>= 2.1.2)
faraday (>= 0.9, < 3, != 2.0.0)
rubocop (1.59.0)
rubocop (1.60.2)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.2.2.4)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
Expand Down
10 changes: 8 additions & 2 deletions controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class UsersController < ApplicationController
end

##
# This endpoint will create a token and store it on the user
# This endpoint will create a token and store it on the use-
# An email is generated with this token, which allows the user
# to click and login to the UI. The token can then be provided to
# the /reset_password endpoint to actually reset the password.
Expand All @@ -24,6 +24,7 @@ class UsersController < ApplicationController
error 404, "User not found" unless user
reset_token = token(36)
user.resetToken = reset_token
user.resetTokenExpireTime = Time.now.to_i + 1.hours.to_i
if user.valid?
user.save(override_security: true)
LinkedData::Utils::Notifications.reset_password(user, reset_token)
Expand All @@ -46,10 +47,15 @@ class UsersController < ApplicationController
user = LinkedData::Models::User.where(email: email, username: username).include(User.goo_attrs_to_load(includes_param)).first
error 404, "User not found" unless user
if token.eql?(user.resetToken)
error 401, "Invalid password reset token" if user.resetTokenExpireTime.nil?
error 401, "The password reset token expired" if user.resetTokenExpireTime < Time.now.to_i
user.resetToken = nil
user.resetTokenExpireTime = nil
user.save(override_security: true) if user.valid?
user.show_apikey = true
reply user
else
error 403, "Password reset not authorized with this token"
error 401, "Password reset not authorized with this token"
end
end

Expand Down
52 changes: 50 additions & 2 deletions test/controllers/test_users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,58 @@ def test_create_new_user

get "/users/#{@@username}"
assert last_response.ok?
assert MultiJson.load(last_response.body)["username"].eql?(@@username)
assert MultiJson.load(last_response.body)["username"].eql?(@@username)
assert_equal "[email protected]", MultiJson.load(last_response.body)["email"]
end

def test_reset_password
username = 'resetpswd'
user = {email: "#{username}@example.org", password: "resetme"}
put "/users/#{username}", MultiJson.dump(user), "CONTENT_TYPE" => "application/json"
assert last_response.status == 201
user = User.find(username).include(User.attributes).first
assert_nil user.resetToken
post "/users/create_reset_password_token", {username: username, email: "[email protected]"}
user = User.find(username).include(User.attributes).first
assert_nil user.resetToken
assert_equal 404, last_response.status
post "/users/reset_password", {username: 'badusername', email: "#{username}@example.org", token: 'badtoken'}
post "/users/create_reset_password_token", {username: username, email: "#{username}@example.org"}
assert_equal 204, last_response.status
user = User.find(username).include(User.attributes).first
refute_nil user.resetToken
post "/users/reset_password", {username: username, email: "#{username}@example.org", token: 'badtoken'}
assert_equal 401, last_response.status
post "/users/reset_password", {username: 'badusername', email: "#{username}@example.org", token: 'badtoken'}
assert_equal 404, last_response.status
post "/users/reset_password", {username: username, token: user.resetToken}
assert_equal 404, last_response.status
post "/users/reset_password", {email: "#{username}@example.org", token: user.resetToken}
assert_equal 404, last_response.status
post "/users/reset_password", {username: username, email: "[email protected]", token: user.resetToken}
assert_equal 404, last_response.status
post "/users/reset_password", {username: username, email: "#{username}@example.org", token: user.resetToken}
assert_equal 200, last_response.status
assert_equal "#{username}@example.org", MultiJson.load(last_response.body)["email"]
user = User.find(username).include(User.attributes).first
assert_nil user.resetToken
end

def test_reset_password_expired_token
username = 'resetexpired'
user = {email: "#{username}@example.org", password: "resetme"}
put "/users/#{username}", MultiJson.dump(user), "CONTENT_TYPE" => "application/json"
assert last_response.status == 201
post "/users/create_reset_password_token", {username: username, email: "#{username}@example.org"}
assert_equal 204, last_response.status
user = User.find(username).include(User.attributes).first
user.resetTokenExpireTime = Time.now.to_i - 2.hours.to_i
user.save
post "/users/reset_password", {username: username, email: "#{username}@example.org", token: user.resetToken}
assert_equal 401, last_response.status
end


def test_create_new_invalid_user
put "/users/totally_new_user"
assert last_response.status == 422
Expand Down Expand Up @@ -120,7 +169,6 @@ def test_authentication
assert user["username"].eql?(@@usernames.first)
end


private

def _delete_user(username)
Expand Down

0 comments on commit e78397b

Please sign in to comment.