casdoor-ruby-sdk 1.0.0 → 1.1.1
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.
- checksums.yaml +4 -4
- data/README.md +134 -55
- data/lib/casdoor/adapter.rb +48 -0
- data/lib/casdoor/application.rb +48 -0
- data/lib/casdoor/auth.rb +143 -0
- data/lib/casdoor/cert.rb +47 -0
- data/lib/casdoor/email.rb +33 -0
- data/lib/casdoor/enforce.rb +47 -0
- data/lib/casdoor/enforcer.rb +48 -0
- data/lib/casdoor/entity.rb +17 -44
- data/lib/casdoor/global.rb +34 -0
- data/lib/casdoor/group.rb +48 -0
- data/lib/casdoor/invitation.rb +57 -0
- data/lib/casdoor/jwt.rb +75 -0
- data/lib/casdoor/ldap.rb +67 -0
- data/lib/casdoor/logout.rb +41 -0
- data/lib/casdoor/mfa.rb +53 -0
- data/lib/casdoor/model.rb +48 -0
- data/lib/casdoor/notification.rb +29 -0
- data/lib/casdoor/order.rb +59 -0
- data/lib/casdoor/order_pay.rb +40 -0
- data/lib/casdoor/organization.rb +47 -0
- data/lib/casdoor/payment.rb +61 -0
- data/lib/casdoor/permission.rb +57 -0
- data/lib/casdoor/plan.rb +48 -0
- data/lib/casdoor/policy.rb +67 -0
- data/lib/casdoor/pricing.rb +48 -0
- data/lib/casdoor/product.rb +48 -0
- data/lib/casdoor/provider.rb +48 -0
- data/lib/casdoor/record.rb +43 -0
- data/lib/casdoor/resource.rb +75 -0
- data/lib/casdoor/role.rb +52 -0
- data/lib/casdoor/session.rb +53 -0
- data/lib/casdoor/sms.rb +31 -0
- data/lib/casdoor/subscription.rb +48 -0
- data/lib/casdoor/syncer.rb +48 -0
- data/lib/casdoor/token.rb +60 -0
- data/lib/casdoor/transaction.rb +62 -0
- data/lib/casdoor/url.rb +47 -0
- data/lib/casdoor/user.rb +124 -0
- data/lib/casdoor/util.rb +147 -0
- data/lib/casdoor/util_modify.rb +65 -0
- data/lib/casdoor/version.rb +1 -1
- data/lib/casdoor/webhook.rb +48 -0
- data/lib/casdoor.rb +13 -5
- metadata +41 -6
- data/lib/casdoor/api/auth.rb +0 -153
- data/lib/casdoor/api/crud.rb +0 -122
- data/lib/casdoor/api/enforce.rb +0 -44
- data/lib/casdoor/api/users.rb +0 -83
- data/lib/casdoor/client.rb +0 -143
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2026 The Casdoor Authors. All Rights Reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
module Casdoor
|
|
18
|
+
# The helpers shared by the APIs of the objects, the counterpart of util_modify.go of the Go SDK
|
|
19
|
+
class Client
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def get_objects(action, klass, query_map)
|
|
23
|
+
(do_get_bytes(get_url(action, query_map)) || []).map { |item| klass.new(item) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Returns the objects of the page and the total count
|
|
27
|
+
def get_pagination_objects(action, klass, owner, page, page_size, query_map)
|
|
28
|
+
query = to_query_map(query_map).merge('owner' => owner, 'p' => page.to_s, 'pageSize' => page_size.to_s)
|
|
29
|
+
response = do_get_response(get_url(action, query))
|
|
30
|
+
[(response['data'] || []).map { |item| klass.new(item) }, response['data2'].to_i]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def get_object(action, klass, query_map)
|
|
34
|
+
data = do_get_bytes(get_url(action, query_map))
|
|
35
|
+
data && klass.new(data)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Posts the object to an add/update/delete API, and returns whether the object is affected. The owner of the
|
|
39
|
+
# object defaults to default_owner, and columns limits the updated fields. Most APIs answer "Affected" or
|
|
40
|
+
# "Unaffected", while some (e.g. update-model) answer only "ok" when they succeed.
|
|
41
|
+
def modify_object(action, klass, object, default_owner, columns = nil, query_map = {})
|
|
42
|
+
object = to_entity(klass, object)
|
|
43
|
+
object.owner = default_owner if object.owner.to_s.empty?
|
|
44
|
+
|
|
45
|
+
query = { 'id' => object.get_id }.merge(query_map)
|
|
46
|
+
if columns && !columns.empty?
|
|
47
|
+
query['columns'] = columns.map do |column|
|
|
48
|
+
column.is_a?(Symbol) ? Entity.camelize(column) : column
|
|
49
|
+
end.join(',')
|
|
50
|
+
end
|
|
51
|
+
do_post(action, query, object)['data'] != 'Unaffected'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def to_entity(klass, object)
|
|
55
|
+
object.is_a?(Entity) ? object : klass.new(object)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Symbol keys are snake_case, e.g. sort_field: "name", while String keys are the names of Casdoor
|
|
59
|
+
def to_query_map(query_map)
|
|
60
|
+
(query_map || {}).each_with_object({}) do |(key, value), query|
|
|
61
|
+
query[key.is_a?(Symbol) ? Entity.camelize(key) : key.to_s] = value
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/casdoor/version.rb
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2026 The Casdoor Authors. All Rights Reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
module Casdoor
|
|
18
|
+
class Webhook < Entity; end
|
|
19
|
+
|
|
20
|
+
# The webhook APIs, the counterpart of webhook.go of the Go SDK
|
|
21
|
+
class Client
|
|
22
|
+
def get_webhooks
|
|
23
|
+
get_objects('get-webhooks', Webhook, 'owner' => organization_name)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Returns the objects of the page and the total count. query_map can filter and sort them, e.g.
|
|
27
|
+
# { field: "name", value: "alice", sort_field: "created_time", sort_order: "descend" }
|
|
28
|
+
def get_pagination_webhooks(page, page_size, query_map = {})
|
|
29
|
+
get_pagination_objects('get-webhooks', Webhook, organization_name, page, page_size, query_map)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def get_webhook(name)
|
|
33
|
+
get_object('get-webhook', Webhook, 'id' => get_id(name))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def update_webhook(webhook)
|
|
37
|
+
modify_object('update-webhook', Webhook, webhook, organization_name)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def add_webhook(webhook)
|
|
41
|
+
modify_object('add-webhook', Webhook, webhook, organization_name)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def delete_webhook(webhook)
|
|
45
|
+
modify_object('delete-webhook', Webhook, webhook, organization_name)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/casdoor.rb
CHANGED
|
@@ -17,8 +17,16 @@
|
|
|
17
17
|
require_relative 'casdoor/version'
|
|
18
18
|
require_relative 'casdoor/error'
|
|
19
19
|
require_relative 'casdoor/entity'
|
|
20
|
-
require_relative 'casdoor/
|
|
21
|
-
require_relative 'casdoor/
|
|
22
|
-
require_relative 'casdoor/
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
require_relative 'casdoor/auth'
|
|
21
|
+
require_relative 'casdoor/util'
|
|
22
|
+
require_relative 'casdoor/util_modify'
|
|
23
|
+
|
|
24
|
+
# The files of the APIs, the same as the casdoorsdk package of the Go SDK
|
|
25
|
+
%w[
|
|
26
|
+
adapter application cert email enforce enforcer group invitation jwt ldap logout mfa model notification order
|
|
27
|
+
order_pay organization payment permission plan policy pricing product provider record resource role session sms
|
|
28
|
+
subscription syncer token transaction url user webhook
|
|
29
|
+
].each { |file| require_relative "casdoor/#{file}" }
|
|
30
|
+
|
|
31
|
+
# Must be the last, it delegates all the methods of Client to the module
|
|
32
|
+
require_relative 'casdoor/global'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: casdoor-ruby-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yang Luo
|
|
@@ -42,14 +42,49 @@ files:
|
|
|
42
42
|
- README.md
|
|
43
43
|
- lib/casdoor-ruby-sdk.rb
|
|
44
44
|
- lib/casdoor.rb
|
|
45
|
-
- lib/casdoor/
|
|
46
|
-
- lib/casdoor/
|
|
47
|
-
- lib/casdoor/
|
|
48
|
-
- lib/casdoor/
|
|
49
|
-
- lib/casdoor/
|
|
45
|
+
- lib/casdoor/adapter.rb
|
|
46
|
+
- lib/casdoor/application.rb
|
|
47
|
+
- lib/casdoor/auth.rb
|
|
48
|
+
- lib/casdoor/cert.rb
|
|
49
|
+
- lib/casdoor/email.rb
|
|
50
|
+
- lib/casdoor/enforce.rb
|
|
51
|
+
- lib/casdoor/enforcer.rb
|
|
50
52
|
- lib/casdoor/entity.rb
|
|
51
53
|
- lib/casdoor/error.rb
|
|
54
|
+
- lib/casdoor/global.rb
|
|
55
|
+
- lib/casdoor/group.rb
|
|
56
|
+
- lib/casdoor/invitation.rb
|
|
57
|
+
- lib/casdoor/jwt.rb
|
|
58
|
+
- lib/casdoor/ldap.rb
|
|
59
|
+
- lib/casdoor/logout.rb
|
|
60
|
+
- lib/casdoor/mfa.rb
|
|
61
|
+
- lib/casdoor/model.rb
|
|
62
|
+
- lib/casdoor/notification.rb
|
|
63
|
+
- lib/casdoor/order.rb
|
|
64
|
+
- lib/casdoor/order_pay.rb
|
|
65
|
+
- lib/casdoor/organization.rb
|
|
66
|
+
- lib/casdoor/payment.rb
|
|
67
|
+
- lib/casdoor/permission.rb
|
|
68
|
+
- lib/casdoor/plan.rb
|
|
69
|
+
- lib/casdoor/policy.rb
|
|
70
|
+
- lib/casdoor/pricing.rb
|
|
71
|
+
- lib/casdoor/product.rb
|
|
72
|
+
- lib/casdoor/provider.rb
|
|
73
|
+
- lib/casdoor/record.rb
|
|
74
|
+
- lib/casdoor/resource.rb
|
|
75
|
+
- lib/casdoor/role.rb
|
|
76
|
+
- lib/casdoor/session.rb
|
|
77
|
+
- lib/casdoor/sms.rb
|
|
78
|
+
- lib/casdoor/subscription.rb
|
|
79
|
+
- lib/casdoor/syncer.rb
|
|
80
|
+
- lib/casdoor/token.rb
|
|
81
|
+
- lib/casdoor/transaction.rb
|
|
82
|
+
- lib/casdoor/url.rb
|
|
83
|
+
- lib/casdoor/user.rb
|
|
84
|
+
- lib/casdoor/util.rb
|
|
85
|
+
- lib/casdoor/util_modify.rb
|
|
52
86
|
- lib/casdoor/version.rb
|
|
87
|
+
- lib/casdoor/webhook.rb
|
|
53
88
|
homepage: https://github.com/casdoor/casdoor-ruby-sdk
|
|
54
89
|
licenses:
|
|
55
90
|
- Apache-2.0
|
data/lib/casdoor/api/auth.rb
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Copyright 2026 The Casdoor Authors. All Rights Reserved.
|
|
4
|
-
#
|
|
5
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
# you may not use this file except in compliance with the License.
|
|
7
|
-
# You may obtain a copy of the License at
|
|
8
|
-
#
|
|
9
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
#
|
|
11
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
# See the License for the specific language governing permissions and
|
|
15
|
-
# limitations under the License.
|
|
16
|
-
|
|
17
|
-
require 'jwt'
|
|
18
|
-
require 'openssl'
|
|
19
|
-
|
|
20
|
-
module Casdoor
|
|
21
|
-
module Api
|
|
22
|
-
# Signing users in with OAuth 2.0 / OIDC, and verifying the JWT tokens issued by Casdoor.
|
|
23
|
-
#
|
|
24
|
-
# The OAuth tokens are returned as the hashes of the Casdoor token API, e.g.
|
|
25
|
-
# {"access_token" => "...", "id_token" => "...", "refresh_token" => "...", "token_type" => "Bearer",
|
|
26
|
-
# "expires_in" => 604800, "scope" => "read"}
|
|
27
|
-
module Auth
|
|
28
|
-
JWT_ALGORITHMS = %w[RS256 RS384 RS512 ES256 ES384 ES512 PS256 PS384 PS512].freeze
|
|
29
|
-
|
|
30
|
-
# The URL of the sign-in page that the browser is redirected to. After signing in, Casdoor redirects back to
|
|
31
|
-
# redirect_uri with "code" and "state" in the query, and the code is exchanged with get_oauth_token.
|
|
32
|
-
def get_signin_url(redirect_uri, state: application_name, scope: 'read')
|
|
33
|
-
query = { 'client_id' => client_id, 'response_type' => 'code', 'redirect_uri' => redirect_uri,
|
|
34
|
-
'scope' => scope, 'state' => state }
|
|
35
|
-
"#{endpoint}/login/oauth/authorize?#{URI.encode_www_form(query)}"
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# The URL of the sign-up page. Without redirect_uri, it's the plain sign-up page of the application, otherwise
|
|
39
|
-
# the user is signed in and redirected back to redirect_uri like get_signin_url.
|
|
40
|
-
def get_signup_url(redirect_uri = nil, state: application_name, scope: 'read')
|
|
41
|
-
return "#{endpoint}/signup/#{application_name}" if redirect_uri.nil?
|
|
42
|
-
|
|
43
|
-
url = get_signin_url(redirect_uri, state: state, scope: scope)
|
|
44
|
-
url.sub('/login/oauth/authorize', '/signup/oauth/authorize')
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def get_user_profile_url(user_name, access_token = nil)
|
|
48
|
-
url = "#{endpoint}/users/#{organization_name}/#{user_name}"
|
|
49
|
-
access_token ? "#{url}?#{URI.encode_www_form('access_token' => access_token)}" : url
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def get_my_profile_url(access_token = nil)
|
|
53
|
-
url = "#{endpoint}/account"
|
|
54
|
-
access_token ? "#{url}?#{URI.encode_www_form('access_token' => access_token)}" : url
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Exchanges the code that Casdoor passed to the redirect URI for the OAuth tokens
|
|
58
|
-
def get_oauth_token(code)
|
|
59
|
-
request_oauth_token('authorization_code', 'code' => code)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def refresh_oauth_token(refresh_token, scope: nil)
|
|
63
|
-
request_oauth_token('refresh_token', 'refresh_token' => refresh_token, 'scope' => scope)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# The "password" grant type, it must be enabled in the "Grant types" of the application in Casdoor.
|
|
67
|
-
# username is the user's name in the application's organization, like "alice".
|
|
68
|
-
def get_oauth_token_by_password(username, password)
|
|
69
|
-
request_oauth_token('password', 'username' => username, 'password' => password)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# A token of the application itself, the "client_credentials" grant type must be enabled in the application
|
|
73
|
-
def get_oauth_token_by_client_credentials
|
|
74
|
-
request_oauth_token('client_credentials')
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# Verifies a JWT token (the access token or the ID token) with the certificate of the client, and returns its
|
|
78
|
-
# claims. The token must not be expired, and must be issued to this application: pass audience: to check a
|
|
79
|
-
# different audience (e.g. the resource of RFC 8707), or audience: false to skip the check.
|
|
80
|
-
#
|
|
81
|
-
# claims = client.parse_jwt_token(token["access_token"])
|
|
82
|
-
# claims.name # => "alice"
|
|
83
|
-
# claims.owner # => "casbin"
|
|
84
|
-
# claims.user # => Casdoor::User
|
|
85
|
-
def parse_jwt_token(token, audience: nil)
|
|
86
|
-
# The expiration and the "not before" time are verified by default
|
|
87
|
-
payload, = JWT.decode(token, jwt_public_key, true, algorithms: JWT_ALGORITHMS)
|
|
88
|
-
check_audience(payload['aud'], audience) unless audience == false
|
|
89
|
-
Claims.new(payload)
|
|
90
|
-
rescue JWT::DecodeError => e
|
|
91
|
-
raise InvalidTokenError, e.message
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Asks Casdoor whether the token is active, see RFC 7662. Returns a hash like {"active" => true, ...}.
|
|
95
|
-
def introspect_token(token, token_type_hint: 'access_token')
|
|
96
|
-
request = Net::HTTP::Post.new(get_url('login/oauth/introspect'))
|
|
97
|
-
request.set_form_data('token' => token, 'token_type_hint' => token_type_hint)
|
|
98
|
-
check_oauth_response(send_request(request))
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# Signs the user out of Casdoor, from all their sessions or only the session of the access token
|
|
102
|
-
def logout(access_token, all_sessions: true)
|
|
103
|
-
with_access_token(access_token).post_form('sso-logout', { 'logoutAll' => all_sessions }, {})
|
|
104
|
-
true
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
private
|
|
108
|
-
|
|
109
|
-
def request_oauth_token(grant_type, params = {})
|
|
110
|
-
form = { 'grant_type' => grant_type, 'client_id' => client_id, 'client_secret' => client_secret }
|
|
111
|
-
form.merge!(params.compact)
|
|
112
|
-
|
|
113
|
-
request = Net::HTTP::Post.new(get_url('login/oauth/access_token'))
|
|
114
|
-
request.set_form_data(form)
|
|
115
|
-
token = check_oauth_response(send_request(request, auth: false))
|
|
116
|
-
|
|
117
|
-
# Older Casdoor versions put the error into the access token
|
|
118
|
-
if token['access_token'].to_s.start_with?('error:')
|
|
119
|
-
raise ApiError, token['access_token'].delete_prefix('error:').strip
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
token
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def check_oauth_response(body)
|
|
126
|
-
if body['error']
|
|
127
|
-
message = [body['error'], body['error_description']].compact.reject(&:empty?).join(': ')
|
|
128
|
-
raise ApiError, message
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
check_response(body)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def jwt_public_key
|
|
135
|
-
pem = certificate.to_s
|
|
136
|
-
raise Error, 'The certificate of the client is required to parse JWT tokens' if pem.strip.empty?
|
|
137
|
-
|
|
138
|
-
pem.include?('CERTIFICATE') ? OpenSSL::X509::Certificate.new(pem).public_key : OpenSSL::PKey.read(pem)
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# Casdoor issues the tokens to the client ID, or to "<client ID>-org-<organization>" for shared applications
|
|
142
|
-
def check_audience(aud, audience)
|
|
143
|
-
auds = Array(aud)
|
|
144
|
-
valid = if audience
|
|
145
|
-
auds.include?(audience)
|
|
146
|
-
else
|
|
147
|
-
auds.any? { |item| item == client_id || item.to_s.start_with?("#{client_id}-org-") }
|
|
148
|
-
end
|
|
149
|
-
raise InvalidTokenError, "The token is issued to #{auds.inspect}, not this application" unless valid
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
end
|
data/lib/casdoor/api/crud.rb
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Copyright 2026 The Casdoor Authors. All Rights Reserved.
|
|
4
|
-
#
|
|
5
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
# you may not use this file except in compliance with the License.
|
|
7
|
-
# You may obtain a copy of the License at
|
|
8
|
-
#
|
|
9
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
#
|
|
11
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
# See the License for the specific language governing permissions and
|
|
15
|
-
# limitations under the License.
|
|
16
|
-
|
|
17
|
-
module Casdoor
|
|
18
|
-
module Api
|
|
19
|
-
# The add/get/update/delete APIs that all the Casdoor objects share. For each object, e.g. the user, it defines:
|
|
20
|
-
#
|
|
21
|
-
# get_users(**query) # => [User]
|
|
22
|
-
# get_pagination_users(page, page_size, **query) # => [[User], total count]
|
|
23
|
-
# get_user(name) # => User or nil, name can be "name" or "owner/name"
|
|
24
|
-
# add_user(user) # => true if added
|
|
25
|
-
# update_user(user, columns: nil) # => true if changed, columns limits the updated fields
|
|
26
|
-
# delete_user(user_or_name) # => true if deleted
|
|
27
|
-
#
|
|
28
|
-
# The objects belong to the client's organization, except the ones in ADMIN_OWNED, which belong to "admin".
|
|
29
|
-
# A different owner can be passed with "owner/name", the owner field of the object, or owner: in the query.
|
|
30
|
-
# Extra query parameters of the list APIs can be passed in snake_case, e.g. get_users(sort_field: "name").
|
|
31
|
-
module Crud
|
|
32
|
-
OBJECTS = {
|
|
33
|
-
adapter: Adapter,
|
|
34
|
-
application: Application,
|
|
35
|
-
cert: Cert,
|
|
36
|
-
enforcer: Enforcer,
|
|
37
|
-
group: Group,
|
|
38
|
-
invitation: Invitation,
|
|
39
|
-
model: Model,
|
|
40
|
-
order: Order,
|
|
41
|
-
organization: Organization,
|
|
42
|
-
payment: Payment,
|
|
43
|
-
permission: Permission,
|
|
44
|
-
plan: Plan,
|
|
45
|
-
pricing: Pricing,
|
|
46
|
-
product: Product,
|
|
47
|
-
provider: Provider,
|
|
48
|
-
resource: Resource,
|
|
49
|
-
role: Role,
|
|
50
|
-
subscription: Subscription,
|
|
51
|
-
syncer: Syncer,
|
|
52
|
-
token: Token,
|
|
53
|
-
transaction: Transaction,
|
|
54
|
-
user: User,
|
|
55
|
-
webhook: Webhook
|
|
56
|
-
}.freeze
|
|
57
|
-
|
|
58
|
-
ADMIN_OWNED = %i[application organization token].freeze
|
|
59
|
-
|
|
60
|
-
OBJECTS.each do |object, klass|
|
|
61
|
-
plural = "#{object}s"
|
|
62
|
-
|
|
63
|
-
define_method("get_#{plural}") do |**query|
|
|
64
|
-
data = get_data("get-#{plural}", list_query(object, query)) || []
|
|
65
|
-
data.map { |item| klass.new(item) }
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
define_method("get_pagination_#{plural}") do |page, page_size, **query|
|
|
69
|
-
query = list_query(object, query).merge('p' => page, 'pageSize' => page_size)
|
|
70
|
-
response = get_response("get-#{plural}", query)
|
|
71
|
-
[(response['data'] || []).map { |item| klass.new(item) }, response['data2'].to_i]
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
define_method("get_#{object}") do |name|
|
|
75
|
-
data = get_data("get-#{object}", 'id' => object_id_of(object, name))
|
|
76
|
-
data && klass.new(data)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
define_method("add_#{object}") do |entity|
|
|
80
|
-
modify_object("add-#{object}", object, entity)
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
define_method("update_#{object}") do |entity, columns: nil|
|
|
84
|
-
modify_object("update-#{object}", object, entity, columns: columns)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# Casdoor deletes an object by the whole object, e.g. an application is only deleted when its organization
|
|
88
|
-
# matches too, so the object is fetched first when only its name is given
|
|
89
|
-
define_method("delete_#{object}") do |entity|
|
|
90
|
-
entity = send("get_#{object}", entity) unless entity.is_a?(Entity)
|
|
91
|
-
return false if entity.nil?
|
|
92
|
-
|
|
93
|
-
modify_object("delete-#{object}", object, entity)
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
private
|
|
98
|
-
|
|
99
|
-
def default_owner(object)
|
|
100
|
-
ADMIN_OWNED.include?(object) ? 'admin' : organization_name
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def object_id_of(object, name)
|
|
104
|
-
name.to_s.include?('/') ? name.to_s : "#{default_owner(object)}/#{name}"
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def list_query(object, query)
|
|
108
|
-
query = query.each_with_object({}) { |(key, value), hash| hash[Entity.camelize(key)] = value }
|
|
109
|
-
{ 'owner' => default_owner(object) }.merge(query)
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def modify_object(action, object, entity, columns: nil, query: {})
|
|
113
|
-
entity = OBJECTS.fetch(object).new(entity) if entity.is_a?(Hash)
|
|
114
|
-
entity.owner = default_owner(object) if entity.owner.to_s.empty?
|
|
115
|
-
|
|
116
|
-
query = { 'id' => "#{entity.owner}/#{entity.name}" }.merge(query)
|
|
117
|
-
query['columns'] = Array(columns).map { |column| Entity.camelize(column) }.join(',') if columns
|
|
118
|
-
post_json(action, query, entity)['data'] == 'Affected'
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
end
|
data/lib/casdoor/api/enforce.rb
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Copyright 2026 The Casdoor Authors. All Rights Reserved.
|
|
4
|
-
#
|
|
5
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
# you may not use this file except in compliance with the License.
|
|
7
|
-
# You may obtain a copy of the License at
|
|
8
|
-
#
|
|
9
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
#
|
|
11
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
# See the License for the specific language governing permissions and
|
|
15
|
-
# limitations under the License.
|
|
16
|
-
|
|
17
|
-
module Casdoor
|
|
18
|
-
module Api
|
|
19
|
-
# The permission checks of Casdoor. Pass one of permission_id, model_id, resource_id, enforcer_id or owner
|
|
20
|
-
# ("owner/name" for the IDs) to choose which policies to check against.
|
|
21
|
-
module Enforce
|
|
22
|
-
# request is a Casbin request like ["alice", "data1", "read"]. Returns true if any of the checked
|
|
23
|
-
# permissions allows it.
|
|
24
|
-
def enforce(request, permission_id: nil, model_id: nil, resource_id: nil, enforcer_id: nil, owner: nil)
|
|
25
|
-
query = enforce_query(permission_id, model_id, resource_id, enforcer_id, owner)
|
|
26
|
-
Array(post_json('enforce', query, request)['data']).any?(true)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# requests is a list of Casbin requests. Returns a list of results for each checked permission, like
|
|
30
|
-
# [[true, false]] for two requests checked against one permission.
|
|
31
|
-
def batch_enforce(requests, permission_id: nil, model_id: nil, resource_id: nil, enforcer_id: nil, owner: nil)
|
|
32
|
-
query = enforce_query(permission_id, model_id, resource_id, enforcer_id, owner)
|
|
33
|
-
post_json('batch-enforce', query, requests)['data'] || []
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
private
|
|
37
|
-
|
|
38
|
-
def enforce_query(permission_id, model_id, resource_id, enforcer_id, owner)
|
|
39
|
-
{ 'permissionId' => permission_id, 'modelId' => model_id, 'resourceId' => resource_id,
|
|
40
|
-
'enforcerId' => enforcer_id, 'owner' => owner }
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
data/lib/casdoor/api/users.rb
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Copyright 2026 The Casdoor Authors. All Rights Reserved.
|
|
4
|
-
#
|
|
5
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
# you may not use this file except in compliance with the License.
|
|
7
|
-
# You may obtain a copy of the License at
|
|
8
|
-
#
|
|
9
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
#
|
|
11
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
# See the License for the specific language governing permissions and
|
|
15
|
-
# limitations under the License.
|
|
16
|
-
|
|
17
|
-
module Casdoor
|
|
18
|
-
module Api
|
|
19
|
-
# The user APIs besides the ones in Crud
|
|
20
|
-
module Users
|
|
21
|
-
# The user that the client is authenticated as, meant to be used with with_access_token:
|
|
22
|
-
#
|
|
23
|
-
# user = client.with_access_token(access_token).get_account
|
|
24
|
-
def get_account
|
|
25
|
-
data = get_data('get-account')
|
|
26
|
-
data && User.new(data)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# The users of all the organizations, only allowed for global admins
|
|
30
|
-
def get_global_users
|
|
31
|
-
(get_data('get-global-users') || []).map { |item| User.new(item) }
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def get_sorted_users(sorter, limit)
|
|
35
|
-
query = { 'owner' => organization_name, 'sorter' => sorter, 'limit' => limit }
|
|
36
|
-
(get_data('get-sorted-users', query) || []).map { |item| User.new(item) }
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# is_online: nil counts all the users, true or false counts the online or offline ones
|
|
40
|
-
def get_user_count(is_online: nil)
|
|
41
|
-
online = { nil => '', true => '1', false => '0' }.fetch(is_online)
|
|
42
|
-
query = { 'owner' => organization_name, 'isOnline' => online }
|
|
43
|
-
get_data('get-user-count', query).to_i
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def get_user_by_email(email)
|
|
47
|
-
find_user('email' => email)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def get_user_by_phone(phone)
|
|
51
|
-
find_user('phone' => phone)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# user_id is the "id" field of the user (a UUID), not "owner/name"
|
|
55
|
-
def get_user_by_user_id(user_id)
|
|
56
|
-
find_user('userId' => user_id)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Sets the user's password. old_password can be empty when an admin sets the password of another user.
|
|
60
|
-
def set_password(owner, name, new_password, old_password: '')
|
|
61
|
-
form = { 'userOwner' => owner, 'userName' => name, 'oldPassword' => old_password,
|
|
62
|
-
'newPassword' => new_password }
|
|
63
|
-
post_form('set-password', {}, form)['status'] == 'ok'
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Checks the password of a user, e.g. User.new(owner: "casbin", name: "alice", password: "123")
|
|
67
|
-
def check_user_password(user)
|
|
68
|
-
user = User.new(user) if user.is_a?(Hash)
|
|
69
|
-
user.owner = organization_name if user.owner.to_s.empty?
|
|
70
|
-
post_json('check-user-password', { 'id' => "#{user.owner}/#{user.name}" }, user)['status'] == 'ok'
|
|
71
|
-
rescue ApiError
|
|
72
|
-
false
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
private
|
|
76
|
-
|
|
77
|
-
def find_user(query)
|
|
78
|
-
data = get_data('get-user', { 'owner' => organization_name }.merge(query))
|
|
79
|
-
data && User.new(data)
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|