ruqqus 1.1.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/.gitignore +1 -0
- data/CHANGELOG.md +6 -0
- data/README.md +13 -5
- data/exe/ruqqus-oauth +2 -2
- data/lib/ruqqus/client.rb +51 -10
- data/lib/ruqqus/token.rb +7 -31
- data/lib/ruqqus/version.rb +1 -1
- data/ruqqus.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aef052e6ba89775ce82f8d1474b12b74a0ec84260ae5abcb41b2c098e1befbf2
|
4
|
+
data.tar.gz: 79385f50df81574939d19746d8f36e9e66e075553ec980c5daa0d411cd27e60c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2039cc29cfde59766c5d249c74b49c132b709db04553848d5e8915df242dc276f2b550289bc43b58b322987851e747feede7dbe7ee89ba5ef9f0e6c096afa546
|
7
|
+
data.tar.gz: d62ca8506d89f4c8e772fa24daf57c0dca3e6d90c57a473a82eb6b0e89c10ab564058c198c5fb75d038c573b8792945d51633e440857322b2d64f8dc6867386c
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
Documentation for library API changes.
|
4
4
|
|
5
|
+
## Version 1.1.1
|
6
|
+
|
7
|
+
* BUGFIX: Added acceptance of variable args to `Token#to_json`
|
8
|
+
* BUGFIX: Fixed regex validator in `ruqqus-oauth` for the client ID
|
9
|
+
* Separated the client ID/secret from the `Token` class, and placed within `Client` to now handle this logic
|
10
|
+
|
5
11
|
## Version 1.1.0
|
6
12
|
|
7
13
|
* Implemented `Ruqqus::Token` class to handle OAuth2 authentication
|
data/README.md
CHANGED
@@ -67,7 +67,11 @@ code = 'XXXXXX' # The generated code (or the one you obtained via tradi
|
|
67
67
|
|
68
68
|
# You must implement a responsible way of storing this token for reuse.
|
69
69
|
token = Ruqqus::Token.new(client_id, client_secret, code)
|
70
|
-
client = Ruqqus::Client.new(token)
|
70
|
+
client = Ruqqus::Client.new(client_id, client_secret, token)
|
71
|
+
|
72
|
+
# Alternatively, you can create a new token and a client with a single call.
|
73
|
+
# This will perform the "grant" action of the code while creating it automatically
|
74
|
+
client = Ruqqus::Client.new(client_id, client_secret, code)
|
71
75
|
```
|
72
76
|
|
73
77
|
The token will automatically refresh itself as-needed, but you will need to handle storing its new value for repeated
|
@@ -75,12 +79,16 @@ uses. To facilitate this and make it easier, there is a callback that can be sub
|
|
75
79
|
time the access key is updated.
|
76
80
|
|
77
81
|
```ruby
|
82
|
+
# Load an existing token that has already been authorized
|
78
83
|
token = Ruqqus::Token.load_json('./token.json')
|
79
|
-
|
84
|
+
|
85
|
+
# Create your client
|
86
|
+
client = Ruqqus::Client.new(client_id, client_secret, token)
|
87
|
+
|
88
|
+
# Set the callback block to automatically update the saved token when it refreshes
|
89
|
+
client.token_refreshed do |t|
|
80
90
|
t.save_json('./token.json')
|
81
91
|
end
|
82
|
-
|
83
|
-
client = Ruqqus::Client.new(token)
|
84
92
|
```
|
85
93
|
|
86
94
|
The token obtains sensitive material, and due to the security issues of storing it in plain text, this functionality is
|
@@ -89,7 +97,7 @@ and where you store this information so that it is not compromised.
|
|
89
97
|
|
90
98
|
## Usage
|
91
99
|
|
92
|
-
See the [documentation](https://www.rubydoc.info/gems/ruqqus) for a complete API reference.
|
100
|
+
See the [documentation](https://www.rubydoc.info/gems/ruqqus) for a complete API reference (100% coverage).
|
93
101
|
|
94
102
|
### Features
|
95
103
|
|
data/exe/ruqqus-oauth
CHANGED
@@ -41,11 +41,11 @@ class RuqqusOAuth
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def ask_client_id(prompt)
|
44
|
-
prompt.ask('Client ID: ') { |q| q.validate(/^[A-
|
44
|
+
prompt.ask('Client ID: ') { |q| q.validate(/^[A-Za-z0-9_-]+$/, "Invalid client ID") }
|
45
45
|
end
|
46
46
|
|
47
47
|
def ask_client_secret(prompt)
|
48
|
-
prompt.ask('Client Secret: ') { |q| q.validate(/^[A-
|
48
|
+
prompt.ask('Client Secret: ') { |q| q.validate(/^[A-Za-z0-9_-]+$/, "Invalid client secret") }
|
49
49
|
end
|
50
50
|
|
51
51
|
def ask_client_redirect(prompt)
|
data/lib/ruqqus/client.rb
CHANGED
@@ -9,7 +9,7 @@ module Ruqqus
|
|
9
9
|
|
10
10
|
##
|
11
11
|
# The user-agent the client identified itself as.
|
12
|
-
USER_AGENT = "ruqqus-ruby/#{Ruqqus::VERSION}
|
12
|
+
USER_AGENT = "ruqqus-ruby/#{Ruqqus::VERSION}".freeze
|
13
13
|
|
14
14
|
##
|
15
15
|
# A collection of valid scopes that can be authorized.
|
@@ -20,22 +20,39 @@ module Ruqqus
|
|
20
20
|
DEFAULT_HEADERS = { 'User-Agent': USER_AGENT, 'Accept': 'application/json', 'Content-Type': 'application/json' }.freeze
|
21
21
|
|
22
22
|
##
|
23
|
-
#
|
24
|
-
|
23
|
+
# @!attribute [rw] token
|
24
|
+
# @return [Token] the OAuth2 token that grants the client authentication.
|
25
25
|
|
26
26
|
##
|
27
27
|
# @!attribute [r] identity
|
28
28
|
# @return [User] the authenticated user this client is performing actions as.
|
29
29
|
|
30
30
|
##
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
|
35
|
-
|
31
|
+
# @overload initialize(client_id, client_secret, token)
|
32
|
+
# Creates a new instance of the {Client} class with an existing token for authorization.
|
33
|
+
# @param client_id [String] the client ID of your of your application, issued after registration on Ruqqus.
|
34
|
+
# @param client_secret [String] the client secret of your of your application, issued after registration on Ruqqus.
|
35
|
+
# @param token [Token] a valid access token that has previously been granted access for the client.
|
36
|
+
#
|
37
|
+
# @overload initialize(client_id, client_secret, code)
|
38
|
+
# Creates a new instance of the {Client} class with an existing token for authorization.
|
39
|
+
# @param client_id [String] the client ID of your of your application, issued after registration on Ruqqus.
|
40
|
+
# @param client_secret [String] the client secret of your of your application, issued after registration on Ruqqus.
|
41
|
+
# @param code [String] a the code from the Oauth2 redirect to create a new {Token} and grant access to it.
|
42
|
+
def initialize(client_id, client_secret, token)
|
43
|
+
@client_id = client_id || raise(ArgumentError, 'client ID cannot be nil')
|
44
|
+
@client_secret = client_secret || raise(ArgumentError, 'client secret cannot be nil')
|
45
|
+
|
46
|
+
@token = token.is_a?(Token) ? token : Token.new(client_id, client_secret, token.to_s)
|
36
47
|
@session = nil
|
37
48
|
end
|
38
49
|
|
50
|
+
attr_reader :token
|
51
|
+
|
52
|
+
def token=(token)
|
53
|
+
@token = token || raise(ArgumentError, 'token cannot be nil')
|
54
|
+
end
|
55
|
+
|
39
56
|
# @!group Object Querying
|
40
57
|
|
41
58
|
##
|
@@ -358,10 +375,25 @@ module Ruqqus
|
|
358
375
|
|
359
376
|
# @!endgroup Object Enumeration
|
360
377
|
|
378
|
+
##
|
379
|
+
# @return [User] the authenticated user this client is performing actions as.
|
361
380
|
def identity
|
362
381
|
@me ||= User.from_json(http_get(Routes::IDENTITY))
|
363
382
|
end
|
364
383
|
|
384
|
+
##
|
385
|
+
# @overload token_refreshed(&block)
|
386
|
+
# Sets a callback to be invoked when the token is refreshed, and a new access token is assigned.
|
387
|
+
# @yieldparam token [Token] yields the newly refreshed {Token} to the block.
|
388
|
+
#
|
389
|
+
# @overload token_refreshed
|
390
|
+
# When called without a block, clears any callback that was previously assigned.
|
391
|
+
#
|
392
|
+
# @return [void]
|
393
|
+
def token_refreshed(&block)
|
394
|
+
@refreshed = block_given? ? block : nil
|
395
|
+
end
|
396
|
+
|
365
397
|
private
|
366
398
|
|
367
399
|
##
|
@@ -444,7 +476,7 @@ module Ruqqus
|
|
444
476
|
# @return [Hash] the response deserialized into a JSON hash.
|
445
477
|
# @see http_post
|
446
478
|
def http_get(uri, header = nil)
|
447
|
-
|
479
|
+
refresh_token
|
448
480
|
header ||= headers
|
449
481
|
response = RestClient.get(uri.chomp('/'), header)
|
450
482
|
@session = response.cookies['session_ruqqus'] if response.cookies['session_ruqqus']
|
@@ -463,12 +495,21 @@ module Ruqqus
|
|
463
495
|
# @return [Hash] the response deserialized into a JSON hash.
|
464
496
|
# @see http_get
|
465
497
|
def http_post(uri, params = {}, header = nil)
|
466
|
-
|
498
|
+
refresh_token
|
467
499
|
header ||= headers
|
468
500
|
response = RestClient.post(uri.chomp('/'), params, header)
|
469
501
|
@session = response.cookies['session_ruqqus'] if response.cookies['session_ruqqus']
|
470
502
|
raise(Ruqqus::Error, 'HTTP request failed') if response.code < 200 || response.code >= 300
|
471
503
|
JSON.parse(response, symbolize_names: response.body)
|
472
504
|
end
|
505
|
+
|
506
|
+
##
|
507
|
+
# @api private
|
508
|
+
# Checks if token is expired, and refreshes if so, calling the {#token_refreshed} block as if defined.
|
509
|
+
def refresh_token
|
510
|
+
return unless @token.expired?
|
511
|
+
@token.refresh(@client_id, @client_secret)
|
512
|
+
@refreshed&.call(@token)
|
513
|
+
end
|
473
514
|
end
|
474
515
|
end
|
data/lib/ruqqus/token.rb
CHANGED
@@ -37,15 +37,11 @@ module Ruqqus
|
|
37
37
|
headers = { 'User-Agent': Client::USER_AGENT, 'Accept': 'application/json', 'Content-Type': 'application/json' }
|
38
38
|
params = { code: code, client_id: client_id, client_secret: client_secret, grant_type: 'code', permanent: persist }
|
39
39
|
resp = RestClient.post('https://ruqqus.com/oauth/grant', params, headers )
|
40
|
-
|
41
|
-
@client_id = client_id
|
42
|
-
@client_secret = client_secret
|
43
40
|
@data = JSON.parse(resp.body, symbolize_names: true)
|
44
41
|
|
45
42
|
raise(Ruqqus::Error, 'failed to grant access for token') if @data[:oauth_error]
|
46
43
|
end
|
47
44
|
|
48
|
-
|
49
45
|
def access_token
|
50
46
|
@data[:access_token]
|
51
47
|
end
|
@@ -70,32 +66,14 @@ module Ruqqus
|
|
70
66
|
# Refreshes the access token and resets its time of expiration.
|
71
67
|
#
|
72
68
|
# @return [void]
|
73
|
-
def refresh
|
69
|
+
def refresh(client_id, client_secret)
|
74
70
|
headers = { 'User-Agent': Client::USER_AGENT, Authorization: "Bearer #{access_token}" }
|
75
|
-
params = { client_id:
|
71
|
+
params = { client_id: client_id, client_secret: client_secret, refresh_token: refresh_token, grant_type: 'refresh' }
|
76
72
|
resp = RestClient.post('https://ruqqus.com/oauth/grant', params, headers )
|
77
73
|
|
78
74
|
data = JSON.parse(resp.body, symbolize_names: true)
|
79
75
|
raise(Ruqqus::Error, 'failed to refresh authentication token') unless resp.code == 200 || data[:oauth_error]
|
80
76
|
@data.merge!(data)
|
81
|
-
@refreshed&.call(self)
|
82
|
-
end
|
83
|
-
|
84
|
-
##
|
85
|
-
# Sets a callback block that will be invoked when the token is refresh. This can be used to automate saving the
|
86
|
-
# token after its gets updated.
|
87
|
-
#
|
88
|
-
# @yieldparam token [Token] yields the token to the block.
|
89
|
-
#
|
90
|
-
# @return [self]
|
91
|
-
#
|
92
|
-
# @example Auto-save updated token
|
93
|
-
# token = Token.load_json('./token.json')
|
94
|
-
# token.on_refresh { |t| t.save_json('./token.json') }
|
95
|
-
def on_refresh(&block)
|
96
|
-
raise(LocalJumpError, "block required") unless block_given?
|
97
|
-
@refreshed = block
|
98
|
-
self
|
99
77
|
end
|
100
78
|
|
101
79
|
##
|
@@ -106,8 +84,8 @@ module Ruqqus
|
|
106
84
|
|
107
85
|
##
|
108
86
|
# @return [String] the object as a JSON-formatted string.
|
109
|
-
def to_json
|
110
|
-
|
87
|
+
def to_json(*_unused_)
|
88
|
+
@data.to_json
|
111
89
|
end
|
112
90
|
|
113
91
|
##
|
@@ -133,15 +111,13 @@ module Ruqqus
|
|
133
111
|
##
|
134
112
|
# Loads the object from a JSON-formatted string.
|
135
113
|
#
|
136
|
-
# @param json [String] a JSON string representing the object.
|
114
|
+
# @param json [String,Hash] a JSON string representing the object, or the parsed Hash of the JSON data.
|
137
115
|
#
|
138
116
|
# @return [Object] the loaded object.
|
139
117
|
def self.from_json(payload)
|
140
|
-
data = JSON.parse(payload, symbolize_names: true)
|
118
|
+
data = payload.is_a?(Hash) ? payload: JSON.parse(payload, symbolize_names: true)
|
141
119
|
token = allocate
|
142
|
-
token.instance_variable_set(:@
|
143
|
-
token.instance_variable_set(:@client_secret, data[:client_secret])
|
144
|
-
token.instance_variable_set(:@data, data[:data])
|
120
|
+
token.instance_variable_set(:@data, data)
|
145
121
|
token
|
146
122
|
end
|
147
123
|
end
|
data/lib/ruqqus/version.rb
CHANGED
data/ruqqus.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
|
|
7
7
|
spec.version = Ruqqus::VERSION
|
8
8
|
spec.authors = ['ForeverZer0']
|
9
9
|
spec.email = ['efreed09@gmail.com']
|
10
|
-
spec.summary = %q{A Ruby API implementation for Ruqqus, an open-source platform for online communities
|
10
|
+
spec.summary = %q{A Ruby API implementation for Ruqqus, an open-source platform for online communities}
|
11
11
|
spec.description = %q{A Ruby API implementation for Ruqqus, an open-source platform for online communities, free of censorship and moderator abuse by design.}
|
12
12
|
spec.homepage = 'https://github.com/ForeverZer0/ruqqus'
|
13
13
|
spec.license = 'MIT'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruqqus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ForeverZer0
|
@@ -145,5 +145,5 @@ rubygems_version: 3.1.4
|
|
145
145
|
signing_key:
|
146
146
|
specification_version: 4
|
147
147
|
summary: A Ruby API implementation for Ruqqus, an open-source platform for online
|
148
|
-
communities
|
148
|
+
communities
|
149
149
|
test_files: []
|