cf-uaac 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/Gemfile +16 -0
- data/README.md +48 -0
- data/Rakefile +50 -0
- data/bin/completion-helper +80 -0
- data/bin/uaac +5 -0
- data/bin/uaac-completion.sh +34 -0
- data/bin/uaas +7 -0
- data/cf-uaac.gemspec +48 -0
- data/lib/cli.rb +15 -0
- data/lib/cli/base.rb +277 -0
- data/lib/cli/client_reg.rb +103 -0
- data/lib/cli/common.rb +187 -0
- data/lib/cli/config.rb +163 -0
- data/lib/cli/favicon.ico +0 -0
- data/lib/cli/group.rb +85 -0
- data/lib/cli/info.rb +54 -0
- data/lib/cli/runner.rb +52 -0
- data/lib/cli/token.rb +217 -0
- data/lib/cli/user.rb +108 -0
- data/lib/cli/version.rb +18 -0
- data/lib/stub/scim.rb +387 -0
- data/lib/stub/server.rb +310 -0
- data/lib/stub/uaa.rb +485 -0
- data/spec/client_reg_spec.rb +104 -0
- data/spec/common_spec.rb +89 -0
- data/spec/group_spec.rb +93 -0
- data/spec/http_spec.rb +165 -0
- data/spec/info_spec.rb +74 -0
- data/spec/spec_helper.rb +87 -0
- data/spec/token_spec.rb +119 -0
- data/spec/user_spec.rb +61 -0
- metadata +292 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
#--
|
2
|
+
# Cloud Foundry 2012.02.03 Beta
|
3
|
+
# Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
6
|
+
# You may not use this product except in compliance with the License.
|
7
|
+
#
|
8
|
+
# This product includes a number of subcomponents with
|
9
|
+
# separate copyright notices and license terms. Your use of these
|
10
|
+
# subcomponents is subject to the terms and conditions of the
|
11
|
+
# subcomponent's license, as noted in the LICENSE file.
|
12
|
+
#++
|
13
|
+
|
14
|
+
require 'spec_helper'
|
15
|
+
require 'cli'
|
16
|
+
|
17
|
+
module CF::UAA
|
18
|
+
|
19
|
+
describe ClientCli do
|
20
|
+
|
21
|
+
include SpecHelper
|
22
|
+
|
23
|
+
before :all do
|
24
|
+
#Util.default_logger(:trace)
|
25
|
+
Cli.configure("", nil, StringIO.new, true)
|
26
|
+
setup_target(authorities: "scim.read", grant_types: "client_credentials")
|
27
|
+
@test_user, @test_pwd = "sam_#{Time.now.to_i}", "correcthorsebatterystaple"
|
28
|
+
end
|
29
|
+
|
30
|
+
after :all do cleanup_target end
|
31
|
+
|
32
|
+
it "registers a new client" do
|
33
|
+
@test_client.should be # actually registered in the before :all block
|
34
|
+
end
|
35
|
+
|
36
|
+
it "gets a client registration" do
|
37
|
+
Cli.run("client get #{@test_client}").should be
|
38
|
+
Cli.output.string.should include @test_client
|
39
|
+
end
|
40
|
+
|
41
|
+
it "lists client registrations" do
|
42
|
+
Cli.run("clients").should be
|
43
|
+
Cli.output.string.should include @admin_client, @test_client
|
44
|
+
end
|
45
|
+
|
46
|
+
context "as test client" do
|
47
|
+
|
48
|
+
before :all do
|
49
|
+
Cli.run("token client get #{@test_client} -s #{@test_secret}").should be
|
50
|
+
end
|
51
|
+
|
52
|
+
it "logs in as test client" do
|
53
|
+
Cli.run("context").should be # login was in before :all block
|
54
|
+
Cli.output.string.should include "access_token", @test_client
|
55
|
+
end
|
56
|
+
|
57
|
+
it "fails to create a user account as test client" do
|
58
|
+
Cli.run("user add #{@test_user} -p #{@test_pwd}").should be_nil
|
59
|
+
Cli.output.string.should include "insufficient_scope"
|
60
|
+
end
|
61
|
+
|
62
|
+
context "as updated client" do
|
63
|
+
|
64
|
+
before :all do
|
65
|
+
# update the test client as the admin client
|
66
|
+
Cli.run("token client get #{@test_client} -s #{@test_secret}").should be
|
67
|
+
Cli.run("context #{@admin_client}").should be
|
68
|
+
Cli.run("client update #{@test_client} --authorities scim.write,scim.read").should be
|
69
|
+
Cli.run("client get #{@test_client}").should be
|
70
|
+
Cli.output.string.should include "scim.read", "scim.write"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "fails to create a user account with old token" do
|
74
|
+
Cli.run("context #{@test_client}").should be
|
75
|
+
Cli.run("user add #{@test_user} -p #{@test_pwd}").should be_nil
|
76
|
+
Cli.output.string.should include "insufficient_scope"
|
77
|
+
end
|
78
|
+
|
79
|
+
it "creates a user account with a new token" do
|
80
|
+
Cli.run("context #{@test_client}").should be
|
81
|
+
Cli.run("token client get #{@test_client} -s #{@test_secret}").should be
|
82
|
+
Cli.run("token decode")
|
83
|
+
Cli.run("user add #{@test_user.capitalize} -p #{@test_pwd} --email #{@test_user}@example.com --family_name #{@test_user.capitalize} --given_name joe").should be
|
84
|
+
Cli.output.string.should_not include "insufficient_scope"
|
85
|
+
Cli.run("user get #{@test_user}").should be
|
86
|
+
Cli.output.string.should include @test_user.capitalize
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
context "as admin client" do
|
93
|
+
it "deletes a client registration" do
|
94
|
+
client = @test_client.dup
|
95
|
+
@test_client.replace("")
|
96
|
+
Cli.run("context #{@admin_client}").should be
|
97
|
+
Cli.run("client delete #{client}").should be
|
98
|
+
Cli.output.string.should include "deleted"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
data/spec/common_spec.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
#--
|
2
|
+
# Cloud Foundry 2012.02.03 Beta
|
3
|
+
# Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
6
|
+
# You may not use this product except in compliance with the License.
|
7
|
+
#
|
8
|
+
# This product includes a number of subcomponents with
|
9
|
+
# separate copyright notices and license terms. Your use of these
|
10
|
+
# subcomponents is subject to the terms and conditions of the
|
11
|
+
# subcomponent's license, as noted in the LICENSE file.
|
12
|
+
#++
|
13
|
+
|
14
|
+
require 'spec_helper'
|
15
|
+
require 'stringio'
|
16
|
+
require 'cli'
|
17
|
+
|
18
|
+
module CF::UAA
|
19
|
+
|
20
|
+
describe CommonCli do
|
21
|
+
|
22
|
+
include SpecHelper
|
23
|
+
|
24
|
+
before :each do
|
25
|
+
Util.default_logger(:trace)
|
26
|
+
Cli.configure("", nil, StringIO.new, true)
|
27
|
+
end
|
28
|
+
|
29
|
+
["-v", "version", "--version"].each do |opt|
|
30
|
+
it "displays a version with #{opt}" do
|
31
|
+
Cli.run(opt).should be
|
32
|
+
Cli.output.string.should include CLI_VERSION
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
["help", "-h"].each do |opt|
|
37
|
+
it "displays general help with #{opt}" do
|
38
|
+
Cli.run(opt).should be
|
39
|
+
["UAA Command Line Interface", "System Information", "Tokens", "User Accounts"].each do |s|
|
40
|
+
Cli.output.string.should include s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "gets commands in bash completion format" do
|
46
|
+
Cli.run("help commands").should be
|
47
|
+
[/--no-version/, /--version/, /^#{File.basename($0)}/, /help/].each do |s|
|
48
|
+
Cli.output.string.should match(s)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
["help targets", "targets -h", "-h targets"].each do |opt|
|
53
|
+
it "displays command specific help like: #{opt}" do
|
54
|
+
Cli.run(opt).should be
|
55
|
+
Cli.output.string.should include("Display all targets")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it "sets a target in the config file" do
|
60
|
+
Cli.run("target example.com --force").should be
|
61
|
+
Config.yaml.should include "https://example.com"
|
62
|
+
end
|
63
|
+
|
64
|
+
it "strips trailing / from target" do
|
65
|
+
Cli.run("target example.com/uaa/ --force")
|
66
|
+
Config.yaml.should include "https://example.com/uaa"
|
67
|
+
Config.yaml.should_not include "https://example.com/uaa/"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "sets multiple targets to be forced qualified in config and targets output" do
|
71
|
+
Cli.run("target example.com --force")
|
72
|
+
Cli.run("target example2.com --force")
|
73
|
+
Cli.run("targets").should be
|
74
|
+
Config.yaml.should include "https://example.com", "https://example2.com"
|
75
|
+
Cli.output.string.should include "https://example.com", "https://example2.com"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "gets it's configuration from alternate source when specified" do
|
79
|
+
Cli.run("target --force foo.bar --config").should be
|
80
|
+
Config.yaml.should include "foo\.bar"
|
81
|
+
Cli.run "target --force baz.com --config"
|
82
|
+
Config.yaml.should include "baz\.com"
|
83
|
+
Config.yaml.should_not include "foo\.bar"
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
data/spec/group_spec.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
#--
|
2
|
+
# Cloud Foundry 2012.02.03 Beta
|
3
|
+
# Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
6
|
+
# You may not use this product except in compliance with the License.
|
7
|
+
#
|
8
|
+
# This product includes a number of subcomponents with
|
9
|
+
# separate copyright notices and license terms. Your use of these
|
10
|
+
# subcomponents is subject to the terms and conditions of the
|
11
|
+
# subcomponent's license, as noted in the LICENSE file.
|
12
|
+
#++
|
13
|
+
|
14
|
+
require 'spec_helper'
|
15
|
+
require 'cli'
|
16
|
+
|
17
|
+
module CF::UAA
|
18
|
+
|
19
|
+
describe GroupCli do
|
20
|
+
|
21
|
+
include SpecHelper
|
22
|
+
|
23
|
+
before :all do
|
24
|
+
#Util.default_logger(:trace)
|
25
|
+
Cli.configure("", nil, StringIO.new, true)
|
26
|
+
setup_target(authorities: "clients.read,scim.read,scim.write")
|
27
|
+
Cli.run("token client get #{@test_client} -s #{@test_secret}").should be
|
28
|
+
@test_user, @test_pwd = "sam_#{Time.now.to_i}", "correcthorsebatterystaple"
|
29
|
+
@test_group = "JaNiToRs_#{Time.now.to_i}"
|
30
|
+
end
|
31
|
+
|
32
|
+
after :all do cleanup_target end
|
33
|
+
before :each do Cli.output.string = "" end
|
34
|
+
|
35
|
+
it "creates many users and a group as the test client" do
|
36
|
+
Cli.run "context #{@test_client}"
|
37
|
+
Cli.run("user add #{@test_user.upcase} -p #{@test_pwd} " +
|
38
|
+
"--email joey@example.com --family_name JONES --given_name JOE").should be
|
39
|
+
29.times { |i| Cli.run("user add #{@test_user.capitalize}-#{i} -p #{@test_pwd} " +
|
40
|
+
"--email #{@test_user}+#{i}@example.com " +
|
41
|
+
"--family_name #{@test_user.capitalize} --given_name joe").should be }
|
42
|
+
Cli.run("group add #{@test_group}").should be
|
43
|
+
Cli.run("groups -a displayName").should be
|
44
|
+
Cli.output.string.should include @test_group
|
45
|
+
end
|
46
|
+
|
47
|
+
it "gets attributes with case-insensitive attribute names" do
|
48
|
+
Cli.run("groups -a displayname").should be
|
49
|
+
Cli.output.string.should include @test_group
|
50
|
+
end
|
51
|
+
|
52
|
+
it "lists all users" do
|
53
|
+
Cli.run("users -a UsernamE").should be
|
54
|
+
29.times { |i| Cli.output.string.should =~ /#{@test_user.capitalize}-#{i}/i }
|
55
|
+
end
|
56
|
+
|
57
|
+
it "preserves case in names" do
|
58
|
+
Cli.run("users -a username").should be
|
59
|
+
29.times { |i| Cli.output.string.should =~ /#{@test_user.capitalize}-#{i}/ }
|
60
|
+
end
|
61
|
+
|
62
|
+
it "lists a page of users" do
|
63
|
+
Cli.run("users -a userName --count 13 --start 5").should be
|
64
|
+
Cli.output.string.should match /itemsPerPage: 13/i
|
65
|
+
Cli.output.string.should match /startIndex: 5/i
|
66
|
+
end
|
67
|
+
|
68
|
+
it "adds users to the group" do
|
69
|
+
cmd = "member add #{@test_group}"
|
70
|
+
29.times { |i| cmd << " #{@test_user.capitalize}-#{i}" }
|
71
|
+
Cli.run(cmd).should be
|
72
|
+
Cli.output.string.should include "success"
|
73
|
+
end
|
74
|
+
|
75
|
+
it "adds one user to the group" do
|
76
|
+
Cli.run("member add #{@test_group} #{@test_user}").should be
|
77
|
+
Cli.output.string.should include "success"
|
78
|
+
end
|
79
|
+
|
80
|
+
it "deletes all members from a group" do
|
81
|
+
pending "waiting on bug fix in uaa, [40594865]" if ENV["UAA_CLIENT_TARGET"]
|
82
|
+
cmd = "member delete #{@test_group} #{@test_user.capitalize}"
|
83
|
+
29.times { |i| cmd << " #{@test_user.capitalize}-#{i}" }
|
84
|
+
Cli.run(cmd).should be
|
85
|
+
Cli.output.string.should include "success"
|
86
|
+
# and they should really be gone
|
87
|
+
Cli.run("group get #{@test_group}")
|
88
|
+
Cli.output.string.should_not match /members/i
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/spec/http_spec.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
#--
|
2
|
+
# Cloud Foundry 2012.02.03 Beta
|
3
|
+
# Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
6
|
+
# You may not use this product except in compliance with the License.
|
7
|
+
#
|
8
|
+
# This product includes a number of subcomponents with
|
9
|
+
# separate copyright notices and license terms. Your use of these
|
10
|
+
# subcomponents is subject to the terms and conditions of the
|
11
|
+
# subcomponent's license, as noted in the LICENSE file.
|
12
|
+
#++
|
13
|
+
|
14
|
+
require 'spec_helper'
|
15
|
+
require 'fiber'
|
16
|
+
require 'net/http'
|
17
|
+
require 'em-http'
|
18
|
+
require 'uaa/http'
|
19
|
+
require 'cli/version'
|
20
|
+
require 'stub/server'
|
21
|
+
|
22
|
+
module CF::UAA
|
23
|
+
|
24
|
+
class StubHttp < Stub::Base
|
25
|
+
route(:get, '/') { reply_in_kind "welcome to stub http, version #{CLI_VERSION}" }
|
26
|
+
route( :get, '/bad') { reply.headers[:location] = ":;+)(\/"; reply_in_kind(3, "bad http status code") }
|
27
|
+
end
|
28
|
+
|
29
|
+
class HttpClient
|
30
|
+
include Http
|
31
|
+
end
|
32
|
+
|
33
|
+
describe Http do
|
34
|
+
|
35
|
+
include SpecHelper
|
36
|
+
|
37
|
+
before :all do
|
38
|
+
@stub_http = Stub::Server.new(StubHttp, Util.default_logger(:info)).run_on_thread
|
39
|
+
end
|
40
|
+
|
41
|
+
after :all do @stub_http.stop if @stub_http end
|
42
|
+
|
43
|
+
it "gets something from stub server on a fiber" do
|
44
|
+
frequest(true) {
|
45
|
+
f = Fiber.current
|
46
|
+
http = EM::HttpRequest.new("#{@stub_http.url}/").get
|
47
|
+
http.errback { f.resume "error" }
|
48
|
+
http.callback {
|
49
|
+
http.response_header.http_status.should == 200
|
50
|
+
f.resume http.response
|
51
|
+
}
|
52
|
+
Fiber.yield
|
53
|
+
}.should match /welcome to stub http/
|
54
|
+
end
|
55
|
+
|
56
|
+
it "uses persistent connections from stubserver" do
|
57
|
+
frequest(true) {
|
58
|
+
f = Fiber.current
|
59
|
+
conn = EM::HttpRequest.new("#{@stub_http.url}/")
|
60
|
+
req1 = conn.get keepalive: true
|
61
|
+
req1.errback { f.resume "error1" }
|
62
|
+
req1.callback {
|
63
|
+
req2 = conn.get
|
64
|
+
req2.errback { f.resume req2.error }
|
65
|
+
req2.callback { f.resume req2.response }
|
66
|
+
}
|
67
|
+
Fiber.yield
|
68
|
+
}.should match /welcome to stub http/
|
69
|
+
end
|
70
|
+
|
71
|
+
it "gets something from stub server on a thread" do
|
72
|
+
@async = false
|
73
|
+
resp = Net::HTTP.get(URI("#{@stub_http.url}/"))
|
74
|
+
resp.should match /welcome to stub http/
|
75
|
+
end
|
76
|
+
|
77
|
+
shared_examples_for "http client" do
|
78
|
+
|
79
|
+
# the following is intended to test that a failed dns lookup will fail the
|
80
|
+
# same way on the buggy em-http-request 1.0.0.beta3 client as it does on
|
81
|
+
# the rest-client. However, some networks (such as the one I am on now)
|
82
|
+
# configure the dhcp client with a dns server that will resolve
|
83
|
+
# every name as a valid address, e.g. bad.example.bad returns an address
|
84
|
+
# to a service signup screen. I have tried stubbing the code in various
|
85
|
+
# ways:
|
86
|
+
# EventMachine.stub(:connect) { raise EventMachine::ConnectionError, "fake error for bad dns lookup" }
|
87
|
+
# EventMachine.unstub(:connect)
|
88
|
+
# Socket.stub(:gethostbyname) { raise SocketError, "getaddrinfo: Name or service not known" }
|
89
|
+
# Socket.unstub(:gethostbyname)
|
90
|
+
# This has had varied success but seems rather brittle. Currently I have opted
|
91
|
+
# to just make the domain name invalid with tildes, but this may not test
|
92
|
+
# the desired code paths
|
93
|
+
it "fails cleanly for a failed dns lookup" do
|
94
|
+
result = frequest(@on_fiber) { @client.http_get("http://bad~host~name/") }
|
95
|
+
result.should be_an_instance_of BadTarget
|
96
|
+
end
|
97
|
+
|
98
|
+
it "fails cleanly for a get operation, no connection to address" do
|
99
|
+
result = frequest(@on_fiber) { @client.http_get("http://127.0.0.1:30000/") }
|
100
|
+
result.should be_an_instance_of BadTarget
|
101
|
+
end
|
102
|
+
|
103
|
+
it "fails cleanly for a get operation with bad response" do
|
104
|
+
frequest(@on_fiber) { @client.http_get(@stub_http.url, "/bad") }.should be_an_instance_of HTTPException
|
105
|
+
end
|
106
|
+
|
107
|
+
it "works for a get operation to a valid address" do
|
108
|
+
status, body, headers = frequest(@on_fiber) { @client.http_get(@stub_http.url, "/") }
|
109
|
+
status.should == 200
|
110
|
+
body.should match /welcome to stub http/
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should send debug information to a custom logger" do
|
114
|
+
class CustomLogger
|
115
|
+
attr_reader :log
|
116
|
+
def initialize; @log = "" end
|
117
|
+
def debug(str = nil) ; @log << (str ? str : yield) end
|
118
|
+
end
|
119
|
+
@client.logger = clog = CustomLogger.new
|
120
|
+
clog.log.should be_empty
|
121
|
+
frequest(@on_fiber) { @client.http_get(@stub_http.url, "/") }
|
122
|
+
clog.log.should_not be_empty
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "on a fiber" do
|
127
|
+
before :all do
|
128
|
+
@on_fiber = true
|
129
|
+
@client = HttpClient.new
|
130
|
+
@client.set_request_handler do |url, method, body, headers|
|
131
|
+
f = Fiber.current
|
132
|
+
connection = EventMachine::HttpRequest.new(url, connect_timeout: 2, inactivity_timeout: 2)
|
133
|
+
client = connection.setup_request(method, head: headers, body: body)
|
134
|
+
|
135
|
+
# This check is for proper error handling with em-http-request 1.0.0.beta.3
|
136
|
+
if defined?(EventMachine::FailedConnection) && connection.is_a?(EventMachine::FailedConnection)
|
137
|
+
raise BadTarget, "HTTP connection setup error: #{client.error}"
|
138
|
+
end
|
139
|
+
|
140
|
+
client.callback { f.resume [client.response_header.http_status, client.response, client.response_header] }
|
141
|
+
client.errback { f.resume [:error, client.error] }
|
142
|
+
result = Fiber.yield
|
143
|
+
if result[0] == :error
|
144
|
+
raise BadTarget, "connection failed" unless result[1] && result[1] != ""
|
145
|
+
raise BadTarget, "connection refused" if result[1].to_s =~ /ECONNREFUSED/
|
146
|
+
raise BadTarget, "unable to resolve address" if /unable.*server.*address/.match result[1]
|
147
|
+
raise HTTPException, result[1]
|
148
|
+
end
|
149
|
+
[result[0], result[1], Util.hash_keys!(result[2], :todash)]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
it_should_behave_like "http client"
|
153
|
+
end
|
154
|
+
|
155
|
+
context "on a thread" do
|
156
|
+
before :all do
|
157
|
+
@on_fiber = false
|
158
|
+
@client = HttpClient.new
|
159
|
+
end
|
160
|
+
it_should_behave_like "http client"
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|