From 59587754bcb83e39a9a10a4a5ecda300eb77044c Mon Sep 17 00:00:00 2001 From: Alberto Bellotti Date: Mon, 6 Nov 2017 10:17:05 -0500 Subject: [PATCH] API Enhancement making the system token a one-time token. - X_MIQ_TOKEN can now only be used once. - Added spec test verifying the OTP check and unauthorized error. --- .../api/base_controller/authentication.rb | 13 ++++++++++++- spec/requests/authentication_spec.rb | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/base_controller/authentication.rb b/app/controllers/api/base_controller/authentication.rb index a9eb205a52..f56d8cfb5d 100644 --- a/app/controllers/api/base_controller/authentication.rb +++ b/app/controllers/api/base_controller/authentication.rb @@ -1,6 +1,8 @@ module Api class BaseController module Authentication + SYSTEM_TOKEN_ALLOWED_TIME_SKEW = 5.minutes + # # REST APIs Authenticator and Redirector # @@ -78,6 +80,8 @@ def authenticate_with_user_token(auth_token) end def authenticate_with_system_token(x_miq_token) + validate_system_token_otp(x_miq_token) + @miq_token_hash = YAML.load(MiqPassword.decrypt(x_miq_token)) validate_system_token_server(@miq_token_hash[:server_guid]) @@ -102,7 +106,14 @@ def validate_system_token_server(server_guid) def validate_system_token_timestamp(timestamp) raise "Missing timestamp" if timestamp.blank? - raise "Invalid timestamp #{timestamp} specified" if 5.minutes.ago.utc > timestamp + raise "Invalid timestamp #{timestamp} specified" if SYSTEM_TOKEN_ALLOWED_TIME_SKEW.ago.utc > timestamp + end + + def validate_system_token_otp(x_miq_token) + token_store = TokenStore.acquire("api_system_token_otp", SYSTEM_TOKEN_ALLOWED_TIME_SKEW) + token_used_timestamp = token_store.read(x_miq_token) + raise "System Token was already used at #{token_used_timestamp}" if token_used_timestamp + token_store.write(x_miq_token, Time.now.getlocal) end end end diff --git a/spec/requests/authentication_spec.rb b/spec/requests/authentication_spec.rb index 93b77a09dc..71467fa24d 100644 --- a/spec/requests/authentication_spec.rb +++ b/spec/requests/authentication_spec.rb @@ -331,6 +331,22 @@ def systoken(server_guid, userid, timestamp) expect(response).to have_http_status(:ok) expect_result_to_have_keys(ENTRYPOINT_KEYS) end + + it "authentication using a valid token succeeds only once" do + miq_token = systoken(MiqServer.first.guid, @user.userid, Time.now.utc) + + get api_entrypoint_url, :headers => {Api::HttpHeaders::MIQ_TOKEN => miq_token} + + expect(response).to have_http_status(:ok) + expect_result_to_have_keys(ENTRYPOINT_KEYS) + + get api_entrypoint_url, :headers => {Api::HttpHeaders::MIQ_TOKEN => miq_token} + + expect(response).to have_http_status(:unauthorized) + expect(response.parsed_body).to include( + "error" => a_hash_including("kind" => "unauthorized", "message" => AUTHENTICATION_ERROR) + ) + end end context "Role Based Authorization" do