diff --git a/gem/lib/cli/common.rb b/gem/lib/cli/common.rb index ba0bcf068fb..f5f16a1ac1f 100644 --- a/gem/lib/cli/common.rb +++ b/gem/lib/cli/common.rb @@ -78,7 +78,7 @@ class MiscCli < CommonCli end define_option :force, "--[no-]force", "-f", "set context even if target UAA is not available" - desc "target [uaa_url]", "Display current or set new target" do |uaa_url| + desc "target [uaa_url] [token_url]", "Display current or set new target" do |uaa_url, token_url| msg = nil if uaa_url if uaa_url.to_i.to_s == uaa_url @@ -95,9 +95,27 @@ class MiscCli < CommonCli end Config.target = url # we now have a canonical url set to https if possible end + if token_url + if token_url.to_i.to_s == token_url + return say "invalid token target index" unless url = Config.token_target?(uaa_url.to_i) + elsif url = normalize_url(token_url) + return say msg if (msg = bad_uaa_url(url)) unless opts[:force] || Config.token_target?(url) + elsif !Config.token_target?(url = normalize_url(token_url, "https")) && + !Config.token_target?(url = normalize_url(token_url, "http")) + if opts[:force] + url = normalize_url(token_url, "https") + elsif bad_uaa_url(url = normalize_url(token_url, "https")) + return say msg if msg = bad_uaa_url(url = normalize_url(token_url, "http")) + end + end + Config.token_target = url # we now have a canonical url set to https if possible + else + Config.token_target = nil + end return say "no target set" unless Config.target - return say "target set to #{Config.target}" unless Config.context - say "target set to #{Config.target}, with context #{Config.context}" + token_target_msg = Config.token_target ? "\ntoken target set to #{Config.token_target}" : "" + return say "target set to #{Config.target}#{token_target_msg}" unless Config.context + say "target set to #{Config.target}, with context #{Config.context}#{token_target_msg}" end desc "targets", "Display all targets" do @@ -113,6 +131,9 @@ def config_pp(tgt = nil, ctx = nil) say "" splat = v[:current] ? '*' : ' ' pp "[#{i}]#{splat}[#{k}]" + if v[:token_target] + pp "token_target: #{v[:token_target]}", 4 + end next unless v[:contexts] v[:contexts].each_with_index do |(sk, sv), si| next if ctx && ctx != sk diff --git a/gem/lib/cli/config.rb b/gem/lib/cli/config.rb index ae114ff6632..86eb790ab1e 100644 --- a/gem/lib/cli/config.rb +++ b/gem/lib/cli/config.rb @@ -19,12 +19,13 @@ module CF::UAA class Config class << self - attr_reader :target, :context + attr_reader :target, :context, :token_target end def self.config; @config ? @config.dup : {} end def self.yaml; YAML.dump(Util.hash_keys(@config, :tostr)) end def self.target?(tgt); tgt if @config[tgt = subhash_key(@config, tgt)] end + def self.token_target?(tgt); tgt if @config[tgt = subhash_key(@config[@target][:token_target], tgt)] end # if a yaml string is provided, config is loaded from the string, otherwise # config is assumed to be a file name to read and store config. @@ -47,7 +48,10 @@ def self.load(config = nil) } end @config = Util.hash_keys(@config, :tosym) - @context = current_subhash(@config[@target][:contexts]) if @target = current_subhash(@config) + if @target = current_subhash(@config) + @token_target = @config[@target][:token_target] + @context = current_subhash(@config[@target][:contexts]) + end end def self.save @@ -64,6 +68,14 @@ def self.target=(tgt) @target = t end + def self.token_target=(tgt) + if tgt + @config[@target][:token_target] = tgt.to_s + save + @token_target = tgt + end + end + def self.context=(ctx) raise ArgumentError, "target not set" unless @target unless c = set_current_subhash(@config[@target][:contexts] ||= {}, ctx, @context) diff --git a/gem/lib/cli/token.rb b/gem/lib/cli/token.rb index a57e90f4027..18a83a23e71 100644 --- a/gem/lib/cli/token.rb +++ b/gem/lib/cli/token.rb @@ -23,7 +23,7 @@ class TokenCatcher < Stub::Base def process_grant(data) server.logger.debug "processing grant for path #{request.path}" secret = server.info.delete(:client_secret) - ti = TokenIssuer.new(Config.target, server.info.delete(:client_id), secret) + ti = TokenIssuer.new(Config.target, server.info.delete(:client_id), secret, nil, Config.token_target) tkn = secret ? ti.authcode_grant(server.info.delete(:uri), data) : ti.implicit_grant(server.info.delete(:uri), data) server.info.update(tkn.info) @@ -200,7 +200,7 @@ def say_success(grant) end def issuer_request(client_id, secret = nil) - return yield TokenIssuer.new(Config.target.to_s, client_id, secret) + return yield TokenIssuer.new(Config.target.to_s, client_id, secret, nil, Config.token_target.to_s) rescue TargetError => e say "\n#{e.message}:\n#{JSON.pretty_generate(e.info)}" rescue Exception => e diff --git a/gem/lib/uaa/client_reg.rb b/gem/lib/uaa/client_reg.rb index e3af69f511f..8c7f0668de6 100644 --- a/gem/lib/uaa/client_reg.rb +++ b/gem/lib/uaa/client_reg.rb @@ -52,24 +52,24 @@ def create(info) info = Util.hash_keys(info) raise ArgumentError, "a client registration must specify a unique client id" unless info[:client_id] info = self.class.multivalues_to_arrays! info - json_parse_reply *json_post("/oauth/clients", info, @auth_header) + json_parse_reply *json_post(@target, "/oauth/clients", info, @auth_header) end def update(info) info = Util.hash_keys(info) raise ArgumentError, "a client registration update must specify a unique client id" unless info[:client_id] info = self.class.multivalues_to_arrays! info - json_parse_reply *json_put("/oauth/clients/#{URI.encode(info[:client_id])}", info, @auth_header) + json_parse_reply *json_put(@target, "/oauth/clients/#{URI.encode(info[:client_id])}", info, @auth_header) end - def get(id); json_get "/oauth/clients/#{URI.encode(id)}", @auth_header end - def delete(id); http_delete "/oauth/clients/#{URI.encode(id)}", @auth_header end - def list; json_get "/oauth/clients", @auth_header end + def get(id); json_get @target, "/oauth/clients/#{URI.encode(id)}", @auth_header end + def delete(id); http_delete @target, "/oauth/clients/#{URI.encode(id)}", @auth_header end + def list; json_get @target, "/oauth/clients", @auth_header end def change_secret(client_id, new_secret, old_secret = nil) req = { secret: new_secret } req[:oldSecret] = old_secret if old_secret - json_parse_reply(*json_put("/oauth/clients/#{URI.encode(client_id)}/secret", req, @auth_header)) + json_parse_reply(*json_put(@target, "/oauth/clients/#{URI.encode(client_id)}/secret", req, @auth_header)) end end diff --git a/gem/lib/uaa/http.rb b/gem/lib/uaa/http.rb index f12a3d53e08..525083f604a 100644 --- a/gem/lib/uaa/http.rb +++ b/gem/lib/uaa/http.rb @@ -36,7 +36,6 @@ def initialize(error_info = {}) module Http attr_accessor :proxy, :async - attr_reader :target def logger=(logr); @logger = logr end def logger ; @logger ||= Util.default_logger end def trace? ; @logger && @logger.respond_to?(:trace?) && @logger.trace? end @@ -47,71 +46,69 @@ def self.basic_auth(name, password) private - def json_get(url, authorization = nil) - json_parse_reply(*http_get(url, 'application/json', authorization)) + def json_get(target, url, authorization = nil) + json_parse_reply(*http_get(target, url, 'application/json', authorization)) end def json_parse_reply(status, body, headers) unless [200, 201, 204, 400, 401, 403].include? status - raise (status == 404 ? NotFound : BadResponse), "invalid status response from #{@target}: #{status}" + raise (status == 404 ? NotFound : BadResponse), "invalid status response: #{status}" end if body && !body.empty? && headers && headers[:content_type] !~ /application\/json/i - raise BadResponse, "received invalid response content type from #{@target}" + raise BadResponse, "received invalid response content type" end parsed_reply = Util.json_parse(body) if status >= 400 raise parsed_reply[:error] == "invalid_token"? InvalidToken : - TargetError.new(parsed_reply), "error response from #{@target}" + TargetError.new(parsed_reply), "error response" end parsed_reply rescue JSON::ParserError - raise BadResponse, "invalid JSON response from #{@target}" + raise BadResponse, "invalid JSON response" end - def json_post(url, body, authorization) - http_post(url, body.to_json, "application/json", authorization) + def json_post(target, url, body, authorization) + http_post(target, url, body.to_json, "application/json", authorization) end - def json_put(url, body, authorization = nil) - http_put(url, body.to_json, "application/json", authorization) + def json_put(target, url, body, authorization = nil) + http_put(target, url, body.to_json, "application/json", authorization) end # HTTP helpers - def http_get(path, content_type = nil, authorization = nil) + def http_get(target, path, content_type = nil, authorization = nil) headers = {} headers[:content_type] = content_type if content_type headers[:authorization] = authorization if authorization - request(:get, path, nil, headers) + request(target, :get, path, nil, headers) end - def http_post(path, body, content_type, authorization = nil) + def http_post(target, path, body, content_type, authorization = nil) headers = { content_type: content_type } headers[:authorization] = authorization if authorization - request(:post, path, body, headers) + request(target, :post, path, body, headers) end - def http_put(path, body, content_type, authorization = nil) + def http_put(target, path, body, content_type, authorization = nil) headers = { content_type: content_type } headers[:authorization] = authorization if authorization - request(:put, path, body, headers) + request(target, :put, path, body, headers) end - def http_delete(path, authorization) - status = request(:delete, path, nil, authorization: authorization)[0] + def http_delete(target, path, authorization) + status = request(target, :delete, path, nil, authorization: authorization)[0] unless [200, 204].include?(status) - raise (status == 404 ? NotFound : BadResponse), "invalid response from #{@target}: #{status}" + raise (status == 404 ? NotFound : BadResponse), "invalid response from #{path}: #{status}" end end - def request(method, path, payload = nil, headers = {}) + def request(target, method, path, payload = nil, headers = {}) headers = headers.dup headers[:proxy_user] = @proxy if @proxy unless headers[:proxy_user] headers[:accept] = headers[:content_type] if headers[:content_type] && !headers[:accept] - raise BadTarget, "Target must be set before executing a request" unless @target - - req = { method: method, url: "#{@target}#{path}", payload: payload, + req = { method: method, url: "#{target}#{path}", payload: payload, headers: Util.hash_keys(headers, :todash), :multipart => true } logger.debug { "---> #{@async ? 'async' : ''}\nrequest: #{method} #{req[:url]}\n" + diff --git a/gem/lib/uaa/misc.rb b/gem/lib/uaa/misc.rb index 58b58aaee1f..c9d5cad4edf 100644 --- a/gem/lib/uaa/misc.rb +++ b/gem/lib/uaa/misc.rb @@ -33,8 +33,7 @@ class << self end def self.check_id(target, token) - @target = target - reply = json_get("/check_id", "Bearer #{token}") + reply = json_get(target, "/check_id", "Bearer #{token}") # To verify the validity of the Token response, the Client MUST do the following: @@ -68,27 +67,23 @@ def self.check_id(target, token) end def self.whoami(target, auth_header) - @target = target - json_get("/userinfo?schema=openid", auth_header) + json_get(target, "/userinfo?schema=openid", auth_header) end def self.server(target) - @target = target - reply = json_get '/login' + reply = json_get target, '/login' return reply if reply && reply[:prompts] raise BadResponse, "Invalid response from target #{target}" end def self.validation_key(target, client_id = nil, client_secret = nil) - @target = target - json_get("/token_key", (client_id && client_secret ? Http.basic_auth(client_id, client_secret) : nil)) + json_get(target, "/token_key", (client_id && client_secret ? Http.basic_auth(client_id, client_secret) : nil)) end # Returns hash of values from the Authorization Server that are associated # with the opaque token. def self.decode_token(target, client_id, client_secret, token, token_type = "bearer", audience_ids = nil) - @target = target - reply = json_get("/check_token?token_type=#{token_type}&token=#{token}", + reply = json_get(target, "/check_token?token_type=#{token_type}&token=#{token}", Http.basic_auth(client_id, client_secret)) auds = Util.arglist(reply[:aud] || reply[:resource_ids]) if audience_ids && (!auds || (auds & audience_ids).empty?) @@ -98,14 +93,12 @@ def self.decode_token(target, client_id, client_secret, token, token_type = "bea end def self.password_strength(target, password) - @target = target - json_parse_reply(*request(:post, '/password/score', URI.encode_www_form(password: password), + json_parse_reply(*request(target, :post, '/password/score', URI.encode_www_form(password: password), content_type: "application/x-www-form-urlencoded", accept: "application/json")) end def self.varz(target, name, pwd) - @target = target - json_get("/varz", Http.basic_auth(name, pwd)) + json_get(target, "/varz", Http.basic_auth(name, pwd)) end end diff --git a/gem/lib/uaa/token_issuer.rb b/gem/lib/uaa/token_issuer.rb index 92739a5c90e..00c3ed1e87c 100644 --- a/gem/lib/uaa/token_issuer.rb +++ b/gem/lib/uaa/token_issuer.rb @@ -38,14 +38,15 @@ class TokenIssuer include Http attr_accessor :default_scope - def initialize(target, client_id, client_secret = nil, default_scope = nil) + def initialize(target, client_id, client_secret = nil, default_scope = nil, token_target = nil) @target, @client_id, @client_secret = target, client_id, client_secret @default_scope = default_scope + @token_target = token_target end # login prompts for use by app to collect credentials for implicit grant def prompts - reply = json_get '/login' + reply = json_get @target, '/login' return reply[:prompts] if reply && reply[:prompts] raise BadResponse, "No prompts in response from target #{@target}" end @@ -70,7 +71,7 @@ def implicit_grant_with_creds(credentials, scope = nil) # the other OAuth APIs when CFID-239 is done: # body = URI.encode_www_form(credentials.merge(credentials: true)) - status, body, headers = request(:post, uri, body, headers) + status, body, headers = request(@target, :post, uri, body, headers) raise BadResponse, "status #{status}" unless status == 302 req_uri, reply_uri = URI.parse(redir_uri), URI.parse(headers[:location]) fragment, reply_uri.fragment = reply_uri.fragment, nil @@ -154,7 +155,8 @@ def request_token(params) headers = {content_type: "application/x-www-form-urlencoded", accept: "application/json", authorization: Http.basic_auth(@client_id, @client_secret) } body = URI.encode_www_form(params) - reply = json_parse_reply(*request(:post, '/oauth/token', body, headers)) + request_token_target = @token_target || @target + reply = json_parse_reply(*request(request_token_target, :post, '/oauth/token', body, headers)) raise BadResponse unless reply[:token_type] && reply[:access_token] Token.new reply end diff --git a/gem/lib/uaa/user_account.rb b/gem/lib/uaa/user_account.rb index a053539e9ad..891df298d98 100644 --- a/gem/lib/uaa/user_account.rb +++ b/gem/lib/uaa/user_account.rb @@ -47,23 +47,33 @@ def create(name, password, email_addresses = nil, given_name = name, family_name def change_password(user_id, new_password, old_password = nil) password_request = { password: new_password } password_request[:oldPassword] = old_password if old_password +<<<<<<< HEAD json_parse_reply(*json_put("/Users/#{URI.encode(user_id)}/password", password_request, @auth_header)) +======= + json_parse_reply(*json_put(@target, "/User/#{URI.encode(user_id)}/password", password_request, @auth_header)) +>>>>>>> Added support for a distinct token target end def query(attributes = nil, filter = nil) query = {} query[:attributes] = attributes.respond_to?(:join) ? attributes.join(","): attributes.to_s if attributes query[:filter] = filter if filter - json_get("/Users?#{URI.encode_www_form(query)}", @auth_header) + json_get(@target, "/Users?#{URI.encode_www_form(query)}", @auth_header) end def query_by_value(attributes, filter_attribute, filter_value) query(attributes, %<#{filter_attribute} eq '#{filter_value}'>) end +<<<<<<< HEAD def get(user_id); json_get("/Users/#{URI.encode(user_id)}", @auth_header) end def get_by_name(name); get user_id_from_name(name) end def delete(user_id); http_delete "/Users/#{URI.encode(user_id)}", @auth_header end +======= + def get(user_id); json_get(@target, "/User/#{URI.encode(user_id)}", @auth_header) end + def get_by_name(name); get user_id_from_name(name) end + def delete(user_id); http_delete @target, "/User/#{URI.encode(user_id)}", @auth_header end +>>>>>>> Added support for a distinct token target def delete_by_name(name); delete user_id_from_name(name) end def change_password_by_name(name, new_password, old_password = nil) diff --git a/gem/spec/http_spec.rb b/gem/spec/http_spec.rb index 8c851e83d07..6d25aad4a42 100644 --- a/gem/spec/http_spec.rb +++ b/gem/spec/http_spec.rb @@ -39,7 +39,7 @@ class StubHttp < Stub::Base @async = true frequest { f = Fiber.current - http = EM::HttpRequest.new("#{target}/").get + http = EM::HttpRequest.new("#{@target}/").get http.errback { f.resume "error" } http.callback { http.response_header.http_status.should == 200 @@ -53,7 +53,7 @@ class StubHttp < Stub::Base @async = true frequest { f = Fiber.current - conn = EM::HttpRequest.new("#{target}/") + conn = EM::HttpRequest.new("#{@target}/") req1 = conn.get keepalive: true req1.errback { f.resume "error1" } req1.callback { @@ -91,7 +91,7 @@ class StubHttp < Stub::Base it "fail cleanly for a failed dns lookup" do result = frequest { @target = "http://bad~host~name/" - http_get("/") + http_get(@target, "/") } result.should be_an_instance_of BadTarget end @@ -99,17 +99,17 @@ class StubHttp < Stub::Base it "fail cleanly for a get operation, no connection to address" do result = frequest { @target = "http://127.0.0.1:30000" - http_get("/") + http_get(@target, "/") } result.should be_an_instance_of BadTarget end it "fail cleanly for a get operation with bad response" do - frequest { http_get("/bad") }.should be_an_instance_of HTTPException + frequest { http_get(@target, "/bad") }.should be_an_instance_of HTTPException end it "work for a get operation to a valid address" do - status, body, headers = frequest { http_get("/") } + status, body, headers = frequest { http_get(@target, "/") } status.should == 200 body.should match /welcome to stub http/ end @@ -122,7 +122,7 @@ def debug(str = nil) ; @log << (str ? str : yield) end end @logger = clog = CustomLogger.new clog.log.should be_empty - frequest { http_get("/") } + frequest { http_get(@target, "/") } clog.log.should_not be_empty end end diff --git a/gem/spec/stub_scim.rb b/gem/spec/stub_scim.rb index 118c64f9e45..55e159b8e8e 100644 --- a/gem/spec/stub_scim.rb +++ b/gem/spec/stub_scim.rb @@ -12,6 +12,7 @@ #++ require 'uaa/util' +require 'time' module CF::UAA