authify-api 0.0.6 → 0.1.0

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.
@@ -0,0 +1,49 @@
1
+ module Authify
2
+ module API
3
+ module Controllers
4
+ Identity = proc do
5
+ helpers do
6
+ def find(id)
7
+ Models::Identity.find(id.to_i)
8
+ end
9
+
10
+ def role
11
+ Array(super).tap do |a|
12
+ a << :myself if current_user && current_user.identities.include?(resource)
13
+ end.uniq
14
+ end
15
+ end
16
+
17
+ index(roles: [:admin, :trusted]) do
18
+ Models::Identity.all
19
+ end
20
+
21
+ show(roles: [:myself, :admin, :trusted]) do
22
+ last_modified resource.updated_at
23
+ next resource
24
+ end
25
+
26
+ create(roles: [:user]) do |attributes|
27
+ ident = Models::Identity.new attributes
28
+ current_user.identities << ident
29
+ current_user.save
30
+ next ident.id, ident
31
+ end
32
+
33
+ destroy(roles: [:myself, :admin]) do
34
+ resource.destroy
35
+ end
36
+
37
+ show_many do |ids|
38
+ Models::Identity.find(ids)
39
+ end
40
+
41
+ has_one :user do
42
+ pluck(roles: [:myself, :admin, :trusted]) do
43
+ resource.user
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -6,13 +6,51 @@ module Authify
6
6
  def find(id)
7
7
  Models::Organization.find(id.to_i)
8
8
  end
9
+
10
+ def role
11
+ Array(super).tap do |a|
12
+ a << :owner if resource && current_user.admin_for?(resource)
13
+ a << :member if resource && resource.users.include?(current_user)
14
+ end.uniq
15
+ end
16
+
17
+ def modifiable_fields
18
+ [
19
+ :name,
20
+ :public_email,
21
+ :gravatar_email,
22
+ :billing_email,
23
+ :description,
24
+ :url,
25
+ :location
26
+ ]
27
+ end
28
+
29
+ def filtered_attributes(attributes)
30
+ attributes.select do |k, _v|
31
+ modifiable_fields.include?(k)
32
+ end
33
+ end
34
+
35
+ def filter(collection, fields = {})
36
+ collection.where(fields)
37
+ end
38
+
39
+ def sort(collection, fields = {})
40
+ collection.order(fields)
41
+ end
9
42
  end
10
43
 
11
- index do
44
+ index(roles: [:admin]) do
12
45
  Models::Organization.all
13
46
  end
14
47
 
15
- show do
48
+ get '/mine' do
49
+ halt(403) unless can?(:create)
50
+ serialize_models current_user.organizations
51
+ end
52
+
53
+ show(roles: [:user]) do
16
54
  last_modified resource.updated_at
17
55
  next resource
18
56
  end
@@ -21,24 +59,36 @@ module Authify
21
59
  Models::Organization.find(ids)
22
60
  end
23
61
 
24
- destroy do
25
- resource.destroy if @current_user.admin_for?(resource)
62
+ create(roles: [:user]) do |attrs|
63
+ o = Models::Organization.new(attrs)
64
+ o.admins << current_user
65
+ o.save
66
+ next o
67
+ end
68
+
69
+ update(roles: [:owner, :admin]) do |attrs|
70
+ resource.update filtered_attributes(attrs)
71
+ next resource
72
+ end
73
+
74
+ destroy(roles: [:owner, :admin]) do
75
+ resource.destroy
26
76
  end
27
77
 
28
78
  has_many :users do
29
- fetch do
79
+ fetch(roles: [:owner, :admin, :member]) do
30
80
  resource.users
31
81
  end
32
82
  end
33
83
 
34
84
  has_many :admins do
35
- fetch do
85
+ fetch(roles: [:owner, :admin, :member]) do
36
86
  resource.admins
37
87
  end
38
88
  end
39
89
 
40
90
  has_many :groups do
41
- fetch do
91
+ fetch(roles: [:owner, :admin]) do
42
92
  resource.groups
43
93
  end
44
94
  end
@@ -6,63 +6,102 @@ module Authify
6
6
  def find(id)
7
7
  Models::User.find(id.to_i)
8
8
  end
9
+
10
+ def role
11
+ Array(super).tap do |a|
12
+ a << :myself if current_user && resource && (resource.id == current_user.id)
13
+ end.uniq
14
+ end
15
+
16
+ def modifiable_fields
17
+ [:full_name, :email].tap do |a|
18
+ a << :admin if role.include?(:admin)
19
+ end
20
+ end
21
+
22
+ def filtered_attributes(attributes)
23
+ attributes.select do |k, _v|
24
+ modifiable_fields.include?(k)
25
+ end
26
+ end
27
+
28
+ def filter(collection, fields = {})
29
+ collection.where(fields)
30
+ end
31
+
32
+ def sort(collection, fields = {})
33
+ collection.order(fields)
34
+ end
9
35
  end
10
36
 
11
- index do
37
+ index(roles: [:user, :trusted]) do
12
38
  Models::User.all
13
39
  end
14
40
 
15
- show do
41
+ show(roles: [:user, :trusted]) do
16
42
  last_modified resource.updated_at
17
43
  next resource
18
44
  end
19
45
 
46
+ create(roles: [:admin]) do |attributes|
47
+ user = Models::User.new filtered_attributes(attributes)
48
+ user.save
49
+ next user
50
+ end
51
+
20
52
  show_many do |ids|
21
53
  Models::User.find(ids)
22
54
  end
23
55
 
24
- has_many :api_keys do
25
- fetch do
26
- resource.api_keys
56
+ has_many :apikeys do
57
+ fetch(roles: [:myself, :admin]) do
58
+ resource.apikeys
27
59
  end
28
60
 
29
- clear do
30
- resource.api_keys.destroy_all
61
+ clear(roles: [:myself, :admin]) do
62
+ resource.apikeys.destroy_all
31
63
  resource.save
32
64
  end
33
65
 
34
- subtract do |rios|
35
- refs = rios.map { |attrs| Models::APIKey.new(attrs) }
36
- resource.api_keys.destroy(refs)
66
+ subtract(roles: [:myself, :admin]) do |rios|
67
+ refs = rios.map { |attrs| Models::APIKey.find(attrs) }
68
+ # This actually calls #destroy on the keys (we don't need orphaned keys)
69
+ resource.apikeys.destroy(refs)
37
70
  resource.save
38
71
  end
39
72
  end
40
73
 
41
74
  has_many :identities do
42
- fetch do
75
+ fetch(roles: [:myself, :admin, :trusted]) do
43
76
  resource.identities
44
77
  end
45
78
 
46
- clear do
79
+ clear(roles: [:myself, :admin]) do
47
80
  resource.identities.destroy_all
48
81
  resource.save
49
82
  end
50
83
 
51
- subtract do |rios|
84
+ merge(roles: [:myself]) do |rios|
52
85
  refs = rios.map { |attrs| Models::Identities.new(attrs) }
86
+ resource.identities << refs
87
+ resource.save
88
+ end
89
+
90
+ subtract(roles: [:myself, :admin]) do |rios|
91
+ refs = rios.map { |attrs| Models::Identities.find(attrs) }
53
92
  resource.identities.destroy(refs)
54
93
  resource.save
55
94
  end
56
95
  end
57
96
 
58
97
  has_many :organizations do
59
- fetch do
98
+ fetch(roles: [:user, :myself]) do
60
99
  resource.organizations
61
100
  end
62
101
  end
63
102
 
64
103
  has_many :groups do
65
- fetch do
104
+ fetch(roles: [:myself, :admin]) do
66
105
  resource.groups
67
106
  end
68
107
  end
@@ -4,7 +4,16 @@ module Authify
4
4
  # Helper methods for API users
5
5
  module APIUser
6
6
  def current_user
7
- @current_user ||= Models::User.find_by_email(env['user']['username'])
7
+ email = env.key?(:user) ? env[:user]['username'] : nil
8
+ @current_user ||= email ? Models::User.find_by_email(email) : nil
9
+ end
10
+
11
+ def remote_app
12
+ @remote_app ||= if processed_headers.key?('x_authify_access')
13
+ access = processed_headers['x_authify_access']
14
+ secret = processed_headers['x_authify_secret']
15
+ Models::TrustedDelegate.from_access_key(access, secret)
16
+ end
8
17
  end
9
18
 
10
19
  def update_current_user(user)
@@ -20,6 +29,12 @@ module Authify
20
29
  @current_user = found_user
21
30
  end
22
31
 
32
+ def processed_headers
33
+ @headers ||= request.env.dup.each_with_object({}) do |(k, v), acc|
34
+ acc[Regexp.last_match(1).downcase] = v if k =~ /^http_(.*)/i
35
+ end
36
+ end
37
+
23
38
  # This shouldn't work via API calls
24
39
  def auth
25
40
  {}
@@ -5,27 +5,40 @@ module Authify
5
5
  module JWTEncryption
6
6
  include Core::Helpers::JWTSSL
7
7
 
8
- def jwt_token
9
- JWT.encode jwt_payload(current_user), private_key, 'ES512'
8
+ def jwt_token(user = nil)
9
+ user ||= current_user
10
+ JWT.encode jwt_payload(user), private_key, CONFIG[:jwt][:algorithm]
10
11
  end
11
12
 
12
13
  def jwt_payload(user)
13
14
  {
14
- exp: Time.now.to_i + 60 * 60,
15
+ exp: Time.now.to_i + 60 * CONFIG[:jwt][:expiration].to_i,
15
16
  iat: Time.now.to_i,
16
17
  iss: CONFIG[:jwt][:issuer],
17
- scopes: Core::Constants::JWTSCOPES,
18
+ scopes: Core::Constants::JWTSCOPES.dup.tap do |scopes|
19
+ scopes << :admin_access if current_user && current_user.admin?
20
+ end,
18
21
  user: {
19
22
  username: user.email,
20
23
  uid: user.id,
21
- organizations: user.organizations.map do |o|
22
- { name: o.name, oid: o.id, admin: o.admins.include?(user) }
23
- end,
24
- groups: user.groups.map { |g| { name: g.name, gid: g.id } }
24
+ organizations: simple_orgs_by_user(user)
25
25
  }
26
26
  }
27
27
  end
28
28
 
29
+ def simple_orgs_by_user(user)
30
+ user.organizations.map do |o|
31
+ {
32
+ name: o.name,
33
+ oid: o.id,
34
+ admin: o.admins.include?(user),
35
+ memberships: o.groups.select { |g| g.users.include?(user) }.map do |g|
36
+ { name: g.name, gid: g.id }
37
+ end
38
+ }
39
+ end
40
+ end
41
+
29
42
  def with_jwt(req, scope)
30
43
  scopes, user = req.env.values_at :scopes, :user
31
44
  set_current_user Models::User.from_username(user['username'])
@@ -3,6 +3,7 @@ module Authify
3
3
  module Models
4
4
  # Additional, revocable user access
5
5
  class APIKey < ActiveRecord::Base
6
+ self.table_name = 'apikeys'
6
7
  include Core::SecureHashing
7
8
  extend Core::SecureHashing
8
9
  include JSONAPIUtils
@@ -25,12 +26,25 @@ module Authify
25
26
  compare_salted_sha512(unencrypted_string, secret_key_digest)
26
27
  end
27
28
 
29
+ def set_access!
30
+ self.access_key = self.class.generate_access_key
31
+ end
32
+
28
33
  def set_secret!
29
- self.secret_key = self.class.generate_access_key + self.class.generate_access_key
34
+ self.secret_key = self.class.generate_secret_key
35
+ end
36
+
37
+ def populate!
38
+ set_access!
39
+ set_secret!
30
40
  end
31
41
 
32
42
  def self.generate_access_key
33
- to_hex(SecureRandom.gen_random(32))[0...32]
43
+ to_hex(SecureRandom.gen_random(32))[0...20]
44
+ end
45
+
46
+ def self.generate_secret_key
47
+ to_hex(SecureRandom.gen_random(32))[0...32] + to_hex(SecureRandom.gen_random(32))[0...32]
34
48
  end
35
49
 
36
50
  private
@@ -11,9 +11,7 @@ module Authify
11
11
  validates_uniqueness_of :email
12
12
  validates_format_of :email, with: /[-a-z0-9_+\.+]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}/i
13
13
 
14
- validates_presence_of :password_digest
15
-
16
- has_many :api_keys,
14
+ has_many :apikeys,
17
15
  class_name: 'Authify::API::Models::APIKey',
18
16
  dependent: :destroy
19
17
 
@@ -44,7 +42,7 @@ module Authify
44
42
  end
45
43
 
46
44
  def admin_for?(organization)
47
- organization.admins.include?(self)
45
+ admin? || organization.admins.include?(self)
48
46
  end
49
47
 
50
48
  def self.from_api_key(access, secret)
@@ -5,7 +5,14 @@ module Authify
5
5
  class APIKeySerializer
6
6
  include JSONAPI::Serializer
7
7
 
8
+ def type
9
+ 'apikeys'
10
+ end
11
+
8
12
  attribute :access_key
13
+ attribute :secret_key
14
+ attribute :created_at
15
+
9
16
  has_one :user
10
17
  end
11
18
  end
@@ -7,6 +7,8 @@ module Authify
7
7
 
8
8
  attribute :name
9
9
  attribute :description
10
+ attribute :created_at
11
+
10
12
  has_one :organization
11
13
  has_many :users
12
14
  end
@@ -0,0 +1,14 @@
1
+ module Authify
2
+ module API
3
+ module Serializers
4
+ # JSON API Serializer for Identity model
5
+ class IdentitySerializer
6
+ include JSONAPI::Serializer
7
+
8
+ attribute :provider
9
+ attribute :uid
10
+ has_one :user
11
+ end
12
+ end
13
+ end
14
+ end
@@ -6,9 +6,17 @@ module Authify
6
6
  include JSONAPI::Serializer
7
7
 
8
8
  attribute :name
9
+ attribute :public_email
10
+ attribute :gravatar_email
11
+ attribute :billing_email
9
12
  attribute :description
13
+ attribute :url
14
+ attribute :location
15
+ attribute :created_at
16
+
10
17
  has_many :groups
11
18
  has_many :users
19
+ has_many :admins
12
20
  end
13
21
  end
14
22
  end