Skip to content

Commit

Permalink
Added support for a distinct token target
Browse files Browse the repository at this point in the history
Refactored to remove target from the http module

This enables the gem to support multiple targets, one for the authorize
and the other for the token endpoint

uaac target now supports a token target in addition to an authorize
target

Change-Id: I6c1d191e7073d8f2b33a2b689e57fb3da7b2230b
  • Loading branch information
joeldsa committed Sep 6, 2012
1 parent ec875a9 commit 635ac69
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 63 deletions.
27 changes: 24 additions & 3 deletions gem/lib/cli/common.rb
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
16 changes: 14 additions & 2 deletions gem/lib/cli/config.rb
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions gem/lib/cli/token.rb
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions gem/lib/uaa/client_reg.rb
Expand Up @@ -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
Expand Down
45 changes: 21 additions & 24 deletions gem/lib/uaa/http.rb
Expand Up @@ -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
Expand All @@ -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" +
Expand Down
21 changes: 7 additions & 14 deletions gem/lib/uaa/misc.rb
Expand Up @@ -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:

Expand Down Expand Up @@ -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?)
Expand All @@ -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
Expand Down
10 changes: 6 additions & 4 deletions gem/lib/uaa/token_issuer.rb
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
12 changes: 11 additions & 1 deletion gem/lib/uaa/user_account.rb
Expand Up @@ -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)
Expand Down

0 comments on commit 635ac69

Please sign in to comment.