ruqqus 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|