tpitale-rack-oauth2-server 2.2.1
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/CHANGELOG +202 -0
- data/Gemfile +16 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +604 -0
- data/Rakefile +90 -0
- data/VERSION +1 -0
- data/bin/oauth2-server +206 -0
- data/lib/rack-oauth2-server.rb +4 -0
- data/lib/rack/oauth2/admin/css/screen.css +347 -0
- data/lib/rack/oauth2/admin/images/loading.gif +0 -0
- data/lib/rack/oauth2/admin/images/oauth-2.png +0 -0
- data/lib/rack/oauth2/admin/js/application.coffee +220 -0
- data/lib/rack/oauth2/admin/js/jquery.js +166 -0
- data/lib/rack/oauth2/admin/js/jquery.tmpl.js +414 -0
- data/lib/rack/oauth2/admin/js/protovis-r3.2.js +277 -0
- data/lib/rack/oauth2/admin/js/sammy.js +5 -0
- data/lib/rack/oauth2/admin/js/sammy.json.js +5 -0
- data/lib/rack/oauth2/admin/js/sammy.oauth2.js +142 -0
- data/lib/rack/oauth2/admin/js/sammy.storage.js +5 -0
- data/lib/rack/oauth2/admin/js/sammy.title.js +5 -0
- data/lib/rack/oauth2/admin/js/sammy.tmpl.js +5 -0
- data/lib/rack/oauth2/admin/js/underscore.js +722 -0
- data/lib/rack/oauth2/admin/views/client.tmpl +58 -0
- data/lib/rack/oauth2/admin/views/clients.tmpl +52 -0
- data/lib/rack/oauth2/admin/views/edit.tmpl +80 -0
- data/lib/rack/oauth2/admin/views/index.html +39 -0
- data/lib/rack/oauth2/admin/views/no_access.tmpl +4 -0
- data/lib/rack/oauth2/models.rb +27 -0
- data/lib/rack/oauth2/models/access_grant.rb +54 -0
- data/lib/rack/oauth2/models/access_token.rb +129 -0
- data/lib/rack/oauth2/models/auth_request.rb +61 -0
- data/lib/rack/oauth2/models/client.rb +93 -0
- data/lib/rack/oauth2/rails.rb +105 -0
- data/lib/rack/oauth2/server.rb +458 -0
- data/lib/rack/oauth2/server/admin.rb +250 -0
- data/lib/rack/oauth2/server/errors.rb +104 -0
- data/lib/rack/oauth2/server/helper.rb +147 -0
- data/lib/rack/oauth2/server/practice.rb +79 -0
- data/lib/rack/oauth2/server/railtie.rb +24 -0
- data/lib/rack/oauth2/server/utils.rb +30 -0
- data/lib/rack/oauth2/sinatra.rb +71 -0
- data/rack-oauth2-server.gemspec +24 -0
- data/rails/init.rb +11 -0
- data/test/admin/api_test.rb +228 -0
- data/test/admin/ui_test.rb +38 -0
- data/test/oauth/access_grant_test.rb +276 -0
- data/test/oauth/access_token_test.rb +311 -0
- data/test/oauth/authorization_test.rb +298 -0
- data/test/oauth/server_methods_test.rb +292 -0
- data/test/rails2/app/controllers/api_controller.rb +40 -0
- data/test/rails2/app/controllers/application_controller.rb +2 -0
- data/test/rails2/app/controllers/oauth_controller.rb +17 -0
- data/test/rails2/config/environment.rb +19 -0
- data/test/rails2/config/environments/test.rb +0 -0
- data/test/rails2/config/routes.rb +13 -0
- data/test/rails3/app/controllers/api_controller.rb +40 -0
- data/test/rails3/app/controllers/application_controller.rb +2 -0
- data/test/rails3/app/controllers/oauth_controller.rb +17 -0
- data/test/rails3/config/application.rb +19 -0
- data/test/rails3/config/environment.rb +2 -0
- data/test/rails3/config/routes.rb +12 -0
- data/test/setup.rb +120 -0
- data/test/sinatra/my_app.rb +69 -0
- metadata +145 -0
data/Rakefile
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require "rake/testtask"
|
2
|
+
|
3
|
+
spec = Gem::Specification.load(Dir["*.gemspec"].first)
|
4
|
+
|
5
|
+
desc "Install dependencies"
|
6
|
+
task :setup do
|
7
|
+
puts "Installing gems for testing with Sinatra ..."
|
8
|
+
sh "bundle install"
|
9
|
+
puts "Installing gems for testing with Rails 2.3 ..."
|
10
|
+
sh "env BUNDLE_GEMFILE=Rails2 bundle install"
|
11
|
+
puts "Installing gems for testing with Rails 3.x ..."
|
12
|
+
sh "env BUNDLE_GEMFILE=Rails3 bundle install"
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Run this in development mode when updating the CoffeeScript file"
|
16
|
+
task :coffee do
|
17
|
+
sh "coffee -w -o lib/rack/oauth2/admin/js/ lib/rack/oauth2/admin/js/application.coffee"
|
18
|
+
end
|
19
|
+
|
20
|
+
task :compile do
|
21
|
+
sh "coffee -c -l -o lib/rack/oauth2/admin/js/ lib/rack/oauth2/admin/js/application.coffee"
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Build the Gem"
|
25
|
+
task :build=>:compile do
|
26
|
+
sh "gem build #{spec.name}.gemspec"
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Install #{spec.name} locally"
|
30
|
+
task :install=>:build do
|
31
|
+
sudo = "sudo" unless File.writable?( Gem::ConfigMap[:bindir])
|
32
|
+
sh "#{sudo} gem install #{spec.name}-#{spec.version}.gem"
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Push new release to gemcutter and git tag"
|
36
|
+
task :push=>["test:all", "build"] do
|
37
|
+
sh "git push"
|
38
|
+
puts "Tagging version #{spec.version} .."
|
39
|
+
sh "git tag v#{spec.version}"
|
40
|
+
sh "git push --tag"
|
41
|
+
puts "Building and pushing gem .."
|
42
|
+
sh "gem push #{spec.name}-#{spec.version}.gem"
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Run all tests"
|
46
|
+
Rake::TestTask.new do |task|
|
47
|
+
task.test_files = FileList['test/**/*_test.rb']
|
48
|
+
if Rake.application.options.trace
|
49
|
+
#task.warning = true
|
50
|
+
task.verbose = true
|
51
|
+
elsif Rake.application.options.silent
|
52
|
+
task.ruby_opts << "-W0"
|
53
|
+
else
|
54
|
+
task.verbose = true
|
55
|
+
end
|
56
|
+
task.ruby_opts << "-I."
|
57
|
+
end
|
58
|
+
|
59
|
+
namespace :test do
|
60
|
+
task :all=>["test:sinatra", "test:rails2", "test:rails3"]
|
61
|
+
desc "Run all tests against Sinatra"
|
62
|
+
task :sinatra do
|
63
|
+
sh "rake test FRAMEWORK=sinatra"
|
64
|
+
end
|
65
|
+
desc "Run all tests against Rails"
|
66
|
+
task :rails do
|
67
|
+
sh "rake test FRAMEWORK=rails"
|
68
|
+
end
|
69
|
+
desc "Run all tests against Rails 2.3.x"
|
70
|
+
task :rails2 do
|
71
|
+
sh "env BUNDLE_GEMFILE=Rails2 rake test FRAMEWORK=rails"
|
72
|
+
end
|
73
|
+
desc "Run all tests against Rails 3.x"
|
74
|
+
task :rails3 do
|
75
|
+
sh "env BUNDLE_GEMFILE=Rails3 bundle exec rake test FRAMEWORK=rails"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
task :default=>"test:all"
|
79
|
+
|
80
|
+
begin
|
81
|
+
require "yard"
|
82
|
+
YARD::Rake::YardocTask.new do |doc|
|
83
|
+
doc.files = FileList["lib/**/*.rb"]
|
84
|
+
end
|
85
|
+
rescue LoadError
|
86
|
+
end
|
87
|
+
|
88
|
+
task :clean do
|
89
|
+
rm_rf %w{doc .yardoc *.gem}
|
90
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.2.1
|
data/bin/oauth2-server
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
|
+
require "rack/oauth2/server"
|
4
|
+
require "uri"
|
5
|
+
|
6
|
+
# this is a weird way to shorten the namespace
|
7
|
+
include Rack::OAuth2
|
8
|
+
|
9
|
+
|
10
|
+
# if (i = ARGV.index("--db")) && ARGV[i+1]
|
11
|
+
# url = ARGV[i + 1]
|
12
|
+
# uri = URI.parse(url)
|
13
|
+
# uri = URI.parse("mongo://#{url}") if uri.opaque
|
14
|
+
# db = Mongo::Connection.new(uri.host, uri.port)[uri.path.sub(/^\//, "")]
|
15
|
+
# db.authenticate uri.user, uri.password if uri.user
|
16
|
+
# Server.database = db
|
17
|
+
# ARGV[i,2] = []
|
18
|
+
# end
|
19
|
+
if (i = ARGV.index("--port") || ARGV.index("-p")) && ARGV[i+1]
|
20
|
+
port = ARGV[i + 1].to_i
|
21
|
+
ARGV[i,2] = []
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
case ARGV[0]
|
26
|
+
when "list"
|
27
|
+
|
28
|
+
# fail "No database. Use the --db option to tell us which database to use" unless Server.database
|
29
|
+
Server::Client.all.each do |client|
|
30
|
+
next if client.revoked
|
31
|
+
print "%-30s\t%s\n" % [client.display_name, client.link]
|
32
|
+
print " ID %s\tSecret %s\n" % [client.id, client.secret]
|
33
|
+
print "\n"
|
34
|
+
end
|
35
|
+
|
36
|
+
when "register"
|
37
|
+
|
38
|
+
# fail "No database. Use the --db option to tell us which database to use" unless Server.database
|
39
|
+
begin
|
40
|
+
print "Application name:\t"
|
41
|
+
display_name = $stdin.gets
|
42
|
+
print "Application URL:\t"
|
43
|
+
link = $stdin.gets
|
44
|
+
print "Redirect URI:\t\t"
|
45
|
+
redirect_uri = $stdin.gets
|
46
|
+
print "Scope (space separated names):\t\t"
|
47
|
+
scope = $stdin.gets
|
48
|
+
client = Server::Client.create(:display_name=>display_name, :link=>link, :redirect_uri=>redirect_uri, :scope=>scope)
|
49
|
+
rescue
|
50
|
+
puts "\nFailed to register client: #{$!}"
|
51
|
+
exit -1
|
52
|
+
end
|
53
|
+
puts "Registered #{client.display_name}"
|
54
|
+
puts "ID\t#{client.id}"
|
55
|
+
puts "Secret\t#{client.secret}"
|
56
|
+
|
57
|
+
when "setup"
|
58
|
+
|
59
|
+
# fail "No database. Use the --db option to tell us which database to use" unless Server.database
|
60
|
+
puts "Where would you mount the Web console? This is a URL that must end with /admin,"
|
61
|
+
puts "for example, http://example.com/oauth/admin"
|
62
|
+
print ": "
|
63
|
+
uri = URI.parse($stdin.gets)
|
64
|
+
begin
|
65
|
+
uri.normalize!
|
66
|
+
fail "No an HTTP/S URL" unless uri.absolute? && %{http https}.include?(uri.scheme)
|
67
|
+
fail "Path must end with /admin" unless uri.path[/\/admin$/]
|
68
|
+
client = Server::Client.create(:display_name=>"OAuth Console", :link=>uri.to_s, :image_url=>"#{uri.to_s}/images/oauth-2.png",
|
69
|
+
:redirect_uri=>uri.to_s, :scope=>"oauth-admin")
|
70
|
+
rescue
|
71
|
+
puts "\nFailed to register client: #{$!}"
|
72
|
+
exit -1
|
73
|
+
end
|
74
|
+
print <<-TEXT
|
75
|
+
|
76
|
+
Next Steps
|
77
|
+
==========
|
78
|
+
|
79
|
+
Make sure you ONLY authorize administrators to use the oauth-admin scope.
|
80
|
+
For example:
|
81
|
+
|
82
|
+
before_filter do
|
83
|
+
# Only admins allowed to authorize the scope oauth-admin
|
84
|
+
head oauth.deny! if oauth.scope.include?("oauth-admin") && !current_user.admin?
|
85
|
+
end
|
86
|
+
|
87
|
+
Rails 2.x, add the following to config/environment.rb:
|
88
|
+
|
89
|
+
config.after_initialize do
|
90
|
+
config.middleware.use Rack::OAuth2::Server::Admin.mount "#{uri.path}"
|
91
|
+
Rack::OAuth2::Server::Admin.set :client_id, "#{client.id}"
|
92
|
+
Rack::OAuth2::Server::Admin.set :client_secret, "#{client.secret}"
|
93
|
+
end
|
94
|
+
|
95
|
+
Rails 3.x, add the following to config/application.rb:
|
96
|
+
|
97
|
+
config.after_initialize do
|
98
|
+
Rack::OAuth2::Server::Admin.set :client_id, "#{client.id}"
|
99
|
+
Rack::OAuth2::Server::Admin.set :client_secret, "#{client.secret}"
|
100
|
+
end
|
101
|
+
|
102
|
+
And add the follownig to config/routes.rb:
|
103
|
+
|
104
|
+
mount Rack::OAuth2::Server::Admin=>"/oauth/admin"
|
105
|
+
|
106
|
+
Sinatra, Padrino and other Rack applications, mount the console:
|
107
|
+
|
108
|
+
Rack::Builder.new do
|
109
|
+
map("#{uri.path}") { run Rack::OAuth2::Server::Admin }
|
110
|
+
map("/") { run MyApp }
|
111
|
+
end
|
112
|
+
Rack::OAuth2::Server::Admin.set :client_id, "#{client.id}"
|
113
|
+
Rack::OAuth2::Server::Admin.set :client_secret, "#{client.secret}"
|
114
|
+
|
115
|
+
The console will authorize access by redirecting to
|
116
|
+
https://#{uri.host}/oauth/authorize
|
117
|
+
|
118
|
+
If this is not your OAuth 2.0 authorization endpoint, you can change it by
|
119
|
+
setting the :authorize option.
|
120
|
+
TEXT
|
121
|
+
|
122
|
+
when "practice"
|
123
|
+
|
124
|
+
require "logger"
|
125
|
+
begin
|
126
|
+
require "thin"
|
127
|
+
rescue LoadError
|
128
|
+
puts "Needs the Thin Web server. Please gem install thin and run again"
|
129
|
+
exit -1
|
130
|
+
end
|
131
|
+
require "rack/oauth2/server/practice"
|
132
|
+
|
133
|
+
# fail "No database. Use the --db option to tell us which database to use" unless Server.database
|
134
|
+
port ||= 8080
|
135
|
+
admin_url = "http://localhost:#{port}/oauth/admin"
|
136
|
+
unless client = Server::Client.lookup(admin_url)
|
137
|
+
client = Server::Client.create(:display_name=>"Practice OAuth Console", :image_url=>"#{admin_url}/images/oauth-2.png",
|
138
|
+
:link=>admin_url, :redirect_uri=>admin_url, :scope=>"oauth-admin")
|
139
|
+
end
|
140
|
+
Server::Admin.configure do |config|
|
141
|
+
logger = Logger.new(STDOUT)
|
142
|
+
logger.level = Logger::DEBUG
|
143
|
+
config.set :client_id, client.id
|
144
|
+
config.set :client_secret, client.secret
|
145
|
+
config.set :scope, "nobody sudo"
|
146
|
+
config.set :logger, logger
|
147
|
+
config.set :logging, true
|
148
|
+
config.set :dump_errors, true
|
149
|
+
config.oauth.logger = logger
|
150
|
+
end
|
151
|
+
|
152
|
+
Server::Practice.configure do |config|
|
153
|
+
logger = Logger.new(STDOUT)
|
154
|
+
logger.level = Logger::DEBUG
|
155
|
+
config.set :logger, logger
|
156
|
+
config.set :logging, true
|
157
|
+
config.set :dump_errors, true
|
158
|
+
config.oauth.logger = logger
|
159
|
+
end
|
160
|
+
|
161
|
+
print "\nFiring up the practice server.\nFor instructions, go to http://localhost:#{port}/\n\n\n"
|
162
|
+
Thin::Server.new "127.0.0.1", port do
|
163
|
+
map("/") { run Server::Practice.new }
|
164
|
+
map("/oauth/admin") { run Server::Admin.new }
|
165
|
+
end.start
|
166
|
+
|
167
|
+
# when "migrate"
|
168
|
+
#
|
169
|
+
# # fail "No database. Use the --db option to tell us which database to use" unless Server.database
|
170
|
+
# puts "Set all clients to this scope (can change later by calling Client.register):"
|
171
|
+
# print ": "
|
172
|
+
# scope = $stdin.gets.strip.split
|
173
|
+
# puts "Updating Client scope to #{scope.join(", ")}"
|
174
|
+
# Server::Client.collection.find({ :scope=>{ :$exists=>false } }, :fields=>[]).each do |client|
|
175
|
+
# update = { :scope=>scope,
|
176
|
+
# :tokens_granted=>Server::AccessToken.count(:client_id=>client["_id"]),
|
177
|
+
# :tokens_revoked=>Server::AccessToken.count(:client_id=>client["_id"], :revoked=>true) }
|
178
|
+
# Server::Client.collection.update({ :_id=>client["_id"] }, { :$set=>update })
|
179
|
+
# end
|
180
|
+
# [Server::AccessToken, Server::AccessGrant, Server::AuthRequest].each do |mod|
|
181
|
+
# puts "Updating #{mod.name} scope from string to array"
|
182
|
+
# mod.collection.find({ :scope=>{ :$type=>2 } }, :fields=>[]).each do |token|
|
183
|
+
# scope = token["scope"].split
|
184
|
+
# mod.collection.update({ :_id=>token["_id"] }, { :$set=>{ :scope=>scope } })
|
185
|
+
# end
|
186
|
+
# end
|
187
|
+
else
|
188
|
+
|
189
|
+
print <<-TEXT
|
190
|
+
Usage: oauth2-server [options] COMMAND [args]
|
191
|
+
Version #{Server::VERSION}
|
192
|
+
|
193
|
+
Commands:
|
194
|
+
list Lists all active clients
|
195
|
+
migrate Run this when migrating from 1.x to 2.x
|
196
|
+
practice Runs a dummy OAuth 2.0 server, use this to test your OAuth 2.0 client
|
197
|
+
register Register a new client application
|
198
|
+
setup Create new admin account and help you setup the OAuth Web console
|
199
|
+
|
200
|
+
Options:
|
201
|
+
--db database Database name or connection URL
|
202
|
+
--port number Port to run admin server, detault is 8080
|
203
|
+
TEXT
|
204
|
+
exit -1
|
205
|
+
|
206
|
+
end
|
@@ -0,0 +1,347 @@
|
|
1
|
+
html {
|
2
|
+
background: #eee;
|
3
|
+
}
|
4
|
+
body {
|
5
|
+
margin: 0;
|
6
|
+
padding: 0;
|
7
|
+
width: 100%;
|
8
|
+
font: 12pt "Helvetica", "Lucida Sans", "Verdana";
|
9
|
+
}
|
10
|
+
|
11
|
+
a { text-decoration: none; color: #00c; }
|
12
|
+
a:hover, a:focus { text-decoration: underline; color: #00c; }
|
13
|
+
h1, h2 {
|
14
|
+
text-shadow: rgba(255,255,255,.2) 0 1px 1px;
|
15
|
+
color: rgb(76, 86, 108);
|
16
|
+
}
|
17
|
+
h1 { font-size: 18pt; margin: 0.6em 0 }
|
18
|
+
h2 { font-size: 16pt; margin: 0.3em 0 }
|
19
|
+
|
20
|
+
label {
|
21
|
+
display: block;
|
22
|
+
color: #000;
|
23
|
+
font-weight: 600;
|
24
|
+
font-size: 0.9em;
|
25
|
+
margin: 0.9em 0;
|
26
|
+
}
|
27
|
+
label input, label textarea, label select {
|
28
|
+
display: block;
|
29
|
+
}
|
30
|
+
label.check {
|
31
|
+
font-weight: normal;
|
32
|
+
}
|
33
|
+
label.check input {
|
34
|
+
display: inline;
|
35
|
+
}
|
36
|
+
label input, label textarea {
|
37
|
+
font-size: 12pt;
|
38
|
+
line-height: 1.3em;
|
39
|
+
}
|
40
|
+
label .hint {
|
41
|
+
font-weight: normal;
|
42
|
+
color: #666;
|
43
|
+
margin: 0;
|
44
|
+
}
|
45
|
+
button {
|
46
|
+
font-size: 11pt;
|
47
|
+
text-shadow: 0 -1px 1px rgba(0,0,0,0.25);
|
48
|
+
border: 1px solid #dddddd;
|
49
|
+
background: #f6f6f6 50% 50% repeat-x;
|
50
|
+
font-weight: bold;
|
51
|
+
color: #0073ea;
|
52
|
+
outline: none;
|
53
|
+
line-height: 1.3em;
|
54
|
+
vertical-align: bottom;
|
55
|
+
padding: 2px 8px;
|
56
|
+
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(226,226,226,0.0)), to(rgba(226,226,226,1.0)));
|
57
|
+
-webkit-border-radius: 4px; -moz-border-radius: 4px;
|
58
|
+
-moz-box-shadow: 0 0 4px rgba(0,0,0,0.0);
|
59
|
+
-webkit-box-shadow: 0 0 4px rgba(0,0,0,0.0);
|
60
|
+
}
|
61
|
+
button:hover, button:focus {
|
62
|
+
text-shadow: 0 -1px 1px rgba(255,255,255,0.25);
|
63
|
+
border: 1px solid #0073ea;
|
64
|
+
background: #0073ea 50% 50% repeat-x;
|
65
|
+
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(0, 115, 234, 0.5)), to(rgba(0,115,234, 1.0)));
|
66
|
+
color: #fff;
|
67
|
+
text-decoration: none;
|
68
|
+
cursor: pointer;
|
69
|
+
-moz-box-shadow: 0 2px 4px rgba(0,0,0,0.5);
|
70
|
+
-webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.5);
|
71
|
+
}
|
72
|
+
button:active { background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(0, 115, 234, 1.0)), to(rgba(0,115,234, 0.5))); position: relative; top: 1px }
|
73
|
+
.error {
|
74
|
+
color: #f44;
|
75
|
+
padding-left: 1em;
|
76
|
+
}
|
77
|
+
|
78
|
+
|
79
|
+
/* Message dropping down from the top */
|
80
|
+
#notice {
|
81
|
+
position: absolute;
|
82
|
+
top: 0;
|
83
|
+
left: 0;
|
84
|
+
right: 0;
|
85
|
+
line-height: 1.6em;
|
86
|
+
background: #FFFE36;
|
87
|
+
color: #000;
|
88
|
+
border-bottom: 1px solid #ddd;
|
89
|
+
text-align: center;
|
90
|
+
z-index: 99;
|
91
|
+
}
|
92
|
+
|
93
|
+
#header {
|
94
|
+
margin: 0;
|
95
|
+
padding: 1em 2em 3em 2em;
|
96
|
+
border-bottom: 2px solid #CCC;
|
97
|
+
background: #6595A4;
|
98
|
+
color: #fff;
|
99
|
+
}
|
100
|
+
#header .title {
|
101
|
+
font-size: 18pt;
|
102
|
+
font-weight: bold;
|
103
|
+
display: block;
|
104
|
+
line-height: 32px;
|
105
|
+
float: left;
|
106
|
+
}
|
107
|
+
#header .title a {
|
108
|
+
color: #fff;
|
109
|
+
text-shadow: white 0px 0px 1px;
|
110
|
+
text-decoration: none;
|
111
|
+
}
|
112
|
+
#header .title img {
|
113
|
+
width: 32px;
|
114
|
+
height: 32px;
|
115
|
+
vertical-align: bottom;
|
116
|
+
}
|
117
|
+
#header .signout, #header .signin {
|
118
|
+
color: #C8E9F3;
|
119
|
+
float: right;
|
120
|
+
font-size: 11pt;
|
121
|
+
line-height: 32px;
|
122
|
+
display: none;
|
123
|
+
}
|
124
|
+
|
125
|
+
#main {
|
126
|
+
margin: 0;
|
127
|
+
padding: 2em 2em 4em 2em;
|
128
|
+
background: #fff;
|
129
|
+
border: 1px solid #fff;
|
130
|
+
min-width: 960px;
|
131
|
+
}
|
132
|
+
#footer {
|
133
|
+
background: #eee;
|
134
|
+
color: #666;
|
135
|
+
border-top: 1px solid #ccc;
|
136
|
+
font-size: 90%;
|
137
|
+
padding: 0 2em 2em 2em;
|
138
|
+
}
|
139
|
+
|
140
|
+
table {
|
141
|
+
width: 100%;
|
142
|
+
table-layout: auto;
|
143
|
+
empty-cells: show;
|
144
|
+
border-collapse: separate;
|
145
|
+
border-spacing: 0px;
|
146
|
+
margin-top: 2em;
|
147
|
+
}
|
148
|
+
table th {
|
149
|
+
text-align: left;
|
150
|
+
border-bottom: 1px solid #ccc;
|
151
|
+
margin-right: 48px;
|
152
|
+
}
|
153
|
+
table td {
|
154
|
+
text-align: left;
|
155
|
+
vertical-align: top;
|
156
|
+
border-bottom: 1px solid #ddf;
|
157
|
+
line-height: 32px;
|
158
|
+
margin: 0;
|
159
|
+
padding: 0;
|
160
|
+
}
|
161
|
+
table tr:hover td {
|
162
|
+
background: #ddf;
|
163
|
+
}
|
164
|
+
table td.created, table td.revoke, table td.accessed {
|
165
|
+
width: 6em;
|
166
|
+
}
|
167
|
+
table tr.revoked td, table tr.revoked a {
|
168
|
+
color: #888;
|
169
|
+
}
|
170
|
+
table button {
|
171
|
+
margin-top: -2px;
|
172
|
+
font-size: 10pt;
|
173
|
+
}
|
174
|
+
|
175
|
+
table.clients td.name {
|
176
|
+
padding-left: 32px;
|
177
|
+
}
|
178
|
+
table.clients td.name img {
|
179
|
+
width: 24px;
|
180
|
+
height: 24px;
|
181
|
+
border: none;
|
182
|
+
margin: 4px 4px -4px -32px;
|
183
|
+
}
|
184
|
+
table.clients td.secrets {
|
185
|
+
width: 28em;
|
186
|
+
}
|
187
|
+
table.clients td.secrets dl {
|
188
|
+
display: none;
|
189
|
+
width: 40em;
|
190
|
+
margin: 0 -12em 0.6em 0;
|
191
|
+
line-height: 1.3em;
|
192
|
+
}
|
193
|
+
table.clients td.secrets dt {
|
194
|
+
width: 4em;
|
195
|
+
float: left;
|
196
|
+
color: #888;
|
197
|
+
margin-bottom: 0.3em;
|
198
|
+
}
|
199
|
+
table.clients td.secrets dd:after {
|
200
|
+
content: ".";
|
201
|
+
display: block;
|
202
|
+
clear: both;
|
203
|
+
visibility: hidden;
|
204
|
+
line-height: 0;
|
205
|
+
height: 0;
|
206
|
+
}
|
207
|
+
|
208
|
+
table.tokens td.token {
|
209
|
+
width: 36em;
|
210
|
+
}
|
211
|
+
table.tokens td.scope {
|
212
|
+
float: none;
|
213
|
+
}
|
214
|
+
|
215
|
+
.pagination {
|
216
|
+
width: 100%;
|
217
|
+
margin-top: 2em;
|
218
|
+
}
|
219
|
+
.pagination a[rel=next] {
|
220
|
+
float: right;
|
221
|
+
}
|
222
|
+
.pagination a[rel=previous] {
|
223
|
+
float: left;
|
224
|
+
}
|
225
|
+
|
226
|
+
.metrics {
|
227
|
+
height: 100px;
|
228
|
+
}
|
229
|
+
.metrics #fig {
|
230
|
+
float: left;
|
231
|
+
width: 500px;
|
232
|
+
height: 60px;
|
233
|
+
}
|
234
|
+
.metrics:after {
|
235
|
+
content: ".";
|
236
|
+
display: block;
|
237
|
+
clear: both;
|
238
|
+
visibility: hidden;
|
239
|
+
line-height: 0;
|
240
|
+
height: 0;
|
241
|
+
}
|
242
|
+
.badges {
|
243
|
+
list-style: none;
|
244
|
+
margin: 0;
|
245
|
+
padding: 0;
|
246
|
+
text-align: right;
|
247
|
+
width: 100%;
|
248
|
+
}
|
249
|
+
.badges li {
|
250
|
+
display: inline-block;
|
251
|
+
margin-left: 8px;
|
252
|
+
min-width: 8em;
|
253
|
+
}
|
254
|
+
.badges big {
|
255
|
+
font-size: 22pt;
|
256
|
+
display: block;
|
257
|
+
text-align: center;
|
258
|
+
}
|
259
|
+
.badges small {
|
260
|
+
font-size: 11pt;
|
261
|
+
display: block;
|
262
|
+
text-align: center;
|
263
|
+
}
|
264
|
+
|
265
|
+
.client .details {
|
266
|
+
margin: 0 0 2em 0;
|
267
|
+
}
|
268
|
+
.client .details .name {
|
269
|
+
margin: 0;
|
270
|
+
font-size: 16pt;
|
271
|
+
font-weight: bold;
|
272
|
+
float: left;
|
273
|
+
}
|
274
|
+
.client .details img {
|
275
|
+
border: none;
|
276
|
+
width: 24px;
|
277
|
+
height: 24px;
|
278
|
+
vertical-align: bottom;
|
279
|
+
}
|
280
|
+
.client .details .actions {
|
281
|
+
float: left;
|
282
|
+
line-height: 20pt;
|
283
|
+
margin-left: 1em;
|
284
|
+
}
|
285
|
+
.client .details .actions a {
|
286
|
+
margin: 0 0 0 0.3em;
|
287
|
+
}
|
288
|
+
.client .details .meta {
|
289
|
+
clear: both;
|
290
|
+
display: block;
|
291
|
+
color: #888;
|
292
|
+
font-size: 10pt;
|
293
|
+
}
|
294
|
+
.client .details .notes {
|
295
|
+
font-size: 11pt;
|
296
|
+
margin: 0.2em 0;
|
297
|
+
}
|
298
|
+
|
299
|
+
.client.new, .client.edit {
|
300
|
+
margin: 0;
|
301
|
+
padding: 1em;
|
302
|
+
border: 1px solid #eee;
|
303
|
+
}
|
304
|
+
.client .fields>#image {
|
305
|
+
float: left;
|
306
|
+
margin: 0.5em 12px 0 0;
|
307
|
+
width: 48px;
|
308
|
+
height: 48px;
|
309
|
+
}
|
310
|
+
.client .fields>* {
|
311
|
+
margin-left: 60px;
|
312
|
+
}
|
313
|
+
.client .fields {
|
314
|
+
float: left;
|
315
|
+
}
|
316
|
+
.client .scope {
|
317
|
+
float: right;
|
318
|
+
}
|
319
|
+
.client .scope .uncommon {
|
320
|
+
color: red;
|
321
|
+
}
|
322
|
+
.client hr {
|
323
|
+
clear: both;
|
324
|
+
border: none;
|
325
|
+
margin: 1em;
|
326
|
+
}
|
327
|
+
|
328
|
+
.no-access {
|
329
|
+
margin: 0;
|
330
|
+
padding: 0;
|
331
|
+
}
|
332
|
+
.no-access h1 {
|
333
|
+
color: red;
|
334
|
+
}
|
335
|
+
|
336
|
+
#throbber {
|
337
|
+
display: none;
|
338
|
+
position: absolute;
|
339
|
+
top: 6em;
|
340
|
+
right: 2em;
|
341
|
+
width: 48px;
|
342
|
+
height: 48px;
|
343
|
+
content: "";
|
344
|
+
}
|
345
|
+
.loading {
|
346
|
+
background: url("../images/loading.gif") no-repeat 50% 50%;
|
347
|
+
}
|