diff options
| -rw-r--r-- | app/controllers/controller_extension/token_authentication.rb | 2 | ||||
| -rw-r--r-- | app/models/anonymous_user.rb | 4 | ||||
| -rw-r--r-- | app/models/api_token.rb | 76 | ||||
| -rw-r--r-- | app/models/api_user.rb | 23 | ||||
| -rw-r--r-- | app/models/user.rb | 4 | ||||
| -rw-r--r-- | config/defaults.yml | 9 | ||||
| -rw-r--r-- | test/integration/api/token_test.rb | 3 | ||||
| -rw-r--r-- | test/unit/api_token_test.rb | 28 | 
8 files changed, 147 insertions, 2 deletions
| diff --git a/app/controllers/controller_extension/token_authentication.rb b/app/controllers/controller_extension/token_authentication.rb index 4ad1977..15ddef7 100644 --- a/app/controllers/controller_extension/token_authentication.rb +++ b/app/controllers/controller_extension/token_authentication.rb @@ -5,7 +5,7 @@ module ControllerExtension::TokenAuthentication    def token      @token ||= authenticate_with_http_token do |token, options| -      Token.find_by_token(token) +      Token.find_by_token(token) || ApiToken.find_by_token(token)      end    end 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: +  # +  # { +  #    "<sha521 of token>" => "<username>" +  # } +  # +  # 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 diff --git a/config/defaults.yml b/config/defaults.yml index 906b446..84ee4c9 100644 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -117,6 +117,9 @@ development:    <<: *common    <<: *service_levels    admins: [blue, red, staff] +  api_tokens: +    test: nil +    admin: nil    domain: example.org    secret_token: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'    reraise_errors: true @@ -128,6 +131,9 @@ test:    <<: *common    <<: *service_levels    admins: [admin, admin2] +  api_tokens: +    test: "212da28a59dcaca487365309dc93aa09" +    admin: nil    domain: test.me    secret_token: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'    reraise_errors: true @@ -142,6 +148,9 @@ production:    <<: *cert_options    <<: *common    admins: [] +  api_tokens: +    test: nil +    admin: nil    domain: example.net    engines:      - support diff --git a/test/integration/api/token_test.rb b/test/integration/api/token_test.rb index ad3ac22..dafbfb7 100644 --- a/test/integration/api/token_test.rb +++ b/test/integration/api/token_test.rb @@ -1,4 +1,4 @@ -require 'test_helper' +require_relative '../../test_helper'  require_relative 'srp_test'  class TokenTest < SrpTest @@ -12,4 +12,5 @@ class TokenTest < SrpTest      token = server_auth['token']      assert Token.find(Digest::SHA512.hexdigest(token))    end +  end diff --git a/test/unit/api_token_test.rb b/test/unit/api_token_test.rb new file mode 100644 index 0000000..55d7507 --- /dev/null +++ b/test/unit/api_token_test.rb @@ -0,0 +1,28 @@ +require_relative '../test_helper' + +class ApiTokenTest < ActiveSupport::TestCase + +  setup do +  end + +  test "api token only authenticates ApiUser" do +    token_string = APP_CONFIG['api_tokens']['test'] +    assert !token_string.nil? +    assert !token_string.empty? +    token = ApiToken.find_by_token(token_string) +    user = token.authenticate +    assert user, 'api token should authenticate' +    assert user.is_a?(ApiUser), 'api token should return api user' +    assert user.is_test?, 'api test token should return test user' +    assert !user.is_admin?, 'api test token should not return admin user' +  end + +  test "invalid api tokens can't authenticate" do +    assert_nil ApiToken.find_by_token("not a token") +    with_config({"api_tokens" => {"test" => ""}}) do +      assert_equal "", APP_CONFIG['api_tokens']['test'] +      assert_nil ApiToken.find_by_token("") +    end +  end + +end
\ No newline at end of file | 
