authify-api 0.0.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -0
- data/Gemfile +3 -0
- data/README.md +154 -6
- data/Rakefile +20 -0
- data/authify-api.gemspec +4 -2
- data/config.ru +3 -2
- data/db/migrate/20170201220029_create_api_keys.rb +2 -2
- data/db/migrate/20170208021933_add_admin_to_user.rb +9 -0
- data/db/migrate/20170208022427_set_default_for_user_admin.rb +7 -0
- data/db/schema.rb +8 -6
- data/lib/authify/api.rb +6 -4
- data/lib/authify/api/controllers/apikey.rb +50 -0
- data/lib/authify/api/controllers/group.rb +34 -6
- data/lib/authify/api/controllers/identity.rb +49 -0
- data/lib/authify/api/controllers/organization.rb +57 -7
- data/lib/authify/api/controllers/user.rb +54 -15
- data/lib/authify/api/helpers/api_user.rb +16 -1
- data/lib/authify/api/helpers/jwt_encryption.rb +21 -8
- data/lib/authify/api/models/{api_key.rb → apikey.rb} +16 -2
- data/lib/authify/api/models/user.rb +2 -4
- data/lib/authify/api/serializers/{api_key_serializer.rb → apikey_serializer.rb} +7 -0
- data/lib/authify/api/serializers/group_serializer.rb +2 -0
- data/lib/authify/api/serializers/identity_serializer.rb +14 -0
- data/lib/authify/api/serializers/organization_serializer.rb +8 -0
- data/lib/authify/api/serializers/user_serializer.rb +4 -1
- data/lib/authify/api/services/api.rb +28 -33
- data/lib/authify/api/services/jwt_provider.rb +30 -8
- data/lib/authify/api/services/registration.rb +63 -0
- data/lib/authify/api/version.rb +2 -2
- metadata +46 -15
- data/lib/authify/api/controllers/api_key.rb +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0f77e5a23fef6d16a82c095c2f76b9840dcf33f
|
4
|
+
data.tar.gz: c77e75b1605e0ea51c1bd589b47e9ca859735ba1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb9a4ed3c1c41e5e3ef207521fcc33d1fe50177eafdfb4037702ae0bf81a941b48d81b266230339a71cdf2e896616325d009f08f656f1a869fa4220588edee63
|
7
|
+
data.tar.gz: 0afea4d9d48418bae6f32d597f15c440259c49e84da4d4403dfdb41ca30e7d3b6d0102f7b5e01f98aba9f40f9cd332b8f1f3b9cd065c970a4f86b1274207e110
|
data/.rubocop.yml
CHANGED
@@ -13,10 +13,15 @@ Metrics/MethodLength:
|
|
13
13
|
Metrics/CyclomaticComplexity:
|
14
14
|
Max: 10
|
15
15
|
|
16
|
+
Metrics/PerceivedComplexity:
|
17
|
+
Max: 9
|
18
|
+
|
16
19
|
BlockLength:
|
20
|
+
Max: 30
|
17
21
|
Exclude:
|
18
22
|
- '*.gemspec'
|
19
23
|
- 'lib/authify/api/controllers/*.rb'
|
24
|
+
- Rakefile
|
20
25
|
|
21
26
|
AllCops:
|
22
27
|
Exclude:
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -19,12 +19,14 @@ The Authify API service consists of a database for storing:
|
|
19
19
|
|
20
20
|
Nearly all API endpoints available via Authify implement the [{json:api}](http://jsonapi.org/) 1.0 specification. The exceptions are:
|
21
21
|
|
22
|
-
* `GET /jwt/key` - Returns Content Type: `application/
|
23
|
-
* `
|
22
|
+
* `GET /jwt/key` - Returns Content Type: `application/json`. This endpoint returns a JSON Object with the key `data` whose value is a PEM-encoded ECDSA public key, which should be used to verify the signature made by the Authify service.
|
23
|
+
* `GET /jwt/meta` - Returns Content Type: `application/json`. This endpoint returns a JSON Object with the keys `algorithm`, `issuer`, and `expiration` that describe the kind of JWTs produced by this service.
|
24
|
+
* `POST /jwt/token` - Returns (and only accepts) Content Type: `application/json`. This endpoint is used to obtain a [JWT](https://en.wikipedia.org/wiki/JSON_Web_Token). This endpoint expects a JSON Object with either the keys `access_key` and `secret_key` _OR_ `email` and `password`. There is no firm requirement to use either pair for any particular purpose, but for scenarios where the credentials may be stored, the `access_key` and `secret_key` may be used since those can easily be revoked if necessary. Upon successful authentication, the endpoint provides a JSON Object with the key `jwt` and a signed JWT. There should be nothing highly sensitive embedded in the JWT. The JWT defaults to expiring every 15 minutes.
|
25
|
+
* `POST /registration/signup` - Returns (and only accepts) Content Type: `application/json`. This endpoint is used to signup for an account with Authify. This endpoint expects a JSON Object, requiring the keys `email` and `password`, with `name` and `via` being optional. If `via` is provided, then it must be a JSON Object with the keys `provider` and `uid`, otherwise it will be ignored. The `via` key is used to add an alternate identity (meaning they logged-in through an integration, like Github). This endpoint returns a JSON Object with the keys `id`, `email`, and `jwt` on success.
|
24
26
|
|
25
27
|
All other endpoints adhere to the {json:api} specification and can be found at the following base paths:
|
26
28
|
|
27
|
-
* `/
|
29
|
+
* `/apikeys` - User API keys. Index is restricted. Should only really be useful for users manipulating their own keys.
|
28
30
|
* `/groups` - Groups. Index is restricted. Most interactions with groups should be scoped via organizations.
|
29
31
|
* `/identities` - Alternate User Identities. These are other services that the user can login via (web UI only).
|
30
32
|
* `/organizations` - Organizations. These are high-level groupings of users and groups. Non-administrators should only be able to see limited amounts of information about organizations.
|
@@ -57,12 +59,158 @@ Or install it yourself as:
|
|
57
59
|
The Authify API services supports the following configuration settings, managed via environment variables of the same name:
|
58
60
|
|
59
61
|
* `AUTHIFY_DB_URL` - The URL used by [ActiveRecord](http://guides.rubyonrails.org/configuring.html#configuring-a-database) to connect to the database. Currently supports `mysql2://` or `sqlite3://` URLs, though any driver supported by ActiveRecord should work if the required gems are installed. Defaults to `mysql2://root@localhost:3306/authifydb`.
|
60
|
-
* `AUTHIFY_PUBKEY_PATH` - The path on the filesystem to the PEM-encoded, public ECDSA key.
|
61
|
-
* `AUTHIFY_PRIVKEY_PATH` - The path on the filesystem to the PEM-encoded, private ECDSA key. Currently, Authify only supports
|
62
|
-
* `AUTHIFY_JWT_ISSUER` - The name of the issuer ([iss field](https://en.wikipedia.org/wiki/JSON_Web_Token#Standard_fields)) used when creating the JWT. This **must** match on any service that verifies the JWT (meaning any service relying on Authify for authentication).
|
62
|
+
* `AUTHIFY_PUBKEY_PATH` - The path on the filesystem to the PEM-encoded, public ECDSA key. Defaults to `~/.authify/ssl/public.pem`.
|
63
|
+
* `AUTHIFY_PRIVKEY_PATH` - The path on the filesystem to the PEM-encoded, private ECDSA key. Currently, Authify only supports an [ECDSA](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm) keys. Options include using a `secp521r1` curve and the [SHA-512](https://en.wikipedia.org/wiki/SHA-2) hashing algorithm (called `ES512`), a `secp384r1` curve and the SHA-384 hashing algorithm (called `ES384`), or a `prime256v1` curve and the SHA-256 hashing algorithm (called `ES256`). See `AUTHIFY_JWT_ALGORITHM` below for information on how to configure Authify's algorithm to match the public and private keys you provide. The keys you specify **must** match the ECDSA algortihm and curve used to create them.
|
64
|
+
* `AUTHIFY_JWT_ISSUER` - The name of the issuer ([iss field](https://en.wikipedia.org/wiki/JSON_Web_Token#Standard_fields)) used when creating the JWT. This **must** match on any service that verifies the JWT (meaning any service relying on Authify for authentication), and it **must** be the same for all services that integrate with Authify.
|
65
|
+
* `AUTHIFY_JWT_ALGORITHM` - The name of the [JWA](https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40) algorithm to use when loading keys and creating or verifying JWT signatures. Valid values are `ES256`, `ES384`, or `ES512`. Defaults to `ES512`. This **must** match the curve and algorithm used to produce the public and private keys found at `AUTHIFY_PUBKEY_PATH` and `AUTHIFY_PRIVKEY_PATH`, respectively. Note that the curves `prime256v1` (also called NIST P-256) used by `ES256` and `secp384r1` (also called NIST P-384) used by `ES384`, while offering a wider range of compatible SSL libraries, are described as unsafe on [SafeCurves](https://safecurves.cr.yp.to/) for several reasons described there.
|
66
|
+
* `AUTHIFY_JWT_EXPIRATION` - How long should a JWT be valid (in minutes). Defaults to 15. Too small of a value will mean a lot more requests to the API; too high increases the possibility of viable keys being captured.
|
63
67
|
|
64
68
|
## Usage and Authentication Workflow
|
65
69
|
|
70
|
+
### Generating an SSL Certificate
|
71
|
+
|
72
|
+
Here is an example in Ruby for generating an SSL cert for use with the Authify API server:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
require 'openssl'
|
76
|
+
# Using ES512. For others, switch 'secp512r1' to the desired curve
|
77
|
+
secret_key = OpenSSL::PKey::EC.new('secp521r1')
|
78
|
+
secret_key.generate_key
|
79
|
+
# write out the private key to a file...
|
80
|
+
File.write(File.expand_path('/path/to/keys/private.pem'), secret_key.to_pem)
|
81
|
+
public_key = secret_key
|
82
|
+
public_key.private_key = nil
|
83
|
+
# write out the public key to a file...
|
84
|
+
File.write(File.expand_path('/path/to/keys/public.pem'), private_key.to_pem)
|
85
|
+
```
|
86
|
+
|
87
|
+
Using the OpenSSL CLI tool:
|
88
|
+
|
89
|
+
```shell
|
90
|
+
# Private key
|
91
|
+
openssl ecparam -name secp521r1 -genkey -out /path/to/keys/private.pem
|
92
|
+
# Public key
|
93
|
+
openssl ec -in /path/to/keys/private.pem -pubout -out /path/to/keys/public.pem
|
94
|
+
```
|
95
|
+
|
96
|
+
### Authenticating for API clients
|
97
|
+
|
98
|
+
We'll show how to interact with the API using `curl` as an example, and we'll assume the server is running at `auth.mycompany.com`.
|
99
|
+
|
100
|
+
#### Register a new user
|
101
|
+
|
102
|
+
```shell
|
103
|
+
curl \
|
104
|
+
-H 'Content-Type: application/json' \
|
105
|
+
-H 'Accept: application/json' \
|
106
|
+
--data \
|
107
|
+
'{
|
108
|
+
"name": "Some User",
|
109
|
+
"email": "someuser@mycompany.com",
|
110
|
+
"password": "b@d!dea",
|
111
|
+
"via": {
|
112
|
+
"provider": "github",
|
113
|
+
"uid": "1234567"
|
114
|
+
}
|
115
|
+
}' \
|
116
|
+
https://auth.mycompany.com/registration/signup
|
117
|
+
```
|
118
|
+
|
119
|
+
This will return JSON similar to the following:
|
120
|
+
|
121
|
+
```javascript
|
122
|
+
{
|
123
|
+
"id": 172,
|
124
|
+
"email": "someuser@mycompany.com",
|
125
|
+
"jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJleHAiOjE0ODY0ODcyODcsImlhdCI6MTQ4NjQ4MzY4NywiaXNzIjoiTXkgQXdlc29tZSBDb21wYW55IEluYy4iLCJzY29wZXMiOlsidXNlcl9hY2Nlc3MiXSwidXNlciI6eyJ1c2VybmFtZSI6ImZvb0BiYXIuY29tIiwidWlkIjoyLCJvcmdhbml6YXRpb25zIjpbXSwiZ3JvdXBzIjpbXX19.AWfPpKX9mP03Djz3-LMneJdEVsXQm_4GOPVCdkfiiBeIR4pVLKTVrNoNdlNgSEkZEeUw1RPsVxpAR7wDgB4cNcYiAP3fNaD8OPyWfOQAV0lTvDUSH3YU39cZAVwvbX9HleOHBLrFGBbui5wSvfi7WZZlH808psiuUAVhBOe7mfrNiHGB"
|
126
|
+
}
|
127
|
+
```
|
128
|
+
|
129
|
+
You'll need the JWT (found at key `jwt`) for the next step.
|
130
|
+
|
131
|
+
#### Create an API key set
|
132
|
+
|
133
|
+
```shell
|
134
|
+
curl \
|
135
|
+
-H 'Content-Type: application/vnd.api+json' \
|
136
|
+
-H 'Accept: application/vnd.api+json' \
|
137
|
+
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJleHAiOjE0ODY0ODcyODcsImlhdCI6MTQ4NjQ4MzY4NywiaXNzIjoiTXkgQXdlc29tZSBDb21wYW55IEluYy4iLCJzY29wZXMiOlsidXNlcl9hY2Nlc3MiXSwidXNlciI6eyJ1c2VybmFtZSI6ImZvb0BiYXIuY29tIiwidWlkIjoyLCJvcmdhbml6YXRpb25zIjpbXSwiZ3JvdXBzIjpbXX19.AWfPpKX9mP03Djz3-LMneJdEVsXQm_4GOPVCdkfiiBeIR4pVLKTVrNoNdlNgSEkZEeUw1RPsVxpAR7wDgB4cNcYiAP3fNaD8OPyWfOQAV0lTvDUSH3YU39cZAVwvbX9HleOHBLrFGBbui5wSvfi7WZZlH808psiuUAVhBOe7mfrNiHGB" \
|
138
|
+
--data \
|
139
|
+
'{
|
140
|
+
"data":
|
141
|
+
{
|
142
|
+
"type": "apikeys"
|
143
|
+
}
|
144
|
+
}' \
|
145
|
+
https://auth.mycompany.com/apikeys
|
146
|
+
```
|
147
|
+
|
148
|
+
This endpoint (as can be seen from the `Accept` and `Content-Type` headers) speaks only {json:api} and will return something like this with an HTTP 201:
|
149
|
+
|
150
|
+
```javascript
|
151
|
+
{
|
152
|
+
"data": {
|
153
|
+
"type": "apikeys",
|
154
|
+
"id": "197",
|
155
|
+
"attributes": {
|
156
|
+
"access-key": "4bb651af1754b2dff5b9",
|
157
|
+
"secret-key": "a3f1ee5085dad87d53ce04a1857a2677c7ffa136c506e8174fef6fa1c962e46f",
|
158
|
+
"created-at": "2017-02-13 22:50:44 UTC"
|
159
|
+
},
|
160
|
+
"links": {
|
161
|
+
"self": "/apikeys/197"
|
162
|
+
},
|
163
|
+
"relationships": {
|
164
|
+
"user": {
|
165
|
+
"links": {
|
166
|
+
"self": "/apikeys/197/relationships/user",
|
167
|
+
"related": "/apikeys/197/user"
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
},
|
172
|
+
"jsonapi": {
|
173
|
+
"version": "1.0"
|
174
|
+
},
|
175
|
+
"included": [
|
176
|
+
|
177
|
+
]
|
178
|
+
}
|
179
|
+
```
|
180
|
+
|
181
|
+
Note that **it will not be possible to retrieve the `secret-key` attribute in plaintext again**, so store the results in a safe place.
|
182
|
+
|
183
|
+
#### Obtain a JWT
|
184
|
+
|
185
|
+
```shell
|
186
|
+
curl \
|
187
|
+
-H 'Accept: application/json' \
|
188
|
+
-H 'Content-Type: application/json' \
|
189
|
+
--data \
|
190
|
+
'{
|
191
|
+
"access_key": "5f4abd1c6423ef02d1ec42e1cddaf5f8",
|
192
|
+
"secret_key": "fb97aa7d4e48f3e4bbb2930161a423fa8308393426c3612940da03f22cf36879"
|
193
|
+
}' \
|
194
|
+
https://auth.mycompany.com/jwt/token
|
195
|
+
```
|
196
|
+
|
197
|
+
Note that you can also use either the underscored format for logging in with API keys (`access_key` and `secret_key`) or the dashed version provided in the {json:api} response before (`access-key` and `secret-key`). For all other endpoints (those adhering to the {json:api} spec) the dashed approach is required.
|
198
|
+
|
199
|
+
The server will return something like:
|
200
|
+
|
201
|
+
```javascript
|
202
|
+
{"jwt":"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJleHAiOjE0ODY0ODcyODcsImlhdCI6MTQ4NjQ4MzY4NywiaXNzIjoiTXkgQXdlc29tZSBDb21wYW55IEluYy4iLCJzY29wZXMiOlsidXNlcl9hY2Nlc3MiXSwidXNlciI6eyJ1c2VybmFtZSI6ImZvb0BiYXIuY29tIiwidWlkIjoyLCJvcmdhbml6YXRpb25zIjpbXSwiZ3JvdXBzIjpbXX19.AWfPpKX9mP03Djz3-LMneJdEVsXQm_4GOPVCdkfiiBeIR4pVLKTVrNoNdlNgSEkZEeUw1RPsVxpAR7wDgB4cNcYiAP3fNaD8OPyWfOQAV0lTvDUSH3YU39cZAVwvbX9HleOHBLrFGBbui5wSvfi7WZZlH808psiuUAVhBOe7mfrNiHGB"}
|
203
|
+
```
|
204
|
+
|
205
|
+
#### Use the JWT to Access a Protected Resource
|
206
|
+
|
207
|
+
```shell
|
208
|
+
curl \
|
209
|
+
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJleHAiOjE0ODY0ODcyODcsImlhdCI6MTQ4NjQ4MzY4NywiaXNzIjoiTXkgQXdlc29tZSBDb21wYW55IEluYy4iLCJzY29wZXMiOlsidXNlcl9hY2Nlc3MiXSwidXNlciI6eyJ1c2VybmFtZSI6ImZvb0BiYXIuY29tIiwidWlkIjoyLCJvcmdhbml6YXRpb25zIjpbXSwiZ3JvdXBzIjpbXX19.AWfPpKX9mP03Djz3-LMneJdEVsXQm_4GOPVCdkfiiBeIR4pVLKTVrNoNdlNgSEkZEeUw1RPsVxpAR7wDgB4cNcYiAP3fNaD8OPyWfOQAV0lTvDUSH3YU39cZAVwvbX9HleOHBLrFGBbui5wSvfi7WZZlH808psiuUAVhBOe7mfrNiHGB" \
|
210
|
+
-H 'Accept: application/vnd.api+json' \
|
211
|
+
https://auth.mycompany.com/organizations
|
212
|
+
```
|
213
|
+
|
66
214
|
## Contributing
|
67
215
|
|
68
216
|
Bug reports and pull requests are welcome on GitHub at https://github.com/knuedge/authify-api.
|
data/Rakefile
CHANGED
@@ -35,4 +35,24 @@ namespace :delegate do
|
|
35
35
|
exit 1
|
36
36
|
end
|
37
37
|
end
|
38
|
+
|
39
|
+
desc 'List Trusted Delegates'
|
40
|
+
task :list do
|
41
|
+
require 'authify/api'
|
42
|
+
Authify::API::Models::TrustedDelegate.all.each do |td|
|
43
|
+
p(name: td.name, access: td.access_key)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
desc 'Delete a Trusted Delegate'
|
48
|
+
task :remove, [:name] do |_t, args|
|
49
|
+
require 'authify/api'
|
50
|
+
td = Authify::API::Models::TrustedDelegate.find_by_name(args[:name])
|
51
|
+
if td && td.destroy
|
52
|
+
puts 'Trusted Delegate destroyed'
|
53
|
+
else
|
54
|
+
puts 'Failed to destroy Trusted Delegate'
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
end
|
38
58
|
end
|
data/authify-api.gemspec
CHANGED
@@ -25,13 +25,15 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_runtime_dependency 'authify-core'
|
26
26
|
spec.add_runtime_dependency 'authify-middleware'
|
27
27
|
spec.add_runtime_dependency 'connection_pool', '~> 2.2'
|
28
|
-
spec.add_runtime_dependency 'sinatra', '
|
28
|
+
spec.add_runtime_dependency 'sinatra', '>= 2.0.0.beta2', '< 3'
|
29
|
+
spec.add_runtime_dependency 'sinatra-contrib', '>= 2.0.0.beta2', '< 3'
|
29
30
|
spec.add_runtime_dependency 'sinatra-activerecord', '~> 2.0'
|
30
31
|
spec.add_runtime_dependency 'moneta', '~> 0.8'
|
31
32
|
spec.add_runtime_dependency 'mysql2', '~> 0.4'
|
32
33
|
spec.add_runtime_dependency 'sqlite3', '~> 1.3'
|
34
|
+
spec.add_runtime_dependency 'json', '~> 2.0'
|
33
35
|
spec.add_runtime_dependency 'jsonapi-serializers', '~> 0.16'
|
34
|
-
spec.add_runtime_dependency 'sinja', '~> 1.2'
|
36
|
+
# spec.add_runtime_dependency 'sinja', '~> 1.2', '>= 1.2.4'
|
35
37
|
spec.add_runtime_dependency 'puma', '~> 3.7'
|
36
38
|
|
37
39
|
spec.add_development_dependency 'bundler', '~> 1.12'
|
data/config.ru
CHANGED
@@ -5,5 +5,6 @@ require 'authify/api'
|
|
5
5
|
use Rack::ShowExceptions
|
6
6
|
|
7
7
|
run Rack::URLMap.new \
|
8
|
-
'/'
|
9
|
-
'/jwt'
|
8
|
+
'/' => Authify::API::Services::API.new,
|
9
|
+
'/jwt' => Authify::API::Services::JWTProvider.new,
|
10
|
+
'/registration' => Authify::API::Services::Registration.new
|
@@ -1,7 +1,7 @@
|
|
1
|
-
# Creates the
|
1
|
+
# Creates the apikeys table
|
2
2
|
class CreateApiKeys < ActiveRecord::Migration[5.0]
|
3
3
|
def change
|
4
|
-
create_table :
|
4
|
+
create_table :apikeys do |t|
|
5
5
|
t.references :user, index: true
|
6
6
|
t.string :access_key, index: true
|
7
7
|
t.text :secret_key_digest
|
data/db/schema.rb
CHANGED
@@ -10,16 +10,16 @@
|
|
10
10
|
#
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
|
-
ActiveRecord::Schema.define(version:
|
13
|
+
ActiveRecord::Schema.define(version: 20170208022427) do
|
14
14
|
|
15
|
-
create_table "
|
15
|
+
create_table "apikeys", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
16
16
|
t.integer "user_id"
|
17
17
|
t.string "access_key"
|
18
18
|
t.text "secret_key_digest", limit: 65535
|
19
19
|
t.datetime "created_at", null: false
|
20
20
|
t.datetime "updated_at", null: false
|
21
|
-
t.index ["access_key"], name: "
|
22
|
-
t.index ["user_id"], name: "
|
21
|
+
t.index ["access_key"], name: "index_apikeys_on_access_key", using: :btree
|
22
|
+
t.index ["user_id"], name: "index_apikeys_on_user_id", using: :btree
|
23
23
|
end
|
24
24
|
|
25
25
|
create_table "groups", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
|
@@ -87,8 +87,10 @@ ActiveRecord::Schema.define(version: 20170204001405) do
|
|
87
87
|
t.string "email"
|
88
88
|
t.text "password_digest", limit: 65535
|
89
89
|
t.string "full_name"
|
90
|
-
t.datetime "created_at",
|
91
|
-
t.datetime "updated_at",
|
90
|
+
t.datetime "created_at", null: false
|
91
|
+
t.datetime "updated_at", null: false
|
92
|
+
t.boolean "admin", default: false, null: false
|
93
|
+
t.index ["admin"], name: "index_users_on_admin", using: :btree
|
92
94
|
t.index ["email"], name: "index_users_on_email", using: :btree
|
93
95
|
end
|
94
96
|
|
data/lib/authify/api.rb
CHANGED
@@ -18,7 +18,6 @@ module Authify
|
|
18
18
|
db: {
|
19
19
|
url: ENV['AUTHIFY_DB_URL'] || 'mysql2://root@localhost:3306/authifydb'
|
20
20
|
},
|
21
|
-
session_secret: ENV['AUTHIFY_SESSION_SECRET'] || '1q2w3e4r5t6y7u8i9o0pazsxdcfvgbhnjmkl10zm',
|
22
21
|
redis: {
|
23
22
|
host: ENV['AUTHIFY_REDIS_HOST'] || 'localhost',
|
24
23
|
port: ENV['AUTHIFY_REDIS_PORT'] || '6379'
|
@@ -29,15 +28,17 @@ end
|
|
29
28
|
|
30
29
|
require 'authify/api/version'
|
31
30
|
require 'authify/api/jsonapi_utils'
|
32
|
-
require 'authify/api/controllers/
|
31
|
+
require 'authify/api/controllers/apikey'
|
33
32
|
require 'authify/api/controllers/group'
|
33
|
+
require 'authify/api/controllers/identity'
|
34
34
|
require 'authify/api/controllers/organization'
|
35
35
|
require 'authify/api/controllers/user'
|
36
|
-
require 'authify/api/serializers/
|
36
|
+
require 'authify/api/serializers/apikey_serializer'
|
37
37
|
require 'authify/api/serializers/group_serializer'
|
38
|
+
require 'authify/api/serializers/identity_serializer'
|
38
39
|
require 'authify/api/serializers/user_serializer'
|
39
40
|
require 'authify/api/serializers/organization_serializer'
|
40
|
-
require 'authify/api/models/
|
41
|
+
require 'authify/api/models/apikey'
|
41
42
|
require 'authify/api/models/group'
|
42
43
|
require 'authify/api/models/identity'
|
43
44
|
require 'authify/api/models/organization'
|
@@ -49,3 +50,4 @@ require 'authify/api/helpers/api_user'
|
|
49
50
|
require 'authify/api/service'
|
50
51
|
require 'authify/api/services/api'
|
51
52
|
require 'authify/api/services/jwt_provider'
|
53
|
+
require 'authify/api/services/registration'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Authify
|
2
|
+
module API
|
3
|
+
module Controllers
|
4
|
+
APIKey = proc do
|
5
|
+
helpers do
|
6
|
+
def find(id)
|
7
|
+
Models::APIKey.find(id.to_i)
|
8
|
+
end
|
9
|
+
|
10
|
+
def role
|
11
|
+
Array(super).tap do |a|
|
12
|
+
a << :myself if current_user && current_user.apikeys.include?(resource)
|
13
|
+
end.uniq
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
index(roles: [:admin]) do
|
18
|
+
Models::APIKey.all
|
19
|
+
end
|
20
|
+
|
21
|
+
show(roles: [:myself, :admin]) do
|
22
|
+
last_modified resource.updated_at
|
23
|
+
next resource, exclude: [:secret_key]
|
24
|
+
end
|
25
|
+
|
26
|
+
create(roles: [:user]) do |_attributes|
|
27
|
+
key = Models::APIKey.new
|
28
|
+
key.populate!
|
29
|
+
current_user.apikeys << key
|
30
|
+
current_user.save
|
31
|
+
next key.id, key
|
32
|
+
end
|
33
|
+
|
34
|
+
destroy(roles: [:myself, :admin]) do
|
35
|
+
resource.destroy
|
36
|
+
end
|
37
|
+
|
38
|
+
show_many do |ids|
|
39
|
+
Models::APIKey.find(ids)
|
40
|
+
end
|
41
|
+
|
42
|
+
has_one :user do
|
43
|
+
pluck(roles: [:myself, :admin]) do
|
44
|
+
resource.user
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -6,22 +6,31 @@ module Authify
|
|
6
6
|
def find(id)
|
7
7
|
Models::Group.find(id.to_i)
|
8
8
|
end
|
9
|
+
|
10
|
+
def role
|
11
|
+
Array(super).tap do |a|
|
12
|
+
a << :owner if current_user && current_user.admin_for?(resource.organization)
|
13
|
+
a << :member if resource && resource.users.include?(current_user)
|
14
|
+
end.uniq
|
15
|
+
end
|
9
16
|
end
|
10
17
|
|
11
|
-
index do
|
18
|
+
index(roles: [:admin]) do
|
12
19
|
Models::Group.all
|
13
20
|
end
|
14
21
|
|
15
|
-
show do
|
22
|
+
show(roles: [:admin, :owner]) do
|
16
23
|
last_modified resource.updated_at
|
17
24
|
next resource
|
18
25
|
end
|
19
26
|
|
20
|
-
create do |attrs|
|
21
|
-
Models::Group.new(attrs)
|
27
|
+
create(roles: [:admin]) do |attrs|
|
28
|
+
g = Models::Group.new(attrs)
|
29
|
+
g.save
|
30
|
+
next g
|
22
31
|
end
|
23
32
|
|
24
|
-
destroy do
|
33
|
+
destroy(roles: [:admin, :owner]) do
|
25
34
|
resource.destroy
|
26
35
|
end
|
27
36
|
|
@@ -30,9 +39,28 @@ module Authify
|
|
30
39
|
end
|
31
40
|
|
32
41
|
has_many :users do
|
33
|
-
fetch do
|
42
|
+
fetch(roles: [:admin, :owner]) do
|
34
43
|
resource.users
|
35
44
|
end
|
45
|
+
|
46
|
+
replace(roles: [:admin, :owner]) do |rios|
|
47
|
+
refs = rios.map { |attrs| Models::User.find(attrs) }
|
48
|
+
resource.users = refs
|
49
|
+
resource.save
|
50
|
+
end
|
51
|
+
|
52
|
+
merge(roles: [:admin, :owner]) do |rios|
|
53
|
+
refs = rios.map { |attrs| Models::User.find(attrs) }
|
54
|
+
resource.users << refs
|
55
|
+
resource.save
|
56
|
+
end
|
57
|
+
|
58
|
+
subtract(roles: [:admin, :owner]) do |rios|
|
59
|
+
refs = rios.map { |attrs| Models::User.find(attrs) }
|
60
|
+
# This only removes the linkage, not the actual users
|
61
|
+
resource.users.delete(refs)
|
62
|
+
resource.save
|
63
|
+
end
|
36
64
|
end
|
37
65
|
end
|
38
66
|
end
|