cf-uaac 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|