Skip to content

Commit 18f304c

Browse files
committed
πŸ› Fixed issues in option parsing by implementing Command#parse_options
- Use Shellwords for proper tokenization - Verified options file loading and CLI flag precedence - Added specs for oauth.opts usage - Added specs for all commands
1 parent 20fee75 commit 18f304c

File tree

13 files changed

+477
-152
lines changed

13 files changed

+477
-152
lines changed

β€Ž.rubocop_gradual.lockβ€Ž

Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,18 @@
22
"lib/oauth/tty/cli.rb:904168046": [
33
[6, 7, 77, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2883780555]
44
],
5-
"lib/oauth/tty/command.rb:3092098200": [
6-
[126, 23, 4, "Security/Open: The use of `Kernel#open` is a serious security risk.", 2087926481]
5+
"lib/oauth/tty/command.rb:587864942": [
6+
[146, 23, 4, "Security/Open: The use of `Kernel#open` is a serious security risk.", 2087926481]
77
],
8-
"oauth-tty.gemspec:3319279878": [
8+
"oauth-tty.gemspec:2020207654": [
99
[129, 3, 40, "Gemspec/DependencyVersion: Dependency version specification is required.", 2300588954],
1010
[131, 3, 44, "Gemspec/DependencyVersion: Dependency version specification is required.", 1905290578],
1111
[132, 3, 46, "Gemspec/DependencyVersion: Dependency version specification is required.", 4289565910]
1212
],
1313
"spec/oauth/backwards_compatibility_spec.rb:4041711732": [
1414
[3, 16, 25, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 3956042931]
1515
],
16-
"spec/oauth/tty/authorize_command_spec.rb:3270431476": [
17-
[3, 1, 53, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/tty/commands/authorize_command*_spec.rb`.", 2667806271],
18-
[14, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316],
19-
[15, 39, 21, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 3390344648],
20-
[16, 38, 20, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1228090493],
21-
[19, 7, 64, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [20].", 1559313276],
22-
[20, 7, 69, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [19].", 3030878101],
23-
[22, 7, 23, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 4174421602],
24-
[23, 7, 16, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 3492346277],
25-
[25, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262],
26-
[26, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262],
27-
[32, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262],
28-
[52, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316],
29-
[54, 7, 23, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 4174421602],
30-
[55, 7, 44, "RSpec/LeakyConstantDeclaration: Stub constant instead of declaring explicitly.", 2395720961],
31-
[57, 7, 16, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 3492346277],
32-
[68, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316],
33-
[69, 39, 21, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 3390344648],
34-
[70, 7, 23, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 4174421602],
35-
[71, 7, 16, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 3492346277],
36-
[73, 7, 45, "RSpec/LeakyConstantDeclaration: Stub constant instead of declaring explicitly.", 1997245299],
37-
[75, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262],
38-
[76, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262],
39-
[77, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262]
40-
],
4116
"spec/oauth/tty/cli_spec.rb:361981118": [
42-
[3, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/tty/cli*_spec.rb`.", 2849860169],
4317
[109, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316],
4418
[110, 38, 20, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1228090493],
4519
[111, 34, 10, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 4294324198],
@@ -65,25 +39,40 @@
6539
[212, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262],
6640
[213, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262]
6741
],
68-
"spec/oauth/tty/command_spec.rb:513689811": [
69-
[3, 1, 34, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/tty/command*_spec.rb`.", 667110152],
42+
"spec/oauth/tty/command_spec.rb:2516268945": [
7043
[9, 3, 275, "RSpec/LeakyConstantDeclaration: Stub class constant instead of declaring explicitly.", 2810654211]
7144
],
72-
"spec/oauth/tty/sign_command_spec.rb:622094174": [
73-
[3, 1, 48, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/tty/commands/sign_command*_spec.rb`.", 3251857167],
74-
[21, 7, 27, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 340848961],
75-
[69, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316],
76-
[70, 35, 21, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 3390344648],
77-
[71, 7, 23, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 4174421602],
78-
[72, 7, 16, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 3492346277],
79-
[73, 7, 67, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [74, 75].", 42619244],
80-
[74, 7, 87, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [73, 75].", 1827647110],
81-
[75, 7, 89, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [73, 74].", 1585010367],
82-
[75, 81, 13, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 4026407386],
83-
[77, 7, 27, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 340848961]
45+
"spec/oauth/tty/commands/authorize_command_spec.rb:3270431476": [
46+
[14, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316],
47+
[15, 39, 21, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 3390344648],
48+
[16, 38, 20, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1228090493],
49+
[19, 7, 64, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [20].", 1559313276],
50+
[20, 7, 69, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [19].", 3030878101],
51+
[22, 7, 23, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 4174421602],
52+
[23, 7, 16, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 3492346277],
53+
[25, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262],
54+
[26, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262],
55+
[32, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262],
56+
[52, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316],
57+
[54, 7, 23, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 4174421602],
58+
[55, 7, 44, "RSpec/LeakyConstantDeclaration: Stub constant instead of declaring explicitly.", 2395720961],
59+
[57, 7, 16, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 3492346277],
60+
[68, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316],
61+
[69, 39, 21, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 3390344648],
62+
[70, 7, 23, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 4174421602],
63+
[71, 7, 16, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 3492346277],
64+
[73, 7, 45, "RSpec/LeakyConstantDeclaration: Stub constant instead of declaring explicitly.", 1997245299],
65+
[75, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262],
66+
[76, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262],
67+
[77, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262]
68+
],
69+
"spec/oauth/tty/commands/query_command_spec.rb:1247725853": [
70+
[20, 32, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316],
71+
[23, 36, 20, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1228090493],
72+
[26, 37, 19, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 511534081],
73+
[55, 5, 20, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 4235470523]
8474
],
8575
"spec/oauth/tty_spec.rb:1891755344": [
86-
[3, 1, 25, "RSpec/EmptyExampleGroup: Empty example group detected.", 208109039],
87-
[3, 1, 25, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/tty*_spec.rb`.", 208109039]
76+
[3, 1, 25, "RSpec/EmptyExampleGroup: Empty example group detected.", 208109039]
8877
]
8978
}

β€Ž.rubocop_rspec.ymlβ€Ž

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ RSpec/InstanceVariable:
1818

1919
RSpec/NestedGroups:
2020
Enabled: false
21-
21+
2222
RSpec/ExpectInHook:
2323
Enabled: false
2424

@@ -28,3 +28,7 @@ RSpec/DescribeClass:
2828

2929
RSpec/MultipleMemoizedHelpers:
3030
Enabled: false
31+
32+
RSpec/SpecFilePathFormat:
33+
CustomTransform:
34+
"OAuth": "oauth"

β€ŽCHANGELOG.mdβ€Ž

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ Please file a bug if you notice a violation of semantic versioning.
2727
- (test) many new tests (@pboling)
2828
- (docs) CITATION.cff
2929
- support window increased, down to Ruby 2.3 (@pboling)
30+
- (test) added specs for oauth.opts usage (@pboling)
31+
- (test) added specs for all commands (@pboling)
3032

3133
### Changed
3234

@@ -42,6 +44,10 @@ Please file a bug if you notice a violation of semantic versioning.
4244

4345
### Fixed
4446

47+
- Fixed issues in option parsing by implementing Command#parse_options (@pboling)
48+
- Use Shellwords for proper tokenization
49+
- Verified options file loading and CLI flag precedence
50+
4551
### Security
4652

4753
## [1.0.5] - 2022-09-20

β€ŽREADME.mdβ€Ž

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,21 @@
2121

2222
## 🌻 Synopsis
2323

24+
OAuth 1.0a is an industry-standard protocol for authorization.
25+
It is an update to the original OAuth 1.0 protocol, and is used by many popular services.
2426

27+
This is a RubyGem for implementing OAuth 1.0 or 1.0a _clients_ and _servers_ in Ruby applications.
28+
See the sibling `oauth2` gem for OAuth 2.0, 2.1, & OIDC clients in Ruby.
29+
30+
All dependencies of this gem are signed, so it can be installed with a `HighSecurity` profile.
31+
32+
* [OAuth 1.0 Spec][oauth1-spec]
33+
* [oauth gem][sibling-gem] for OAuth 1.0 / 1.0a client and server implementations in Ruby.
34+
* [oauth2 sibling gem][sibling2-gem] for OAuth 2.0 / 2.1, & OIDC client implementations in Ruby.
35+
36+
[oauth1-spec]: http://oauth.net/core/1.0/
37+
[sibling-gem]: https://gitlab.com/ruby-oauth/oauth
38+
[sibling2-gem]: https://gitlab.com/ruby-oauth/oauth2
2539

2640
## πŸ’‘ Info you can shake a stick at
2741

@@ -138,12 +152,141 @@ NOTE: Be prepared to track down certs for signed gems and add them the same way
138152

139153
## βš™οΈ Configuration
140154

155+
The oauth-tty gem is a thin CLI over the oauth gem. You supply your consumer credentials, token credentials (when applicable), a target URI, and optional parameters, and the tool signs requests or helps you complete an OAuth 1.0/1.0a 3-legged flow.
156+
157+
What you can configure
158+
- Locations for OAuth parameters:
159+
- --header (default): send OAuth params in Authorization header
160+
- --body: send OAuth params in the request body
161+
- --query-string: send OAuth params on the query string
162+
- HTTP method: --method GET|POST|PUT|DELETE|… (default: POST)
163+
- Signature method: --signature-method HMAC-SHA1|RSA-SHA1|PLAINTEXT (default: HMAC-SHA1)
164+
- OAuth version: --version 1.0 (default: 1.0) or --no-version to omit
165+
- Nonce/timestamp: auto-generated by default; can be overridden via --nonce and --timestamp
166+
- Verbose output: --verbose prints the full signing breakdown, headers, and signature base string
167+
- XMPP mode: --xmpp emits OAuth as an XMPP stanza instead of HTTP artifacts
168+
169+
Required inputs (by command)
170+
- sign, query: --consumer-key, --consumer-secret, --token, --secret, and --uri
171+
- authorize: --consumer-key, --consumer-secret, the OAuth endpoints below, and --uri (service resource you’re authorizing for)
172+
173+
Authorization endpoints (for oauth authorize)
174+
- --request-token-url URL
175+
- --authorize-url URL
176+
- --access-token-url URL
177+
- Optional: --callback-url URL (for 1.0a), --scope SCOPE (provider-specific)
178+
179+
Providing options
180+
- CLI flags (preferred for quick usage)
181+
- Options file: use -O or --options to read additional arguments from a file. The file is tokenized by whitespace; put the same flags you’d pass on the command line, spread across lines as needed.
182+
183+
Example options file (oauth.opts)
184+
```text
185+
--consumer-key ck_123
186+
--consumer-secret cs_456
187+
--token at_789
188+
--secret ats_abc
189+
--method GET
190+
--uri https://api.example.com/v1/profile
191+
--parameters foo:bar
192+
--parameters "status=active"
193+
--header
194+
```
195+
Run with: oauth sign -O oauth.opts
141196

197+
Defaults at a glance
198+
- scheme: header
199+
- method: POST
200+
- signature method: HMAC-SHA1
201+
- oauth_version: 1.0 (omit with --no-version)
202+
- nonce, timestamp: auto-generated each run
203+
204+
Tips
205+
- For parameters you can use either key:value or already-escaped pairs like key=value. Repeat --parameters to add multiple pairs.
206+
- When using --body, only methods that support bodies should be used (e.g., POST/PUT/PATCH).
207+
- Some providers require exact parameter ordering and inclusion; use --verbose to see normalized parameters and the signature base string.
142208

143209
## πŸ”§ Basic Usage
144210

145211
In a shell run `oauth` to start the console.
146212

213+
Quick help and version
214+
```console
215+
oauth --help
216+
oauth --version # or oauth -v
217+
```
218+
219+
Sign a request (minimal)
220+
Print just the OAuth signature value for a GET request:
221+
```console
222+
oauth sign \
223+
--consumer-key ck \
224+
--consumer-secret cs \
225+
--token at \
226+
--secret ats \
227+
--method GET \
228+
--uri "https://api.example.com/v1/resource?limit=10"
229+
```
230+
231+
Sign a request (verbose, header output)
232+
```console
233+
oauth sign \
234+
--consumer-key ck \
235+
--consumer-secret cs \
236+
--token at \
237+
--secret ats \
238+
--method POST \
239+
--uri https://api.example.com/v1/resource \
240+
--parameters "status=active" \
241+
--header \
242+
--verbose
243+
```
244+
This prints OAuth parameters, normalized parameters, signature base string, Authorization header, and both raw and escaped signatures.
245+
246+
Query a protected resource
247+
Performs the signed HTTP request and prints the HTTP status and body.
248+
```console
249+
oauth query \
250+
--consumer-key ck \
251+
--consumer-secret cs \
252+
--token at \
253+
--secret ats \
254+
--method GET \
255+
--uri https://api.example.com/v1/profile \
256+
--parameters "fields=id,name"
257+
```
258+
Notes:
259+
- The CLI will append any --parameters to the request URI’s query string and sign the request.
260+
- Use --body or --header/--query-string to influence where OAuth params go; query also constructs OAuth via the consumer internally.
261+
262+
Start an OAuth 1.0a authorization flow
263+
Guides you to obtain an access token and token secret from a provider.
264+
```console
265+
oauth authorize \
266+
--consumer-key ck \
267+
--consumer-secret cs \
268+
--request-token-url https://provider.example.com/oauth/request_token \
269+
--authorize-url https://provider.example.com/oauth/authorize \
270+
--access-token-url https://provider.example.com/oauth/access_token \
271+
--callback-url https://yourapp.example.com/oauth/callback
272+
```
273+
What happens:
274+
- You’ll be shown an authorization URL to open in a browser.
275+
- After approving, the provider shows a verifier (PIN). Paste it back into the prompt.
276+
- The tool prints the access token and secret under β€œResponse:”. Save those and use them with sign/query.
277+
278+
Using an options file
279+
```console
280+
oauth sign -O oauth.opts
281+
```
282+
You can still add/override flags after -O; later flags win.
283+
284+
For more examples
285+
- Run any command without args to see its specific help.
286+
- Browse the specs under spec/oauth/tty for additional scenarios and edge cases.
287+
288+
In a shell run `oauth` to start the console.
289+
147290
For now, please see the tests for other usage.
148291

149292
## 🦷 FLOSS Funding

β€Žlib/oauth/tty/command.rbβ€Ž

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,25 @@ def option_parser
8888
end
8989
end
9090

91+
# Parse an array of CLI-like arguments into an options hash without mutating current state
92+
# This is used by the -O/--options FILE feature to load args from a file and merge them
93+
def parse_options(arguments)
94+
original_options = @options
95+
begin
96+
temp_options = {}
97+
@options = temp_options
98+
_option_parser_defaults
99+
OptionParser.new do |opts|
100+
_option_parser_common(opts)
101+
_option_parser_sign_and_query(opts)
102+
_option_parser_authorization(opts)
103+
end.parse!(arguments)
104+
temp_options
105+
ensure
106+
@options = original_options
107+
end
108+
end
109+
91110
def _option_parser_defaults
92111
options[:oauth_nonce] = OAuth::Helper.generate_key
93112
options[:oauth_signature_method] = "HMAC-SHA1"
@@ -123,7 +142,8 @@ def _option_parser_common(opts)
123142
end
124143

125144
opts.on("-O", "--options FILE", "Read options from a file") do |v|
126-
arguments = open(v).readlines.map { |l| l.chomp.split }.flatten
145+
require "shellwords"
146+
arguments = open(v).readlines.flat_map { |l| Shellwords.shellsplit(l.chomp) }
127147
options2 = parse_options(arguments)
128148
options.merge!(options2)
129149
end

β€Žspec/oauth/tty/command_spec.rbβ€Ž

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,9 @@ def build_cmd(args = [])
186186
expect(stderr.read).to eq("err!\n")
187187
end
188188
end
189+
190+
it "has no required options by default" do
191+
command = described_class.new(stdout, stdin, stderr, [])
192+
expect(command.required_options).to eq([])
193+
end
189194
end

0 commit comments

Comments
Β (0)