cf-uaac 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/
5
+ doc/
6
+ coverage/
7
+ spec_reports/
8
+ vendor/
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ # Cloud Foundry 2012.02.03 Beta
2
+ # Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
3
+ #
4
+ # This product is licensed to you under the Apache License, Version 2.0 (the "License").
5
+ # You may not use this product except in compliance with the License.
6
+ #
7
+ # This product includes a number of subcomponents with
8
+ # separate copyright notices and license terms. Your use of these
9
+ # subcomponents is subject to the terms and conditions of the
10
+ # subcomponent's license, as noted in the LICENSE file.
11
+ #
12
+
13
+ source "http://rubygems.org"
14
+
15
+ # Specify your gem's dependencies in uaa.gemspec
16
+ gemspec
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # CloudFoundry UAA Command Line Client
2
+
3
+ Command line gem for interacting with the CloudFoundry UAA server.
4
+
5
+ Set up a local ruby environment (so sudo not required):
6
+
7
+ `$ rvm use 1.9.2`
8
+
9
+ or
10
+
11
+ `$ rbenv global 1.9.2-p180`
12
+
13
+ see: https://rvm.io/ or http://rbenv.org/
14
+
15
+ Build the gem
16
+
17
+ `$ bundle install`
18
+ `$ gem build cf-uaac.gemspec`
19
+
20
+ Install it
21
+
22
+ `$ gem install cf-uaac*.gem`
23
+
24
+ Run it
25
+
26
+ `$ uaac help`
27
+ `$ uaac target uaa.cloudfoundry.com`
28
+ `$ uaac token get <your-cf-username>`
29
+ `$ uaac token decode`
30
+
31
+ To use the APIs, see: https://github.com/cloudfoundry/cf-uaa-lib
32
+
33
+ ## Tests
34
+
35
+ Run the tests with rake:
36
+
37
+ `$ bundle exec rake test`
38
+
39
+ Run the tests and see a fancy coverage report:
40
+
41
+ `$ bundle exec rake cov`
42
+
43
+ Run integration tests (on a server running on localhost:8080/uaa):
44
+
45
+ `$ export UAA_CLIENT_ID="admin"`
46
+ `$ export UAA_CLIENT_SECRET="adminsecret"`
47
+ `$ export UAA_CLIENT_TARGET="http://localhost:8080/uaa"`
48
+ `$ bundle exec rspec spec/integration_spec.rb`
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ # Cloud Foundry 2012.02.03 Beta
2
+ # Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
3
+ #
4
+ # This product is licensed to you under the Apache License, Version 2.0 (the "License").
5
+ # You may not use this product except in compliance with the License.
6
+ #
7
+ # This product includes a number of subcomponents with
8
+ # separate copyright notices and license terms. Your use of these
9
+ # subcomponents is subject to the terms and conditions of the
10
+ # subcomponent's license, as noted in the LICENSE file.
11
+ #
12
+
13
+ require "rdoc/task"
14
+ require "rspec/core/rake_task"
15
+ require "bundler/gem_tasks" # only available in bundler >= 1.0.15
16
+ require "ci/reporter/rake/rspec"
17
+
18
+ ENV['CI_REPORTS'] = File.expand_path("spec_reports")
19
+ COV_REPORTS = File.expand_path("coverage")
20
+
21
+ task :default => [:test]
22
+ task :tests => [:test]
23
+ task :spec => [:test]
24
+
25
+ RSpec::Core::RakeTask.new("test") do |t|
26
+ t.rspec_opts = ["--format", "documentation", "--colour"]
27
+ t.pattern = "spec/**/*_spec.rb"
28
+ end
29
+
30
+ RDoc::Task.new do |rd|
31
+ rd.rdoc_files.include("lib/**/*.rb")
32
+ rd.rdoc_dir = "doc"
33
+ end
34
+
35
+ task :ci => [:pre_coverage, :rcov_reports, "ci:setup:rspec", :test]
36
+ task :cov => [:pre_coverage, :test, :view_coverage]
37
+ task :coverage => [:pre_coverage, :test]
38
+
39
+ task :pre_coverage do
40
+ rm_rf COV_REPORTS
41
+ ENV['COVERAGE'] = "exclude-spec exclude-vendor"
42
+ end
43
+
44
+ task :rcov_reports do
45
+ ENV['COVERAGE'] += " rcov"
46
+ end
47
+
48
+ task :view_coverage do
49
+ `firefox #{File.join(COV_REPORTS, 'index.html')}`
50
+ end
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # given the indents, find sub-commands of a given command
4
+ def find_sub_commands (lines, indents, i)
5
+ result = Array.new
6
+ x = i+1
7
+ while x < lines.size && indents[x] > indents[i]
8
+ if indents[x] - indents[i] == 1
9
+ result.push(x)
10
+ end
11
+ x = x+1
12
+ end
13
+ result
14
+ end
15
+
16
+ def find_sub_command_index (lines, sub_command_indices, sub_command)
17
+ sub_command_indices.each do |i|
18
+ if lines[i] == sub_command
19
+ return i
20
+ end
21
+ end
22
+ -1
23
+ end
24
+
25
+ # traverse sub-command tree with the given list of args
26
+ def traverse_command_tree(lines, sub_command_indices, args)
27
+ #puts "traversing for #{args}"
28
+ x = 0
29
+ args.drop(1).each do |arg|
30
+ next if arg.start_with? ("-")
31
+ nx = find_sub_command_index(lines, sub_command_indices[x], arg)
32
+ if nx != -1
33
+ x = nx
34
+ end
35
+ end
36
+ x
37
+ end
38
+
39
+ # use values in array1 as indices into array2 and find the subset from array2
40
+ def find_subset(values, indices)
41
+ result = Array.new
42
+ indices.each do |i|
43
+ result.push(values[i])
44
+ end
45
+ result.join(' ')
46
+ end
47
+
48
+ def get_cache_filename(cmd)
49
+ curr_user = `whoami`.chomp
50
+ home_dir = `echo ~#{curr_user}`.chomp
51
+ "#{home_dir}/.#{cmd}-commands"
52
+ end
53
+
54
+ indent_char = "\t"
55
+ command_file_name = get_cache_filename(ARGV[0])
56
+ if !FileTest.exists?(command_file_name)
57
+ `#{ARGV[0]} help commands > #{command_file_name}`
58
+ end
59
+ lines = File.readlines(command_file_name)
60
+ children = Array.new
61
+ indents = Array.new
62
+
63
+ lines.each_with_index do |line, i|
64
+ indents[i] = line.count(indent_char)
65
+ end
66
+
67
+ # now that we have computed the indent level for each line, remove new-lines and tabs
68
+ lines.collect! { |line| line.gsub(/[#{indent_char}]/, '') }
69
+ lines.collect! { |line| line.gsub(/[\n]/, '') }
70
+
71
+ # pre-processing: find the sub-commands for command (using indent levels)
72
+ lines.each_with_index do |line, i|
73
+ children[i] = find_sub_commands(lines, indents, i)
74
+ end
75
+
76
+ # now that a sub-command tree is available, parse it using the input arguments to find the auto-completion options
77
+ x = traverse_command_tree(lines, children, ARGV.drop(1))
78
+ puts x == -1 ? "" : find_subset(lines, children[x])
79
+
80
+
data/bin/uaac ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path File.join __FILE__, '..', '..', 'lib'
4
+ require 'cli'
5
+ exit CF::UAA::Cli.configure("#{ENV['HOME']}/.uaac.yml").run ? 0 : 1
@@ -0,0 +1,34 @@
1
+ #! /bin/bash
2
+ GLOBAL_OPTS="--help --no-help -h --version --no-version -v --debug --no-debug -d --trace --no-trace -t --config"
3
+
4
+ _debug() {
5
+ if [[ $UAAC_DEBUG -eq 1 ]] ; then
6
+ echo "$@;"
7
+ fi
8
+ }
9
+
10
+ _add_completion_options() {
11
+ local current="${COMP_WORDS[${COMP_CWORD}]}"
12
+ COMPREPLY=( "${COMPREPLY[@]}" $(compgen -W "$1" -- $current) )
13
+ }
14
+
15
+ _uaac() {
16
+ local current="${COMP_WORDS[${COMP_CWORD}]}"
17
+ local helper_input=()
18
+ if [[ "$current" == "" ]] || [[ "$current" == " " ]] || [[ $current == -* ]] ; then
19
+ helper_input=( ${COMP_WORDS[@]} )
20
+ else
21
+ helper_input=( ${COMP_WORDS[@]/$current/} )
22
+ fi
23
+
24
+ local parent_command="${COMP_WORDS[0]}"
25
+ local uaac_opts=$(completion-helper "${parent_command}" "${helper_input[@]}")
26
+ local opts=$uaac_opts
27
+ if [[ $current == -* ]] ; then
28
+ opts="${GLOBAL_OPTS} ${uaac_opts}"
29
+ fi
30
+ _add_completion_options "${opts}"
31
+
32
+ }
33
+
34
+ complete -F _uaac uaac
data/bin/uaas ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path File.join __FILE__, '..', '..', 'lib'
4
+ require 'stub/uaa'
5
+
6
+ CF::UAA::Util.default_logger(:trace)
7
+ CF::UAA::StubUAA.new.run('localhost', 8080)
data/cf-uaac.gemspec ADDED
@@ -0,0 +1,48 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Cloud Foundry 2012.02.03 Beta
4
+ # Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved.
5
+ #
6
+ # This product is licensed to you under the Apache License, Version 2.0 (the "License").
7
+ # You may not use this product except in compliance with the License.
8
+ #
9
+ # This product includes a number of subcomponents with
10
+ # separate copyright notices and license terms. Your use of these
11
+ # subcomponents is subject to the terms and conditions of the
12
+ # subcomponent's license, as noted in the LICENSE file.
13
+ #
14
+
15
+ $:.push File.expand_path("../lib", __FILE__)
16
+ require "cli/version"
17
+
18
+ Gem::Specification.new do |s|
19
+ s.name = "cf-uaac"
20
+ s.version = CF::UAA::CLI_VERSION
21
+ s.authors = ["Dave Syer", "Dale Olds", "Joel D'sa", "Vidya Valmikinathan", "Luke Taylor"]
22
+ s.email = ["dsyer@vmware.com", "olds@vmware.com", "jdsa@vmware.com", "vidya@vmware.com", "ltaylor@vmware.com"]
23
+ s.homepage = "https://github.com/cloudfoundry/cf-uaac"
24
+ s.summary = %q{Command line interface for CloudFoundry UAA}
25
+ s.description = %q{Client command line tools for interacting with the CloudFoundry User Account and Authorization (UAA) server. The UAA is an OAuth2 Authorization Server so it can be used by webapps and command line apps to obtain access tokens to act on behalf of users. The tokens can then be used to access protected resources in a Resource Server. This library can be used by clients (as a convenient wrapper for mainstream oauth gems) or by resource servers.}
26
+
27
+ s.rubyforge_project = "cf-uaac"
28
+
29
+ s.files = `git ls-files`.split("\n")
30
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
31
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
32
+ s.require_paths = ["lib"]
33
+
34
+ # dependencies
35
+ s.add_development_dependency "bundler"
36
+ s.add_development_dependency "rake"
37
+ s.add_development_dependency "rspec"
38
+ s.add_development_dependency "simplecov"
39
+ s.add_development_dependency "simplecov-rcov"
40
+ s.add_development_dependency "ci_reporter"
41
+ s.add_runtime_dependency "highline"
42
+ s.add_runtime_dependency "cf-uaa-lib", ">= 1.3.0"
43
+ s.add_runtime_dependency "multi_json"
44
+ s.add_runtime_dependency "eventmachine"
45
+ s.add_runtime_dependency "launchy"
46
+ s.add_runtime_dependency "em-http-request", ">= 1.0.0.beta.3"
47
+
48
+ end
data/lib/cli.rb ADDED
@@ -0,0 +1,15 @@
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 "cli/version"
15
+ require "cli/runner"
data/lib/cli/base.rb ADDED
@@ -0,0 +1,277 @@
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 'highline'
15
+ require 'optparse'
16
+
17
+ module CF; module UAA end end
18
+
19
+ module CF::UAA
20
+
21
+ class Topic
22
+
23
+ class << self; attr_reader :synonyms end
24
+
25
+ def self.option_defs ; @option_defs || {} end
26
+ def self.commands; @commands || {} end
27
+ def self.topic(*args)
28
+ return @description if args.empty?
29
+ @synonyms = (args[0].split(' ') + args[1..-1]).map(&:downcase)
30
+ @description = args[0]
31
+ end
32
+
33
+ def self.define_option(key, *args)
34
+ @option_defs ||= {}
35
+ raise "conflicting option definition for #{key}" if @option_defs.key?(key) && @option_defs[key] != args
36
+ @option_defs[key] = args
37
+ end
38
+
39
+ def self.desc(template, desc, *options, &handler)
40
+ parts, argc = template.split(' '), 0
41
+ cmd = parts.each_with_object([]) { |p, o|
42
+ if p =~ /^\[/
43
+ argc = parts[-1] =~ /\.\.\.\]$/ ? -1 : parts.length - o.length
44
+ break o
45
+ end
46
+ o << p
47
+ }
48
+ cmd_key = cmd.join('_').to_sym
49
+ define_method(cmd_key, handler)
50
+ @commands ||= {}
51
+ @commands[cmd_key] = {parts: cmd, argc: argc, template: template, desc: desc, options: options}
52
+ end
53
+
54
+ def initialize(cli_class, options = {}, input = $stdin, output = $stdout)
55
+ @cli_class, @options, @input, @output = cli_class, options, input, output
56
+ @highline = HighLine.new(input, output)
57
+ end
58
+
59
+ def ask(prompt); @highline.ask("#{prompt}: ") end
60
+ def ask_pwd(prompt); @highline.ask("#{prompt}: ") { |q| q.echo = '*' } end
61
+ def say(msg); @output.puts(msg); msg end
62
+ def gripe(msg); @output.puts(msg) end
63
+ def opts; @options end
64
+
65
+ def terminal_columns
66
+ return @terminal_columns ||= 0 if @terminal_columns || !@output.tty?
67
+ cols = HighLine::SystemExtensions.terminal_size.first rescue 0
68
+ @terminal_columns = !cols || cols < 40 ? 0 : cols
69
+ end
70
+
71
+ def help_col_start
72
+ return @help_col_start ||= 35 if @help_col_start || terminal_columns == 0 || terminal_columns > 80
73
+ @help_col_start = terminal_columns / 2
74
+ end
75
+
76
+ def pp(obj, indent = 0, wrap = terminal_columns, label = nil)
77
+ case obj
78
+ when Array
79
+ if obj.empty? || !obj[0].is_a?(Hash) && !obj[0].is_a?(Array)
80
+ say_definition(indent, ("#{label}: " if label), Util.strlist(obj), nil, wrap)
81
+ else
82
+ say_definition(indent, "#{label}: ", nil, nil, wrap) if label
83
+ obj.each {|o| pp o, indent, wrap, '-' }
84
+ end
85
+ when Hash
86
+ say_definition(indent, label, nil, nil, wrap) if label
87
+ obj.each {|k, v| pp v, indent + 2, wrap, k.to_s}
88
+ when nil
89
+ else say_definition(indent, ("#{label}: " if label), obj.to_s, nil, wrap)
90
+ end
91
+ obj
92
+ end
93
+
94
+ def say_definition(indent, term, text = nil, start = help_col_start, wrap = terminal_columns)
95
+ cur = indent + (term ? term.length : 0)
96
+ indent < 1 ? @output.printf("%s", term) : @output.printf("%*c%s", indent, ' ', term)
97
+ if start.nil?
98
+ start = 2 if (start = indent + 4) > wrap
99
+ else
100
+ start = 2 if start > wrap
101
+ if cur < start
102
+ @output.printf("%*c", start - cur, ' ')
103
+ elsif cur > start
104
+ @output.printf("\n%*c", start, ' ')
105
+ end
106
+ cur = start
107
+ end
108
+ return @output.printf("\n") unless text && !text.empty?
109
+ text = text.dup
110
+ text.each_line do |line|
111
+ width = wrap == 0 ? 4096 : wrap - cur
112
+ line = line.chomp
113
+ while line.length > width
114
+ i = line.rindex(' ', width) || width
115
+ @output.printf("%s\n%*c", line[0..i - 1], start, ' ')
116
+ width = wrap == 0 ? 4096 : wrap - start
117
+ line = line[i..-1].strip
118
+ end
119
+ @output.printf("%s\n", line)
120
+ cur = start
121
+ end
122
+ nil
123
+ end
124
+
125
+ def opt_help(key, args)
126
+ raise "missing option definition for #{key}" unless args
127
+ long = short = desc = nil
128
+ args.each do |a|
129
+ case a
130
+ when /^-.$/ then short = a
131
+ when /^--.*/ then long = a
132
+ else desc = a
133
+ end
134
+ end
135
+ raise "option definition must include long form (--#{key})" unless long
136
+ [ short ? "#{short} | #{long}" : "#{long}", desc]
137
+ end
138
+
139
+ def opt_strs(opts)
140
+ opts.each_with_object([]) { |o, a|
141
+ @cli_class.option_defs[o].each { |d|
142
+ case d
143
+ when /^--\[no-\](\S+)/ then a << "--#{$1} --no-#{$1}"
144
+ when /^--(\S+)/ then a << "--#{$1}"
145
+ end
146
+ }
147
+ }.join(' ')
148
+ end
149
+
150
+ def say_cmd_helper(info, suffix = nil)
151
+ say_definition 2, info[:template], info[:desc]
152
+ info[:options].each do |o|
153
+ odef, desc = opt_help(o, @cli_class.option_defs[o])
154
+ say_definition help_col_start, "", desc ? "#{odef}, #{desc}" : odef
155
+ end
156
+ @output.print suffix
157
+ end
158
+
159
+ def say_command_help(args)
160
+ say ""
161
+ @cli_class.topics.each do |tpc|
162
+ tpc.commands.each do |k, v|
163
+ if args[0..v[:parts].length - 1] == v[:parts]
164
+ say_cmd_helper(v, "\n")
165
+ return "help command"
166
+ end
167
+ end
168
+ end
169
+ args = args.map(&:downcase)
170
+ @cli_class.topics.each { |tpc| return say_help(tpc) unless (args & tpc.synonyms).empty? }
171
+ gripe "No command or topic found to match: #{args.join(' ')}\n"
172
+ end
173
+
174
+ def say_help(topic = nil)
175
+ @output.print "\n#{@cli_class.overview}\n" unless topic
176
+ @cli_class.topics.each do |tpc|
177
+ next if topic && topic != tpc
178
+ @output.print "\n#{tpc.topic}\n"
179
+ tpc.commands.each { |k, v| say_cmd_helper v }
180
+ end
181
+ if topic || !@cli_class.global_options
182
+ @output.print("\n")
183
+ return topic ? "help topic" : "help"
184
+ end
185
+ @output.print "\nGlobal options:\n"
186
+ @cli_class.global_options.each do |o|
187
+ odef, desc = opt_help(o, @cli_class.option_defs[o])
188
+ say_definition 2, odef, desc
189
+ end
190
+ @output.print("\n")
191
+ "help"
192
+ end
193
+
194
+ def add_command(branches, parts, opts = nil)
195
+ if parts.empty?
196
+ return if opts.nil? || opts.empty?
197
+ return branches << {label: opt_strs(opts)}
198
+ end
199
+ if i = branches.find_index { |b| parts[0] == b[:label] }
200
+ parts.shift
201
+ else
202
+ branches << {label: parts.shift, sub: []}
203
+ i = -1
204
+ end
205
+ add_command(branches[i][:sub], parts, opts)
206
+ end
207
+
208
+ def print_tree(branches, indent)
209
+ return unless branches
210
+ branches.each do |b|
211
+ indent.times { @output.print "\t" };
212
+ @output.puts b[:label]
213
+ print_tree b[:sub], indent + 1
214
+ end
215
+ end
216
+
217
+ def say_commands
218
+ tree = {label: File.basename($0), sub: []}
219
+ @cli_class.topics.each {|t| t.commands.each {|k, v| add_command(tree[:sub], v[:parts].dup, v[:options])}}
220
+ add_command(tree[:sub], [], @cli_class.global_options)
221
+ @output.puts tree[:label]
222
+ print_tree(tree[:sub], 1)
223
+ "help commands"
224
+ end
225
+
226
+ end
227
+
228
+ class BaseCli
229
+
230
+ class << self
231
+ attr_reader :input, :output, :option_defs
232
+ attr_accessor :overview, :topics, :global_options
233
+ end
234
+
235
+ def self.preprocess_options(args, opts); end # to be implemented in subclass
236
+ def self.too_many_args(cmd); end # to be implemented in subclass
237
+
238
+ def self.run(args = ARGV)
239
+ @input ||= $stdin
240
+ @output ||= $stdout
241
+ @option_defs = {}
242
+ @output.string = "" if @output.respond_to?(:string)
243
+ args = args.split if args.respond_to?(:split)
244
+ @parser = OptionParser.new
245
+ opts = @topics.each_with_object({}) do |tpc, o|
246
+ tpc.option_defs.each do |k, optdef|
247
+ @parser.on(*optdef) { |v| o[k] = v }
248
+ @option_defs[k] = optdef
249
+ end
250
+ end
251
+ @parser.parse! args
252
+ preprocess_options(args, opts)
253
+ @topics.each do |tpc|
254
+ tpc.commands.each do |k, v|
255
+ next unless args[0..v[:parts].length - 1] == v[:parts]
256
+ args = args[v[:parts].length..-1]
257
+ if v[:argc] == -1
258
+ # variable args, leave args alone
259
+ elsif args.length > v[:argc]
260
+ too_many_args(v[:parts].dup)
261
+ return nil
262
+ elsif args.length < v[:argc]
263
+ (v[:argc] - args.length).times { args << nil }
264
+ end
265
+ return tpc.new(self, opts, @input, @output).send(k, *args)
266
+ end
267
+ end
268
+ @output.puts "#{File.basename($0)}: subcommand not found"
269
+ rescue Exception => e
270
+ @output.puts "#{File.basename($0)} error", "#{e.class}: #{e.message}", (e.backtrace if opts[:trace])
271
+ ensure
272
+ puts @output.string if opts[:trace] && @print_on_trace
273
+ end
274
+
275
+ end
276
+
277
+ end