Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: enforce user creation security #131

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ GIT

GIT
remote: https://github.com/ncbo/ncbo_cron.git
revision: bb93561f78522cf6b289afc81b3bf86cdbbb8cfc
revision: 6317dc4976d2ab8e17104887bab0abf5f412b2ef
branch: develop
specs:
ncbo_cron (0.0.1)
Expand Down Expand Up @@ -110,7 +110,7 @@ GEM
ast (2.4.2)
backports (3.24.1)
base64 (0.2.0)
bcrypt (3.1.19)
bcrypt (3.1.20)
bcrypt_pbkdf (1.1.0)
bigdecimal (1.4.2)
builder (3.2.4)
Expand All @@ -133,10 +133,9 @@ GEM
dante (0.2.0)
date (3.3.4)
docile (1.4.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
domain_name (0.6.20231109)
ed25519 (1.3.0)
faraday (2.7.11)
faraday (2.7.12)
base64
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
Expand Down Expand Up @@ -166,10 +165,10 @@ GEM
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.3.1)
google-protobuf (3.25.0-aarch64-linux)
google-protobuf (3.25.0-arm64-darwin)
google-protobuf (3.25.0-x86_64-darwin)
google-protobuf (3.25.0-x86_64-linux)
google-protobuf (3.25.1-aarch64-linux)
google-protobuf (3.25.1-arm64-darwin)
google-protobuf (3.25.1-x86_64-darwin)
google-protobuf (3.25.1-x86_64-linux)
googleapis-common-protos (1.4.0)
google-protobuf (~> 3.14)
googleapis-common-protos-types (~> 1.2)
Expand Down Expand Up @@ -226,7 +225,7 @@ GEM
redis
multi_json (1.15.0)
net-http-persistent (2.9.4)
net-imap (0.4.4)
net-imap (0.4.6)
date
net-protocol
net-pop (0.1.2)
Expand Down Expand Up @@ -255,7 +254,7 @@ GEM
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (5.0.3)
public_suffix (5.0.4)
racc (1.7.3)
rack (1.6.13)
rack-accept (0.4.5)
Expand Down Expand Up @@ -344,7 +343,7 @@ GEM
rack-test
sinatra (~> 1.4.0)
tilt (>= 1.3, < 3)
sshkit (1.21.5)
sshkit (1.21.6)
net-scp (>= 1.1.2)
net-ssh (>= 2.8.0)
systemu (2.6.5)
Expand All @@ -353,9 +352,6 @@ GEM
timeout (0.4.1)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (2.5.0)
unicorn (6.1.0)
kgio (~> 2.6)
Expand All @@ -371,6 +367,7 @@ PLATFORMS
arm64-darwin-22
x86_64-darwin-18
x86_64-darwin-21
x86_64-darwin-23
x86_64-linux

DEPENDENCIES
Expand Down Expand Up @@ -423,4 +420,4 @@ DEPENDENCIES
unicorn-worker-killer

BUNDLED WITH
2.3.15
2.4.21
3 changes: 3 additions & 0 deletions controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class UsersController < ApplicationController
# Update an existing submission of an user
patch '/:username' do
user = User.find(params[:username]).include(User.attributes).first
params.delete("role") unless current_user.admin?
populate_from_params(user, params)
if user.valid?
user.save
Expand All @@ -92,6 +93,7 @@ class UsersController < ApplicationController

# Delete a user
delete '/:username' do
error 403, "Access denied" unless current_user.admin?
User.find(params[:username]).first.delete
halt 204
end
Expand All @@ -109,6 +111,7 @@ def create_user
params ||= @params
user = User.find(params["username"]).first
error 409, "User with username `#{params["username"]}` already exists" unless user.nil?
params.delete("role") unless current_user.admin?
user = instance_from_params(User, params)
if user.valid?
user.save
Expand Down
31 changes: 6 additions & 25 deletions test/controllers/test_slices_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def self.before_suite
password: "12345"
}).save
@@new_slice_data = { acronym: 'tst-c', name: "Test Slice C", ontologies: ont_acronyms}
@@old_security_setting = LinkedData.settings.enable_security
enable_security
end

def self.after_suite
Expand All @@ -26,7 +26,7 @@ def self.after_suite

def setup
self.class.reset_security
self.class.rest_to_not_admin(@@user)
self.class.reset_to_not_admin(@@user)
LinkedData::Models::Slice.find(@@new_slice_data[:acronym]).first&.delete
end

Expand All @@ -38,28 +38,28 @@ def test_all_slices
end

def test_create_slices
enable_security
self.class.enable_security

post "/slices?apikey=#{@@user.apikey}", MultiJson.dump(@@new_slice_data), "CONTENT_TYPE" => "application/json"
assert_equal 403, last_response.status

make_admin(@@user)
self.class.make_admin(@@user)

post "/slices?apikey=#{@@user.apikey}", MultiJson.dump(@@new_slice_data), "CONTENT_TYPE" => "application/json"

assert 201, last_response.status
end

def test_delete_slices
enable_security
self.class.enable_security
LinkedData.settings.enable_security = @@old_security_setting
self.class._create_slice(@@new_slice_data[:acronym], @@new_slice_data[:name], @@onts)


delete "/slices/#{@@new_slice_data[:acronym]}?apikey=#{@@user.apikey}"
assert_equal 403, last_response.status

make_admin(@@user)
self.class.make_admin(@@user)

delete "/slices/#{@@new_slice_data[:acronym]}?apikey=#{@@user.apikey}"
assert 201, last_response.status
Expand All @@ -76,23 +76,4 @@ def self._create_slice(acronym, name, ontologies)
slice.save
end

def enable_security
LinkedData.settings.enable_security = true
end

def self.reset_security
LinkedData.settings.enable_security = @@old_security_setting
end

def make_admin(user)
user.bring_remaining
user.role = [LinkedData::Models::Users::Role.find(LinkedData::Models::Users::Role::ADMIN).first]
user.save
end

def self.rest_to_not_admin(user)
user.bring_remaining
user.role = [LinkedData::Models::Users::Role.find(LinkedData::Models::Users::Role::DEFAULT).first]
user.save
end
end
62 changes: 57 additions & 5 deletions test/controllers/test_users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def self.before_suite
@@usernames = %w(fred goerge henry ben mark matt charlie)

# Create them again
@@usernames.each do |username|
@@users = @@usernames.map do |username|
User.new(username: username, email: "#{username}@example.org", password: "pass_word").save
end

Expand All @@ -21,6 +21,17 @@ def self._delete_users
end
end

def test_admin_creation
existent_user = @@users.first #no admin

refute _create_admin_user(apikey: existent_user.apikey), "A no admin user can't create an admin user or update it to an admin"

existent_user = self.class.make_admin(existent_user)
assert _create_admin_user(apikey: existent_user.apikey), "Admin can create an admin user or update it to be an admin"
self.class.reset_to_not_admin(existent_user)
_delete_user(@@username)
end

def test_all_users
get '/users'
assert last_response.ok?
Expand Down Expand Up @@ -48,7 +59,8 @@ def test_create_new_user
assert last_response.ok?
assert MultiJson.load(last_response.body)["username"].eql?(@@username)

delete created_user["@id"]
_delete_user(created_user["username"])

post "/users", MultiJson.dump(user.merge(username: @@username)), "CONTENT_TYPE" => "application/json"
assert last_response.status == 201
assert MultiJson.load(last_response.body)["username"].eql?(@@username)
Expand Down Expand Up @@ -79,13 +91,21 @@ def test_update_patch_user
end

def test_delete_user
delete "/users/ben"
assert last_response.status == 204
self.class.enable_security

delete "/users/ben?apikey=#{@@users.first.apikey}"
assert_equal 403, last_response.status

self.class.make_admin(@@users.first)
delete "/users/ben?apikey=#{@@users.first.apikey}"
assert_equal 204, last_response.status

@@usernames.delete("ben")
self.class.reset_security
self.class.reset_to_not_admin(@@users.first)

get "/users/ben"
assert last_response.status == 404
assert_equal 404, last_response.status
end

def test_user_not_found
Expand All @@ -100,4 +120,36 @@ def test_authentication
assert user["username"].eql?(@@usernames.first)
end


private

def _delete_user(username)
LinkedData::Models::User.find(@@username).first&.delete
end
def _create_admin_user(apikey: nil)
user = {email: "#{@@username}@example.org", password: "pass_the_word", role: ['ADMINISTRATOR']}
_delete_user(@@username)

put "/users/#{@@username}", MultiJson.dump(user), "CONTENT_TYPE" => "application/json", "Authorization" => "apikey token=#{apikey}"
assert last_response.status == 201
created_user = MultiJson.load(last_response.body)
assert created_user["username"].eql?(@@username)

get "/users/#{@@username}?apikey=#{apikey}"
assert last_response.ok?
user = MultiJson.load(last_response.body)
assert user["username"].eql?(@@username)

return true if user["role"].eql?(['ADMINISTRATOR'])

patch "/users/#{@@username}", MultiJson.dump(role: ['ADMINISTRATOR']), "CONTENT_TYPE" => "application/json", "Authorization" => "apikey token=#{apikey}"
assert last_response.status == 204

get "/users/#{@@username}?apikey=#{apikey}"
assert last_response.ok?
user = MultiJson.load(last_response.body)
assert user["username"].eql?(@@username)

true if user["role"].eql?(['ADMINISTRATOR'])
end
end
22 changes: 22 additions & 0 deletions test/test_case.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,26 @@ def get_errors(response)
return errors.strip
end

def self.enable_security
@@old_security_setting = LinkedData.settings.enable_security
LinkedData.settings.enable_security = true
end

def self.reset_security(old_security = @@old_security_setting)
LinkedData.settings.enable_security = old_security
end


def self.make_admin(user)
user.bring_remaining
user.role = [LinkedData::Models::Users::Role.find(LinkedData::Models::Users::Role::ADMIN).first]
user.save
end

def self.reset_to_not_admin(user)
user.bring_remaining
user.role = [LinkedData::Models::Users::Role.find(LinkedData::Models::Users::Role::DEFAULT).first]
user.save
end

end