authify-api 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 836953fab50ba0ca070a7556a2d23a20951d5d49
4
- data.tar.gz: 8923b8d048285d3dc09440fa598855d174e4a87d
3
+ metadata.gz: 5033995f7f6ff20f2149dc9f334c65b05448f4ae
4
+ data.tar.gz: 91c04c240880c69e414e3798c8233736855208cb
5
5
  SHA512:
6
- metadata.gz: 9aea4b4146a3a8652fee589e2e414f8e144110b3367421815dae87a3a76b4de58c16985fd5dc6616907dfb85788b5cc704bb46be9d00a6e756f49f6e13a4fc3b
7
- data.tar.gz: 73c3b021b747f9cd8690257be62b4c1f4080e3a994c3576e4bfdc61233e732f0863a578a95f876c6894a66e5573e89a994d80ffbacb1a41728ca1a0eec3d7b7e
6
+ metadata.gz: 5892be4a3773859531a761a9d38bc45f1436def5c6e98917e58400806728affc775b1a62a92c946226e7ba611781059fb7b63b77489c5e2ceec4d73e61c8bbaf
7
+ data.tar.gz: e2fa79cc813152f078308a65e4b933389a6097cce821089ce8f9601af6cae39766267a6c450ba1beb9154be40edd4efb2ac8e87d9359e3795321c05334c672d3
@@ -20,7 +20,7 @@ Metrics/PerceivedComplexity:
20
20
  Max: 9
21
21
 
22
22
  Metrics/BlockLength:
23
- Max: 30
23
+ Max: 35
24
24
  Exclude:
25
25
  - '*.gemspec'
26
26
  - 'lib/authify/api/controllers/*.rb'
data/README.md CHANGED
@@ -94,6 +94,9 @@ Alternate User Identities. These are other services that the user can login via
94
94
  **`/organizations`**
95
95
  Organizations. These are high-level groupings of users and groups. Non-administrators should only be able to see limited amounts of information about organizations.
96
96
 
97
+ **`/trusted-delegates`**
98
+ Trusted Delegates. These are heavily-integrated applications that can offload some of the API's functionality (usually getting a user's credentials). All actions on this controller require `admin` access to Authify. See [Trusted Delegates](#trusted_delegates) below for more info.
99
+
97
100
  **`/users`**
98
101
  Users controller.
99
102
 
@@ -101,7 +104,7 @@ Users controller.
101
104
 
102
105
  In addition to expiring JWTs provided via `/jwt/token` for normal user interactions, Trusted Delegates can perform any action by providing the `X-Authify-Access`, `X-Authify-Secret`, and the `X-Authify-On-Behalf-Of` headers. The `Access` and `Secret` headers are used to authenticate the remote application, and the `On-Behalf-Of` is used to impersonate the user (determined through a process on the remote, trusted delegate's end to establish the user's identity).
103
106
 
104
- Note that while these sound similar to User API keys, these Trusted Delegate credentials are longer and can not be interchanged with User API Keys. These values do not expire and are not easily created or removed. For this reason, they should be used **very** sparingly. They can only be created, listed, or removed via a set of `rake` commands run server-side. These are:
107
+ Note that while these sound similar to User API keys, these Trusted Delegate credentials are longer and can not be interchanged with User API Keys. These values do not expire and are not easily created or removed. For this reason, they should be used **very** sparingly. In a pinch, they can be created, listed, or removed via a set of `rake` commands run server-side. These are:
105
108
 
106
109
  * `rake delegate:add[<name>]` - where `<name>` is the unique name of the trusted delegate. For example, `rake delegate:add[foo]` adds a remote delegate named `foo`. This command will output a key / value set providing the access\_key and secret\_key. The secret\_key is stored as a one-way hash in the DB, so it can never be retrieved again.
107
110
  * `rake delegate:list` - lists the names of all trusted delegates along with their access keys.
@@ -201,6 +204,7 @@ This will return JSON similar to the following:
201
204
  {
202
205
  "id": 172,
203
206
  "email": "someuser@mycompany.com",
207
+ "handle": "someuser",
204
208
  "verified": false
205
209
  }
206
210
  ```
@@ -226,6 +230,7 @@ This will return JSON similar to the following:
226
230
  {
227
231
  "id": 172,
228
232
  "email": "someuser@mycompany.com",
233
+ "handle": "someuser",
229
234
  "verified": true,
230
235
  "jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJleHAiOjE0ODY0ODcyODcsImlhdCI6MTQ4NjQ4MzY4NywiaXNzIjoiTXkgQXdlc29tZSBDb21wYW55IEluYy4iLCJzY29wZXMiOlsidXNlcl9hY2Nlc3MiXSwidXNlciI6eyJ1c2VybmFtZSI6ImZvb0BiYXIuY29tIiwidWlkIjoyLCJvcmdhbml6YXRpb25zIjpbXSwiZ3JvdXBzIjpbXX19.AWfPpKX9mP03Djz3-LMneJdEVsXQm_4GOPVCdkfiiBeIR4pVLKTVrNoNdlNgSEkZEeUw1RPsVxpAR7wDgB4cNcYiAP3fNaD8OPyWfOQAV0lTvDUSH3YU39cZAVwvbX9HleOHBLrFGBbui5wSvfi7WZZlH808psiuUAVhBOe7mfrNiHGB"
231
236
  }
@@ -365,7 +370,7 @@ to include a `templates` section like this:
365
370
  }
366
371
  ```
367
372
 
368
- Authify's templating supports something that looks a bit like [Handlebars](http://handlebarsjs.com/) templating (though it doesn't yet support most of the Handlebars features). This is useful for allowing the injection of dynamic data into your templates. Available expressions should be declared in the README section that describes a template-capable endpoint.
373
+ Authify's uses [Liquid](https://shopify.github.io/liquid/) for templating. This is useful for allowing the injection of dynamic data into your templates, and it also supports a robust set of [tags](https://shopify.github.io/liquid/basics/introduction/#tags) for iteration and control flow, as well as [filters](https://shopify.github.io/liquid/basics/introduction/#filters) for manipulating data. Predefined variables and available expressions should be declared in the README section that describes a template-capable endpoint.
369
374
 
370
375
  For some template data, escaping can be difficult or inconvenient. For these situations, Authify supports optional [Base64](https://en.wikipedia.org/wiki/Base64) encoding of values. To provide a Base64-encoded value, just declare it as such using `{base64}` followed by the data:
371
376
 
@@ -38,6 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_runtime_dependency 'puma', '~> 3.7'
39
39
  spec.add_runtime_dependency 'resque', '~> 1.26'
40
40
  spec.add_runtime_dependency 'hitimes', '~> 1.2'
41
+ spec.add_runtime_dependency 'liquid', '~> 4.0'
41
42
 
42
43
  spec.add_development_dependency 'bundler', '~> 1.12'
43
44
  spec.add_development_dependency 'rake', '~> 10.0'
@@ -0,0 +1,12 @@
1
+ # User handle
2
+ class AddHandleToUser < ActiveRecord::Migration[5.0]
3
+ def up
4
+ add_column :users, :handle, :string
5
+ add_index :users, :handle, unique: true
6
+ end
7
+
8
+ def down
9
+ remove_index :users, :handle
10
+ remove_column :users, :handle
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ # Adds a unique index on the group name and org
2
+ class AddUniqueIndexToGroups < ActiveRecord::Migration[5.1]
3
+ def change
4
+ add_index :groups, %i[name organization_id], unique: true
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # Adding a unique index to identities on uid and provider
2
+ class AddUniqueIndexToIdentities < ActiveRecord::Migration[5.1]
3
+ def change
4
+ add_index :identities, %i[uid provider], unique: true
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # Adding a unique index to name on organizations
2
+ class AddUniqueIndexToOrganizations < ActiveRecord::Migration[5.1]
3
+ def change
4
+ add_index :organizations, :name, unique: true, name: :index_organizations_unique_on_name
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # Adding a unique index to email on users
2
+ class AddUniqueIndexToUsers < ActiveRecord::Migration[5.1]
3
+ def change
4
+ add_index :users, :email, unique: true, name: :index_users_unique_on_email
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ # Sets a handle for users without one
2
+ class SetDefaultHandleOnUsers < ActiveRecord::Migration[5.1]
3
+ def up
4
+ Authify::API::Models::User.reset_column_information
5
+ Authify::API::Models::User.all.each do |user|
6
+ tmp_handle = Authify::API::Models::User.uniq_handle_generator(user.full_name, user.email)
7
+ user.handle = tmp_handle unless user.handle
8
+ puts "Setting handle #{user.handle} for User #{user.id}"
9
+ user.save
10
+ end
11
+ end
12
+
13
+ def down
14
+ # nothing to do
15
+ end
16
+ end
@@ -10,7 +10,7 @@
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: 20170609181046) do
13
+ ActiveRecord::Schema.define(version: 20170714051226) do
14
14
 
15
15
  create_table "apikeys", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
16
16
  t.integer "user_id"
@@ -28,6 +28,7 @@ ActiveRecord::Schema.define(version: 20170609181046) do
28
28
  t.text "description"
29
29
  t.datetime "created_at", null: false
30
30
  t.datetime "updated_at", null: false
31
+ t.index ["name", "organization_id"], name: "index_groups_on_name_and_organization_id", unique: true
31
32
  t.index ["name"], name: "index_groups_on_name"
32
33
  t.index ["organization_id"], name: "index_groups_on_organization_id"
33
34
  end
@@ -46,6 +47,7 @@ ActiveRecord::Schema.define(version: 20170609181046) do
46
47
  t.datetime "created_at", null: false
47
48
  t.datetime "updated_at", null: false
48
49
  t.index ["provider"], name: "index_identities_on_provider"
50
+ t.index ["uid", "provider"], name: "index_identities_on_uid_and_provider", unique: true
49
51
  t.index ["uid"], name: "index_identities_on_uid"
50
52
  t.index ["user_id"], name: "index_identities_on_user_id"
51
53
  end
@@ -70,6 +72,7 @@ ActiveRecord::Schema.define(version: 20170609181046) do
70
72
  t.datetime "created_at", null: false
71
73
  t.datetime "updated_at", null: false
72
74
  t.index ["name"], name: "index_organizations_on_name"
75
+ t.index ["name"], name: "index_organizations_unique_on_name", unique: true
73
76
  end
74
77
 
75
78
  create_table "trusted_delegates", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
@@ -92,8 +95,11 @@ ActiveRecord::Schema.define(version: 20170609181046) do
92
95
  t.boolean "admin", default: false, null: false
93
96
  t.boolean "verified"
94
97
  t.string "verification_token"
98
+ t.string "handle"
95
99
  t.index ["admin"], name: "index_users_on_admin"
96
100
  t.index ["email"], name: "index_users_on_email"
101
+ t.index ["email"], name: "index_users_unique_on_email", unique: true
102
+ t.index ["handle"], name: "index_users_on_handle", unique: true
97
103
  t.index ["verified"], name: "index_users_on_verified"
98
104
  end
99
105
 
@@ -13,6 +13,7 @@ require 'connection_pool'
13
13
  require 'moneta'
14
14
  require 'resque'
15
15
  require 'hitimes'
16
+ require 'liquid'
16
17
 
17
18
  # Internal Requirements
18
19
  module Authify
@@ -41,12 +42,14 @@ require 'authify/api/controllers/apikey'
41
42
  require 'authify/api/controllers/group'
42
43
  require 'authify/api/controllers/identity'
43
44
  require 'authify/api/controllers/organization'
45
+ require 'authify/api/controllers/trusted_delegate'
44
46
  require 'authify/api/controllers/user'
45
47
  require 'authify/api/serializers/apikey_serializer'
46
48
  require 'authify/api/serializers/group_serializer'
47
49
  require 'authify/api/serializers/identity_serializer'
48
50
  require 'authify/api/serializers/user_serializer'
49
51
  require 'authify/api/serializers/organization_serializer'
52
+ require 'authify/api/serializers/trusted_delegate_serializer'
50
53
  require 'authify/api/helpers/jwt_encryption'
51
54
  require 'authify/api/helpers/api_user'
52
55
  require 'authify/api/helpers/text_processing'
@@ -0,0 +1,48 @@
1
+ module Authify
2
+ module API
3
+ module Controllers
4
+ TrustedDelegate = proc do
5
+ helpers do
6
+ def find(id)
7
+ Models::TrustedDelegate.find(id.to_i)
8
+ end
9
+
10
+ def modifiable_fields
11
+ %i[name]
12
+ end
13
+
14
+ def filtered_attributes(attributes)
15
+ attributes.select do |k, _v|
16
+ modifiable_fields.include?(k)
17
+ end
18
+ end
19
+ end
20
+
21
+ index(roles: %i[admin]) do
22
+ Models::TrustedDelegate.all
23
+ end
24
+
25
+ show(roles: %i[admin]) do
26
+ last_modified resource.updated_at
27
+ next resource, exclude: [:secret_key]
28
+ end
29
+
30
+ create(roles: %i[admin]) do |attrs|
31
+ key = Models::TrustedDelegate.new filtered_attributes(attrs)
32
+ key.access_key = Models::TrustedDelegate.generate_access_key
33
+ key.set_secret!
34
+ key.save
35
+ next key.id, key
36
+ end
37
+
38
+ destroy(roles: %i[admin]) do
39
+ resource.destroy
40
+ end
41
+
42
+ show_many do |ids|
43
+ Models::TrustedDelegate.find(ids)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -26,17 +26,18 @@ module Authify
26
26
  end
27
27
 
28
28
  def modifiable_fields
29
- %i[full_name email].tap do |a|
29
+ %i[full_name email handle].tap do |a|
30
30
  a << :admin if role.include?(:admin)
31
31
  end
32
32
  end
33
33
 
34
34
  def indexable_fields
35
- %i[full-name admin apikeys groups organizations identities].tap do |a|
35
+ %i[full-name admin handle groups organizations identities].tap do |a|
36
36
  if role?(:admin) || role?(:myself)
37
37
  a << :verified
38
38
  a << :email
39
39
  a << :'created-at'
40
+ a << :apikeys
40
41
  end
41
42
  end
42
43
  end
@@ -11,10 +11,9 @@ module Authify
11
11
  hash.update(hash) { |_, v| v.is_a?(String) ? human_readable(v) : v }
12
12
  end
13
13
 
14
- # Interpolates handlebars-style templates
15
- # OPTIMIZE: this can probably be faster
14
+ # Interpolates handlebars-style (liquid) templates
16
15
  def dehandlebar(text, data = {})
17
- text.gsub(/{{([a-z0-9_-]+)}}/) { data[Regexp.last_match[1].to_sym].to_s }
16
+ Liquid::Template.parse(text).render(data, error_mode: :warn, strict_variables: true)
18
17
  end
19
18
 
20
19
  def human_readable(text)
@@ -5,6 +5,8 @@ module Authify
5
5
  class Group < ActiveRecord::Base
6
6
  include JSONAPIUtils
7
7
 
8
+ validates_uniqueness_of :name, scope: :organization_id
9
+
8
10
  belongs_to :organization,
9
11
  required: true,
10
12
  class_name: 'Authify::API::Models::Organization'
@@ -5,6 +5,8 @@ module Authify
5
5
  class Identity < ActiveRecord::Base
6
6
  include JSONAPIUtils
7
7
 
8
+ validates_uniqueness_of :uid, scope: :provider
9
+
8
10
  belongs_to :user,
9
11
  required: true,
10
12
  class_name: 'Authify::API::Models::User'
@@ -5,6 +5,8 @@ module Authify
5
5
  class Organization < ActiveRecord::Base
6
6
  include JSONAPIUtils
7
7
 
8
+ validates_uniqueness_of :name
9
+
8
10
  has_many :organization_memberships,
9
11
  class_name: 'Authify::API::Models::OrganizationMembership',
10
12
  dependent: :destroy
@@ -5,6 +5,7 @@ module Authify
5
5
  class TrustedDelegate < ActiveRecord::Base
6
6
  include Core::SecureHashing
7
7
  extend Core::SecureHashing
8
+ include JSONAPIUtils
8
9
 
9
10
  attr_reader :secret_key
10
11
 
@@ -10,6 +10,7 @@ module Authify
10
10
  attr_reader :password
11
11
 
12
12
  validates_uniqueness_of :email
13
+ validates_uniqueness_of :handle
13
14
  validates_format_of :email, with: /[-a-z0-9_+\.+]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}/i
14
15
 
15
16
  has_many :apikeys,
@@ -33,8 +34,9 @@ module Authify
33
34
 
34
35
  # Encrypts the password into the password_digest attribute.
35
36
  def password=(plain_password)
37
+ return false unless viable(plain_password)
36
38
  @password = plain_password
37
- self.password_digest = salted_sha512(plain_password) if viable(plain_password)
39
+ self.password_digest = salted_sha512(plain_password)
38
40
  end
39
41
 
40
42
  def authenticate(unencrypted_password)
@@ -57,7 +59,7 @@ module Authify
57
59
  valid_until = valid_time.to_i
58
60
  self.verification_token = "#{token}:#{valid_until}"
59
61
 
60
- subdata = { token: token, valid_until: valid_time }
62
+ subdata = { 'token' => token, 'valid_until' => valid_time }
61
63
 
62
64
  email_opts = {
63
65
  body: if opts.key?(:body)
@@ -98,10 +100,26 @@ module Authify
98
100
  provided_identity.user if provided_identity
99
101
  end
100
102
 
103
+ def self.uniq_handle_generator(name, email)
104
+ possibilities = [email.split('@').first.downcase.gsub(/[._-]/, '')]
105
+ possibilities << name.downcase.gsub(/[.-]/, '_') if name && !name.empty?
106
+ possibilities.each do |possibility|
107
+ return possibility unless find_by_handle(possibility)
108
+ end
109
+ 100.times do
110
+ possibilities.each do |possibility|
111
+ rando_num = rand(9999)
112
+ attempt = "#{possibility.downcase.gsub(/[.-]/, '_')}#{rando_num}"
113
+ return attempt unless find_by_handle(attempt)
114
+ end
115
+ end
116
+ false # didn't work if we got here
117
+ end
118
+
101
119
  private
102
120
 
103
121
  def viable(string)
104
- string && !string.empty?
122
+ string && !string.empty? && string.length >= 8
105
123
  end
106
124
  end
107
125
  end
@@ -0,0 +1,19 @@
1
+ module Authify
2
+ module API
3
+ module Serializers
4
+ # JSON API Serializer for TrustedDelegate model
5
+ class TrustedDelegateSerializer
6
+ include JSONAPI::Serializer
7
+
8
+ def type
9
+ 'trusted-delegates'
10
+ end
11
+
12
+ attribute :name
13
+ attribute :access_key
14
+ attribute :secret_key
15
+ attribute :created_at
16
+ end
17
+ end
18
+ end
19
+ end
@@ -10,6 +10,7 @@ module Authify
10
10
  attribute :admin
11
11
  attribute :created_at
12
12
  attribute :verified
13
+ attribute :handle
13
14
 
14
15
  has_many :apikeys
15
16
  has_many :groups
@@ -67,6 +67,7 @@ module Authify
67
67
  resource :identities, &Controllers::Identity
68
68
  resource :groups, &Controllers::Group
69
69
  resource :organizations, &Controllers::Organization
70
+ resource :trusted_delegate, &Controllers::TrustedDelegate
70
71
  resource :users, &Controllers::User
71
72
 
72
73
  freeze_jsonapi
@@ -48,6 +48,7 @@ module Authify
48
48
 
49
49
  post '/signup' do
50
50
  email = @parsed_body[:email]
51
+ handle = @parsed_body[:handle]
51
52
  via = @parsed_body[:via]
52
53
  password = @parsed_body[:password]
53
54
  name = @parsed_body[:name]
@@ -57,8 +58,12 @@ module Authify
57
58
  halt(403, 'Password Required') unless password || remote_app
58
59
 
59
60
  new_user = Models::User.new(email: email)
61
+ new_user.handle = handle || Models::User.uniq_handle_generator(name, email)
60
62
  new_user.full_name = name if name
61
- new_user.password = password if password
63
+ if password
64
+ new_user.password = password
65
+ halt(422, 'Invalid password') unless new_user.password
66
+ end
62
67
  if via && via[:provider] && remote_app
63
68
  new_user.identities.build(
64
69
  provider: via[:provider], uid: via[:uid] ? via[:uid] : email
@@ -73,7 +78,7 @@ module Authify
73
78
  halt(422, 'Failed to save user') unless new_user.save
74
79
  update_current_user new_user
75
80
 
76
- response = { id: new_user.id, email: new_user.email }
81
+ response = { id: new_user.id, email: new_user.email, handle: new_user.handle }
77
82
  if new_user.verified? || !CONFIG[:verifications][:required]
78
83
  response[:verified] = true
79
84
  response[:jwt] = jwt_token(user: new_user)
@@ -99,11 +104,14 @@ module Authify
99
104
  if token && @parsed_body[:password] && found_user.verify(token)
100
105
  found_user.verified = true
101
106
  found_user.password = @parsed_body[:password]
107
+ halt(422, 'Invalid password') unless found_user.password == @parsed_body[:password]
108
+
102
109
  found_user.save
103
110
  Metrics.instance.increment('registration.password.resets')
104
111
  {
105
112
  id: found_user.id,
106
113
  email: found_user.email,
114
+ handle: found_user.handle,
107
115
  verified: found_user.verified?,
108
116
  jwt: jwt_token(user: found_user)
109
117
  }.to_json
@@ -2,8 +2,8 @@ module Authify
2
2
  module API
3
3
  VERSION = [
4
4
  0, # Major
5
- 4, # Minor
6
- 3 # Patch
5
+ 5, # Minor
6
+ 0 # Patch
7
7
  ].join('.')
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: authify-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Gnagy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-06-16 00:00:00.000000000 Z
11
+ date: 2017-07-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: authify-core
@@ -218,6 +218,20 @@ dependencies:
218
218
  - - "~>"
219
219
  - !ruby/object:Gem::Version
220
220
  version: '1.2'
221
+ - !ruby/object:Gem::Dependency
222
+ name: liquid
223
+ requirement: !ruby/object:Gem::Requirement
224
+ requirements:
225
+ - - "~>"
226
+ - !ruby/object:Gem::Version
227
+ version: '4.0'
228
+ type: :runtime
229
+ prerelease: false
230
+ version_requirements: !ruby/object:Gem::Requirement
231
+ requirements:
232
+ - - "~>"
233
+ - !ruby/object:Gem::Version
234
+ version: '4.0'
221
235
  - !ruby/object:Gem::Dependency
222
236
  name: bundler
223
237
  requirement: !ruby/object:Gem::Requirement
@@ -387,12 +401,19 @@ files:
387
401
  - db/migrate/20170208021933_add_admin_to_user.rb
388
402
  - db/migrate/20170208022427_set_default_for_user_admin.rb
389
403
  - db/migrate/20170328151033_add_verified_to_user.rb
404
+ - db/migrate/20170711151033_add_handle_to_user.rb
405
+ - db/migrate/20170712101155_add_unique_index_to_groups.rb
406
+ - db/migrate/20170712101438_add_unique_index_to_identities.rb
407
+ - db/migrate/20170712103319_add_unique_index_to_organizations.rb
408
+ - db/migrate/20170712103320_add_unique_index_to_users.rb
409
+ - db/migrate/20170714051226_set_default_handle_on_users.rb
390
410
  - db/schema.rb
391
411
  - lib/authify/api.rb
392
412
  - lib/authify/api/controllers/apikey.rb
393
413
  - lib/authify/api/controllers/group.rb
394
414
  - lib/authify/api/controllers/identity.rb
395
415
  - lib/authify/api/controllers/organization.rb
416
+ - lib/authify/api/controllers/trusted_delegate.rb
396
417
  - lib/authify/api/controllers/user.rb
397
418
  - lib/authify/api/helpers/api_user.rb
398
419
  - lib/authify/api/helpers/jwt_encryption.rb
@@ -411,6 +432,7 @@ files:
411
432
  - lib/authify/api/serializers/group_serializer.rb
412
433
  - lib/authify/api/serializers/identity_serializer.rb
413
434
  - lib/authify/api/serializers/organization_serializer.rb
435
+ - lib/authify/api/serializers/trusted_delegate_serializer.rb
414
436
  - lib/authify/api/serializers/user_serializer.rb
415
437
  - lib/authify/api/service.rb
416
438
  - lib/authify/api/services/api.rb