From c63791c7ffacb7c6cfc685e2654ffe66f0a6b185 Mon Sep 17 00:00:00 2001 From: elijah Date: Sun, 20 Mar 2016 01:13:24 -0700 Subject: api tokens: allow for special api tokens that work like session tokens but are configured in the static config, to be used for infrastructure monitoring. --- app/models/anonymous_user.rb | 4 +++ app/models/api_token.rb | 76 ++++++++++++++++++++++++++++++++++++++++++++ app/models/api_user.rb | 23 ++++++++++++++ app/models/user.rb | 4 +++ 4 files changed, 107 insertions(+) create mode 100644 app/models/api_token.rb create mode 100644 app/models/api_user.rb (limited to 'app/models') diff --git a/app/models/anonymous_user.rb b/app/models/anonymous_user.rb index 5745316..e1f7a0e 100644 --- a/app/models/anonymous_user.rb +++ b/app/models/anonymous_user.rb @@ -9,6 +9,10 @@ class AnonymousUser < Object false end + def is_test? + false + end + def id nil end diff --git a/app/models/api_token.rb b/app/models/api_token.rb new file mode 100644 index 0000000..49b1870 --- /dev/null +++ b/app/models/api_token.rb @@ -0,0 +1,76 @@ +# +# Works like a regular authentication Token, but is configured in the conf file for +# use by admins or testing. +# +# This is not actually a model, but it used in the place of the normal Token model +# when appropriate +# + +require 'digest/sha2' + +class ApiToken + + # + # Searches static config to see if there is a matching api token string. + # Return an ApiToken if successful, or nil otherwise. + # + def self.find_by_token(token) + if APP_CONFIG["api_tokens"].nil? || APP_CONFIG["api_tokens"].empty? + # no api auth tokens are configured + return nil + elsif !token.is_a?(String) || token.size < 24 + # don't allow obviously invalid token strings + return nil + else + token_digest = Digest::SHA512.hexdigest(token) + username = self.static_auth_tokens[token_digest] + if username + if username == "test" + return ApiTestToken.new + elsif username == "admin" + # not yet supported + return nil + end + else + return nil + end + end + end + + private + + # + # A static hash to represent the configured api auth tokens, in the form: + # + # { + # "" => "" + # } + # + # SHA512 is used here in order to prevent an attacker from discovering + # the value for an auth token by measuring the string comparison time. + # + def self.static_auth_tokens + @static_auth_tokens ||= APP_CONFIG["api_tokens"].inject({}) {|hsh, entry| + hsh[Digest::SHA512.hexdigest(entry[1])] = entry[0] + hsh + }.freeze + end + +end + +class ApiAdminToken < Token + # not yet supported + #def authenticate + # AdminUser.new + #end +end + +# +# These tokens used by the platform to run regular monitor tests +# of a production infrastructure. +# +class ApiTestToken < Token + def authenticate + ApiTestUser.new + end +end diff --git a/app/models/api_user.rb b/app/models/api_user.rb new file mode 100644 index 0000000..d80a4d1 --- /dev/null +++ b/app/models/api_user.rb @@ -0,0 +1,23 @@ + +class ApiUser < AnonymousUser +end + +# +# A user that has limited admin access, to be used +# for running monitor tests against a live production +# installation. +# +class ApiTestUser < ApiUser + def is_test? + true + end +end + +# +# Not yet supported: +# +#class ApiAdminUser < ApiUser +# def is_admin? +# true +# end +#end \ No newline at end of file diff --git a/app/models/user.rb b/app/models/user.rb index 61793be..1aeea1c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -107,6 +107,10 @@ class User < CouchRest::Model::Base false end + def is_test? + false + end + def most_recent_tickets(count=3) Ticket.for_user(self).limit(count).all #defaults to having most recent updated first end -- cgit v1.2.3 From 67b5aa4198e0f6ab2cd29767aedcb4bf5b5dc4d9 Mon Sep 17 00:00:00 2001 From: elijah Date: Mon, 28 Mar 2016 15:52:21 -0700 Subject: api tokens - clarify terms: "monitors" are admins that authenticated via api token, "tmp" users are users that exist only in tmp db, "test" users are either tmp users or users named "test_user_x" --- app/models/account.rb | 8 ++++---- app/models/anonymous_user.rb | 2 +- app/models/api_user.rb | 4 ++-- app/models/temporary_user.rb | 23 +++++++++++++++++++---- app/models/user.rb | 2 +- 5 files changed, 27 insertions(+), 12 deletions(-) (limited to 'app/models') diff --git a/app/models/account.rb b/app/models/account.rb index abde89d..a85e56c 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -21,7 +21,7 @@ class Account user = User.new(attrs) user.save - if !user.tmp? && user.persisted? + if !user.is_tmp? && user.persisted? identity = user.identity identity.user_id = user.id identity.save @@ -59,7 +59,7 @@ class Account def destroy(destroy_identity=false) return unless @user - if !@user.tmp? + if !@user.is_tmp? if destroy_identity == false @user.identities.each do |id| id.orphan! @@ -77,7 +77,7 @@ class Account # in place, but the user should not be able to send email or # create new authentication certificates. def disable - if @user && !@user.tmp? + if @user && !@user.is_tmp? @user.enabled = false @user.save @user.identities.each do |id| @@ -119,7 +119,7 @@ class Account def self.creation_problem?(user, identity) return true if user.nil? || !user.persisted? || user.errors.any? - if !user.tmp? + if !user.is_tmp? return true if identity.nil? || !identity.persisted? || identity.errors.any? end return false diff --git a/app/models/anonymous_user.rb b/app/models/anonymous_user.rb index e1f7a0e..79a5f32 100644 --- a/app/models/anonymous_user.rb +++ b/app/models/anonymous_user.rb @@ -9,7 +9,7 @@ class AnonymousUser < Object false end - def is_test? + def is_monitor? false end diff --git a/app/models/api_user.rb b/app/models/api_user.rb index d80a4d1..2efe1cb 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -7,8 +7,8 @@ end # for running monitor tests against a live production # installation. # -class ApiTestUser < ApiUser - def is_test? +class ApiMonitorUser < ApiUser + def is_monitor? true end end diff --git a/app/models/temporary_user.rb b/app/models/temporary_user.rb index 87800ed..2afae15 100644 --- a/app/models/temporary_user.rb +++ b/app/models/temporary_user.rb @@ -16,7 +16,8 @@ module TemporaryUser USER_DB = 'users' TMP_USER_DB = 'tmp_users' - TMP_LOGIN = 'test_user' + TMP_LOGIN = 'tmp_user' # created and deleted frequently + TEST_LOGIN = 'test_user' # created, rarely deleted included do use_database_method :db_name @@ -26,7 +27,7 @@ module TemporaryUser # override it. instance_eval <<-EOS, __FILE__, __LINE__ + 1 def find_by_login(*args) - if args.grep(/#{TMP_LOGIN}/).any? + if args.grep(/^#{TMP_LOGIN}/).any? by_login.database(tmp_database).key(*args).first() else by_login.key(*args).first() @@ -60,6 +61,14 @@ module TemporaryUser def create_tmp_database! design_doc.sync!(tmp_database.tap{|db|db.create!}) end + + def is_tmp?(login) + !login.nil? && login =~ /^#{TMP_LOGIN}/ + end + + def is_test?(login) + !login.nil? && (login =~ /^#{TMP_LOGIN}/ || login =~ /^#{TEST_LOGIN}/) + end end # @@ -71,8 +80,14 @@ module TemporaryUser end # returns true if this User instance is stored in tmp db. - def tmp? - !login.nil? && login.include?(TMP_LOGIN) + def is_tmp? + self.class.is_tmp?(self.login) + end + + # returns true if this user is used for testing purposes + # (either a temporary or long lived) + def is_test? + self.class.is_test?(self.login) end end diff --git a/app/models/user.rb b/app/models/user.rb index 1aeea1c..1c54f0c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -107,7 +107,7 @@ class User < CouchRest::Model::Base false end - def is_test? + def is_monitor? false end -- cgit v1.2.3 From e072ac2fa8bc93ed782df1ff95130f4794f9640f Mon Sep 17 00:00:00 2001 From: elijah Date: Mon, 28 Mar 2016 15:55:19 -0700 Subject: api: added allow ability to limit what IPs can access api using a static configured auth token. --- app/models/api_token.rb | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'app/models') diff --git a/app/models/api_token.rb b/app/models/api_token.rb index 49b1870..5c7923d 100644 --- a/app/models/api_token.rb +++ b/app/models/api_token.rb @@ -14,10 +14,12 @@ class ApiToken # Searches static config to see if there is a matching api token string. # Return an ApiToken if successful, or nil otherwise. # - def self.find_by_token(token) + def self.find_by_token(token, ip_address=nil) if APP_CONFIG["api_tokens"].nil? || APP_CONFIG["api_tokens"].empty? # no api auth tokens are configured return nil + elsif ip_address && !ip_allowed?(ip_address) + return nil elsif !token.is_a?(String) || token.size < 24 # don't allow obviously invalid token strings return nil @@ -25,8 +27,8 @@ class ApiToken token_digest = Digest::SHA512.hexdigest(token) username = self.static_auth_tokens[token_digest] if username - if username == "test" - return ApiTestToken.new + if username == "monitor" + return ApiMonitorToken.new elsif username == "admin" # not yet supported return nil @@ -51,14 +53,25 @@ class ApiToken # def self.static_auth_tokens @static_auth_tokens ||= APP_CONFIG["api_tokens"].inject({}) {|hsh, entry| - hsh[Digest::SHA512.hexdigest(entry[1])] = entry[0] + if ["monitor", "admin"].include?(entry[0]) + hsh[Digest::SHA512.hexdigest(entry[1])] = entry[0] + end hsh }.freeze end + def self.ip_allowed?(ip) + ip == "0.0.0.0" || + ip == "127.0.0.1" || ( + APP_CONFIG["api_tokens"] && + APP_CONFIG["api_tokens"]["allowed_ips"].is_a?(Array) && + APP_CONFIG["api_tokens"]["allowed_ips"].include?(ip) + ) + end + end -class ApiAdminToken < Token +class ApiAdminToken < ApiToken # not yet supported #def authenticate # AdminUser.new @@ -69,8 +82,8 @@ end # These tokens used by the platform to run regular monitor tests # of a production infrastructure. # -class ApiTestToken < Token +class ApiMonitorToken < ApiToken def authenticate - ApiTestUser.new + ApiMonitorUser.new end end -- cgit v1.2.3 From 9a8577a2d19aa51318dce6ff9ffe1bd26f25c09e Mon Sep 17 00:00:00 2001 From: elijah Date: Mon, 28 Mar 2016 15:56:21 -0700 Subject: api: added get(:show) to identities and users, allow monitors to create/delete test & tmp users. --- app/models/user.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'app/models') diff --git a/app/models/user.rb b/app/models/user.rb index 1c54f0c..51e9279 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -68,8 +68,10 @@ class User < CouchRest::Model::Base def to_json(options={}) { - :login => login, - :ok => valid? + :login => self.login, + :ok => self.valid?, + :id => self.id, + :enabled => self.enabled? }.to_json(options) end -- cgit v1.2.3