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.
@@ -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