coca 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright 2013 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,208 @@
1
+ # Coca
2
+
3
+ Coca stands for **Chain of Command Authentication**. It's a lightweight authentication-delegation scheme that makes SSO very easy to add to a devise-based rails app. Delegation is through an ordinary JSON API so you can pass any user data you like down the chain after authentication succeeds.
4
+
5
+ We thought about calling it 'delegated devise' because in the present implementation it's just a devise strategy that can delegate to another application and an API for receiving delegated auth calls. In future we'd like to extend the principle more widely so we gave it a more general name.
6
+
7
+ Coca is highly configurable but the defaults are simple and secure. It is designed to be part of a service-based architecture, where it will bind together a set of applications around a shared authentication service.
8
+
9
+ Coca is naturally extensible because you can send any information you like down the chain and do whatever you like when it arrives. RBAC or other authorization information is easily distributed this way.
10
+
11
+ Since we already have a nice tidy deference tree, Coca also supports attribute propagation. Your auth master is likely also to be a directory server and coca can help you to dry out contact (or any) attributes by passing notifications up and down the chain when they change.
12
+
13
+
14
+ ## tl;dr
15
+
16
+ class User < ActiveRecord::Base
17
+ # must have a :uid column
18
+ devise :cocable
19
+ end
20
+
21
+ # and in an initialiser:
22
+
23
+ Coca.secret = "Shared secret key"
24
+
25
+ Coca.delegate_to do |master|
26
+ master.host = auth.example.com
27
+ end
28
+
29
+
30
+ ## Use coca if
31
+
32
+ * You want to implement single sign-on within a cluster of trusted applications that all inhabit a single domain;
33
+
34
+ * You want an easy way to pass around permissions or other extended user information;
35
+
36
+ * You want your authentication service to be part of your usual rails cluster and benefit from its existing availability and backup measures;
37
+
38
+ * You're not planning to offer a remote login-with-me service to applications as yet unknown.
39
+
40
+ There's nothing to stop you running coca in parallel with other auth services including OAuth, SAML or even LDAP.
41
+
42
+
43
+ ## Usage
44
+
45
+ Each link in the chain of command is defined as a server to which we defer and/or a set of servers from which we accept requests. Within that link you also have to declare that one or models should take part in the authentication scheme.
46
+
47
+
48
+ ### Configuring the chain
49
+
50
+ Each application can be master, servant or both. Configuration is usually in config/initialisers/coca.rb:
51
+
52
+ # On every application in this chain
53
+
54
+ Coca.secret = "Shared secret key"
55
+
56
+ # To act as a servant:
57
+
58
+ Coca.delegate_to do |master|
59
+ master.host = fq.domain.com or ip address or localhost
60
+ master.port = optional
61
+ master.path = "/coca/user"
62
+ end
63
+
64
+ # To act as a master:
65
+
66
+ Coca.delegate_from do |servant|
67
+ servant.host = fq.domain.com or ip address or localhost
68
+ servant.ttl = 3600
69
+ end
70
+
71
+ You can have any number of links up and down. More than one `delegate_to` is unlikely but possible.
72
+
73
+ # TTL can be set globally
74
+ Coca.token_ttl = 86400
75
+
76
+ # disable to not broadcast user changes or ignore incoming updates
77
+ Coca.propagate_updates = true
78
+
79
+ # disable to allow insecure requests in and out
80
+ Coca.require_https = true
81
+
82
+ # disable to omit reverse IP check on incoming requests and updates
83
+ Coca.check_source = true
84
+
85
+ # you may want to set this per environment
86
+ Coca.cookie_name = 'must_be_the_same_across_sso_group'
87
+ Coca.cookie_domain = :all
88
+
89
+
90
+ ### Delegating in a model
91
+
92
+ To start with coca is implemented as a devise strategy. Minimally:
93
+
94
+ class User < ActiveRecord::Base
95
+ devise :cocable
96
+
97
+ You can try other strategies first: they will work in the usual way and check against local resource properties. The `:cocable` strategy would normally be last.
98
+
99
+ class User < ActiveRecord::Base
100
+ devise :database_authenticatable,
101
+ :token_authenticatable,
102
+ :cocable
103
+
104
+ If masters are defined, the cocable strategy will pass credentials up the chain before finally admitting failure.
105
+
106
+
107
+ ### Authenticating in the master
108
+
109
+ The delegated auth call is just an ordinary API request. The supplied auth parameters are passed on. If they are accepted by the master server then the API request will work and a JSON packet will be returned. If not, we return 401. All of this is done just by trying to authenticate the resource in the usual way, and usually requires no change to your resource class.
110
+
111
+ To continue the chain upward, just add the cocable strategy to the User class in your master application (and define a master).
112
+
113
+
114
+ ### Routing in the master
115
+
116
+ For most purposese you only need to mount coca:
117
+
118
+ mount Coca::Engine => "/coca", :as => :coca
119
+
120
+ The main api controller response to /coca/authenticate. It's a very ordinary controller that inherits from your ApplicationController and tries to authenticate against your local resources.
121
+
122
+
123
+ ### Data
124
+
125
+ Coca requires all your authenticable resources to have a `uid` column, and it expects your authentication master to set that value and pass it down on auth.
126
+
127
+ In the servant coca requires no devise columns. If you're not trying any other strategies locally, your user model can be very minimal.
128
+
129
+ In the master coca has no other requirements but you will want a fuller set of devise strategies, probably include `:database_authenticatable` and `:token_authenticatable` and all the data columns they require.
130
+
131
+
132
+ ## Extending Coca
133
+
134
+ At heart coca is a simple remote authentication service: credentials are checked remotely and if they are found valid, user information is returned.
135
+
136
+ The data package that we return on successful auth is built by calling `serializable_hash` on the user model in the coca master. As a minimum it should return an auth token, but you can add any other data you like. If you have local resources that are owned by remote users, some kind of uid will be needed. Also permissions, friends lists, recent messages, password replacement instructions: anything it makes sense to hold centrally can be worked out in the master and passed down to all servant applications.
137
+
138
+ Your coca response package will be treated as model attributes. It's up to you to make sure that your local user object does the right thing with whatever is passed down.
139
+
140
+
141
+ ## Reminders and confirmations
142
+
143
+ We're going to need some routing here. Only the auth master is in a position to handle password-reminders and to send confirmation and other messages to new users.
144
+
145
+
146
+ ## Propagating resource changes
147
+
148
+ It's a short step from an auth server to a directory server. Once authentication is centralised, you start to avoid duplicating other user attributes. But what if you want to display the user's email address in one of the servant apps, or offer the user a change of address form while they're working in the calendar? You don't always want to send them somewhere else, or call another server just to get a single attribute.
149
+
150
+
151
+ ## How coca works
152
+
153
+ All the actual authentication done by devise as usual. Coca just adds a way to try out credentials on another server.
154
+
155
+ 1. A coca servant app has its own devise strategies. You can set that up any way you like, and you may choose to accept credentials locally for some users.
156
+
157
+ 2. If the app is presented with an auth token or login pair that it doesn't recognise, those credentials are passed up to the configured master application in a JSON request that includes a predefined API key.
158
+
159
+ 3. The master will check that the requesting host and the secret key match before it gives any response.
160
+
161
+ 4. If the supplied credentials succeed in authenticating the user at the master application, it will return 200 and a package of user information. If the app doesn't recognise the credentials, it may pass them on to its own coca master. If there isn't one, or that request fails too, it will return 401 and no data.
162
+
163
+ 5. As the confirmation package travels down the chain each servant app stores the auth token and does what it likes with any other data that is returned.
164
+
165
+ 6. Subsequent requests that present a valid auth token will be successful at the bottom of the chain.
166
+
167
+ 7. The master will include in the confirmation package a configurable TTL for the auth token. You can use this to set a balance between revokability and responsiveness. Set it to zero for maximum control or to a sensible session length like 30 minutes to minimise the coca overhead.
168
+
169
+ 8. The original servant app receives the confirmation package, saves the authentication token and does whatever it does with the other data. It also puts the auth token in a domain cookie (not the usual session cookie) that will be picked up by any other application in the same domain.
170
+
171
+ 9. When the same browser visits another application in the same domain, it presents the master's auth token cookie. The second servant app passes that token up the chain to the same master. Having issued the token, the master accepts it and returns the same confirmation package as before. The second servant stores the auth token locally so that it too can accept subsequent requests without delegation, token expiry permitting.
172
+
173
+
174
+ ## Compared to OAuth
175
+
176
+ * **This is not an authorization service.** There is no mechanism for issuing API tokens or granting access to individual resources. The chain of command is defined on initialisation and all requests and responses have to come from sources that are already trusted. OAuth makes it possible to have ad-hoc relationships between strangers. Coca is a conversation among friends.
177
+
178
+ * **OAuth is a user-pull scheme** that bounces off the browser a few times and requires it to pass callbacks between servers. The user sees confirmation dialogs from different providers or is forced to visit services in a certain order. Coca works by server-pull, invisible to the user and no more difficult than just logging in. It doesn't matter where you arrive: once you've logged in, you're in.
179
+
180
+ * **Oauth works across domains.** Coca mostly doesn't: you can authenticate against a master at any address, but SSO is limited to domains that can share a cookie. In practice that means coca only provides SSO across related subdomains.
181
+
182
+ * **OAuth is horrible to work with,** absurdly complicated and a daft way to implement SSO unless you are also piggybacking on a remote service. Coca is quite nice.
183
+
184
+
185
+ ## Compared to XAuth
186
+
187
+ * XAuth is just OAuth made stupider. It is superficially similar to coca in that credentials are passed to a remote server, but after that you're bouncing around callbacks again.
188
+
189
+
190
+ ## Compared to LDAP
191
+
192
+ There's nothing wrong with LDAP. If you're working in a situation where an LDAP server is available, and you don't need to layer RBAC or anything else on top, we advise you to use it.
193
+
194
+ If you don't already have an LDAP server with its own availability regime and a dedicated team of lab-coats, you get to choose between several lardy Java services. None of them will sit nicely on your load-balanced cloud node, and none of them are easy to couple with RBAC or other services. In that case you're probably better off using coca and providing a CardDAV service from the top of the chain.
195
+
196
+
197
+ ## Author
198
+
199
+ William Ross for spanner. Will at spanner dot org.
200
+
201
+ ## Copyright
202
+
203
+ Copyright Spanner 2013. All rights reserved.
204
+
205
+ ## License
206
+
207
+ Released under the MIT license.
208
+
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Coca'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,24 @@
1
+ module Coca
2
+ class AuthenticationsController < RocketPants::Base
3
+ include Devise::Controllers::Helpers
4
+
5
+ before_filter :require_valid_servant!
6
+ before_filter :allow_params_authentication!
7
+
8
+ def show
9
+ scope = params[:scope].to_sym
10
+ if user = warden.authenticate(:scope => scope)
11
+ expose user
12
+ else
13
+ head :unauthorized
14
+ end
15
+ end
16
+
17
+ protected
18
+
19
+ def require_valid_servant!
20
+ Coca.valid_servant?(request.remote_ip, params[:key])
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ module Coca
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Coca</title>
5
+ <%= stylesheet_link_tag "coca/application", :media => "all" %>
6
+ <%= javascript_include_tag "coca/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,7 @@
1
+ Rails.application.routes.draw do
2
+ namespace "coca" do
3
+ api :version => 1 do
4
+ get "/check" => "authentications#show", :as => :check
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,62 @@
1
+ require "coca/engine"
2
+ require 'coca/delegate'
3
+ require 'coca/auth_cookie'
4
+ require 'devise/models/cocable'
5
+ require 'devise/strategies/cocable'
6
+
7
+ Warden::Manager.after_set_user do |user, warden, options|
8
+ Coca::AuthCookie.new(warden.cookies, options[:scope]).set(user)
9
+ end
10
+
11
+ Warden::Manager.before_logout do |user, warden, options|
12
+ Coca::AuthCookie.new(warden.cookies, options[:scope]).unset
13
+ end
14
+
15
+ module Coca
16
+ mattr_accessor :masters,
17
+ :servants,
18
+ :cookie_domain,
19
+ :check_source,
20
+ :require_https,
21
+ :propagate_updates,
22
+ :token_ttl,
23
+ :secret,
24
+ :debug
25
+
26
+ @@masters = []
27
+ @@servants = []
28
+ @@cookie_domain = :all
29
+ @@check_source = true
30
+ @@require_https = true
31
+ @@propagate_updates = false
32
+ @@token_ttl = 1800
33
+ @@secret = "Unset"
34
+ @@debug = true
35
+
36
+ class << self
37
+ def add_master(name, &block)
38
+ @@masters.push Coca::Delegate.new(name, &block)
39
+ end
40
+
41
+ def add_servant(name, &block)
42
+ @@servants.push Coca::Delegate.new(name, &block)
43
+ end
44
+
45
+ def valid_servant?(referer, key)
46
+ servants.find { |servant| servant.valid_referer?(referer) && valid_secret?(key) }
47
+ end
48
+
49
+ def valid_master?(referer, key)
50
+ masters.find { |master| master.valid_referer?(referer) && valid_secret?(key) }
51
+ end
52
+
53
+ def valid_secret?(key)
54
+ !!key && !key.blank? && key == Coca.secret
55
+ end
56
+ end
57
+ end
58
+
59
+ Devise.add_module :cocable,
60
+ :route => :session,
61
+ :strategy => true,
62
+ :controller => :sessions
@@ -0,0 +1,77 @@
1
+ require 'signed_json'
2
+
3
+ # Based on the same class in devise-login-cookie by pda
4
+ # https://github.com/pda/devise-login-cookie
5
+
6
+ module Coca
7
+
8
+ class AuthCookie
9
+
10
+ def initialize(cookies, scope)
11
+ @cookies = cookies
12
+ @scope = scope
13
+ end
14
+
15
+ # Sets the cookie, referencing the given resource.id (e.g. User)
16
+ def set(resource)
17
+ @cookies[cookie_name] = cookie_options.merge(:value => encoded_value(resource))
18
+ end
19
+
20
+ # Unsets the cookie via the HTTP response.
21
+ def unset
22
+ @cookies.delete cookie_name, cookie_options
23
+ end
24
+
25
+ def token
26
+ value[0]
27
+ end
28
+
29
+ # The Time at which the cookie was created.
30
+ def created_at
31
+ valid? ? Time.at(value[1]) : nil
32
+ end
33
+
34
+ # Whether the cookie appears valid.
35
+ def valid?
36
+ present? && value.all?
37
+ end
38
+
39
+ def present?
40
+ @cookies[cookie_name].present?
41
+ end
42
+
43
+ # Whether the cookie was set since the given Time
44
+ def set_since?(time)
45
+ created_at && created_at >= time
46
+ end
47
+
48
+ private
49
+
50
+ def value
51
+ begin
52
+ @value = signer.decode @cookies[cookie_name]
53
+ rescue SignedJson::Error
54
+ [nil, nil]
55
+ end
56
+ end
57
+
58
+ def cookie_name
59
+ :"coca_#{@scope}_token"
60
+ end
61
+
62
+ def encoded_value(resource)
63
+ signer.encode [ resource.authentication_token, Time.now.to_i ]
64
+ end
65
+
66
+ def cookie_options
67
+ @session_options ||= Rails.configuration.session_options
68
+ @session_options.slice(:path, :secure, :httponly).merge(:domain => Coca.cookie_domain)
69
+ end
70
+
71
+ def signer
72
+ @signer ||= SignedJson::Signer.new(Coca.secret)
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,62 @@
1
+ require 'resolv'
2
+ require 'httparty'
3
+
4
+ module Coca
5
+ class Delegate
6
+ include HTTParty
7
+ attr_writer :name, :host, :port, :ttl, :protocol
8
+
9
+ def initialize(name='')
10
+ @name = name
11
+ yield self if block_given?
12
+ end
13
+
14
+ def ttl
15
+ @ttl ||= Coca.token_ttl
16
+ end
17
+
18
+ def host
19
+ @host ||= "localhost"
20
+ end
21
+
22
+ def port
23
+ @port
24
+ end
25
+
26
+ def protocol
27
+ @protocol ||= 'https'
28
+ end
29
+
30
+ def base_uri
31
+ [host, port].compact.join(':')
32
+ end
33
+
34
+ def path
35
+ @path ||= "/coca/1/check"
36
+ end
37
+
38
+ def ip_address
39
+ @ip ||= Resolv.new.getaddress(host)
40
+ end
41
+
42
+ def valid_referer?(referer)
43
+ !!referer && !referer.blank? && referer == ip_address
44
+ end
45
+
46
+ def host_url_with_port
47
+ hup = "#{protocol}://#{host}"
48
+ hup << ":#{port}" if port
49
+ hup
50
+ end
51
+
52
+ def url
53
+ @url ||= URI.join(host_url_with_port, path).to_s
54
+ end
55
+
56
+ def authenticate(scope, credentials)
57
+ response = HTTParty.get url, :scope => scope, :"#{scope}" => credentials
58
+ response.body if response.code != 401
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,16 @@
1
+ require 'devise'
2
+ require 'rocket_pants'
3
+
4
+ module Coca
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace Coca
7
+
8
+ config.generators do |g|
9
+ g.test_framework :rspec, :fixture => false
10
+ g.fixture_replacement :factory_girl, :dir => 'spec/factories'
11
+ g.assets false
12
+ g.helper false
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module Coca
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,11 @@
1
+ require 'devise/strategies/cocable'
2
+
3
+ module Devise
4
+ module Models
5
+ module Cocable
6
+ extend ActiveSupport::Concern
7
+
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,56 @@
1
+ require 'devise/strategies/authenticatable'
2
+
3
+ module Devise
4
+ module Strategies
5
+
6
+ # The cocable strategy will look for a user in two places:
7
+ # in a domain cookie set by one of our peers and verified by an upstream app
8
+ # or in credentials supplied to us and verified by an upstream app
9
+ #
10
+ # (If the credentials matched locally, we wouldn't usually get to this strategy)
11
+
12
+ class Cocable < Authenticatable
13
+ def valid?
14
+ valid_for_params_auth? || cookie.valid?
15
+ end
16
+
17
+ def authenticate!
18
+ resource = nil
19
+ response = nil
20
+
21
+ if authentication_hash
22
+ response = delegate(authentication_hash)
23
+ else
24
+ response = delegate({:auth_token => cookie.token})
25
+ end
26
+
27
+ if response
28
+ resource = mapping.to.where(:uid => response[:uid]).first_or_create
29
+ resource.update_attributes(response.except(:uid))
30
+ end
31
+
32
+ success!(resource) if resource# && validate(resource)
33
+ end
34
+
35
+ def delegate(credentials)
36
+ response = nil
37
+ Coca.masters.each do |master|
38
+ response = master.authenticate(scope, credentials)
39
+ break if response
40
+ end
41
+ response
42
+ end
43
+
44
+ private
45
+ # AuthCookie takes care of the naming and signing of cookies for each warden scope.
46
+ # All we need is the token contained in the cookie for this scope, if there is one.
47
+ #
48
+ def cookie
49
+ @cookie ||= Coca::AuthCookie.new(cookies, scope)
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+
56
+ Warden::Strategies.add(:cocable, Devise::Strategies::Cocable)
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :coca do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,256 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: coca
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - William Ross
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.13
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 3.2.13
30
+ - !ruby/object:Gem::Dependency
31
+ name: jquery-rails
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: devise
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rocket_pants
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: signed_json
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: mysql2
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rspec-rails
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: factory_girl_rails
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: simplecov
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: database_cleaner
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: factory_girl_rails
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ - !ruby/object:Gem::Dependency
191
+ name: shoulda-matchers
192
+ requirement: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ! '>='
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ type: :development
199
+ prerelease: false
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ! '>='
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ description: Coca is a chainable, devise-based scheme for delegation of authentication.
207
+ It works through a standard JSON API so the packet that you pass down on auth is
208
+ completely configurable. See coca-rbac for a similarly transparent RBAC implementation.
209
+ email:
210
+ - will@spanner.org
211
+ executables: []
212
+ extensions: []
213
+ extra_rdoc_files: []
214
+ files:
215
+ - app/assets/javascripts/coca/application.js
216
+ - app/assets/stylesheets/coca/application.css
217
+ - app/controllers/coca/authentications_controller.rb
218
+ - app/helpers/coca/application_helper.rb
219
+ - app/views/layouts/coca/application.html.erb
220
+ - config/routes.rb
221
+ - lib/coca/auth_cookie.rb
222
+ - lib/coca/delegate.rb
223
+ - lib/coca/engine.rb
224
+ - lib/coca/version.rb
225
+ - lib/coca.rb
226
+ - lib/devise/models/cocable.rb
227
+ - lib/devise/strategies/cocable.rb
228
+ - lib/tasks/coca_tasks.rake
229
+ - MIT-LICENSE
230
+ - Rakefile
231
+ - README.md
232
+ homepage: https://github.com/spanner/coca
233
+ licenses: []
234
+ post_install_message:
235
+ rdoc_options: []
236
+ require_paths:
237
+ - lib
238
+ required_ruby_version: !ruby/object:Gem::Requirement
239
+ none: false
240
+ requirements:
241
+ - - ! '>='
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ required_rubygems_version: !ruby/object:Gem::Requirement
245
+ none: false
246
+ requirements:
247
+ - - ! '>='
248
+ - !ruby/object:Gem::Version
249
+ version: '0'
250
+ requirements: []
251
+ rubyforge_project:
252
+ rubygems_version: 1.8.23
253
+ signing_key:
254
+ specification_version: 3
255
+ summary: Lightweight, simple SSO for rails.
256
+ test_files: []