glogin 0.17.0 → 0.17.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86f42105e518db6f5ded5356f3b425db907633a9844e6a96f78c43a2ffcedeae
4
- data.tar.gz: bdcee8c5d3986e72429e491210c4780083c7f66097431a8b050fe1dbfebc9187
3
+ metadata.gz: d365fbcc98249133c9c6ee63b621ebb900be53df49bfbeb5c40d5256e2e752e0
4
+ data.tar.gz: 50abb7bdfc000d36f5f84362b3550c8de20f20a265725e37782e665289085d4a
5
5
  SHA512:
6
- metadata.gz: 2fc31276166f15cf59ee139acdf3712e9d40fd77c570b910430c1ba594007e1ec4bd237ec266092a95fd6d6072da8af6994e1a8465009540cb779a6641c8052e
7
- data.tar.gz: b689331ec8d35b56a1d325d97b2e237d138c25b3975af841a91b44c97221b6e1893c71e9e0ceaddc721b7ef6793a9438cdc4660df54b47988eb3193b38b2d0ea
6
+ metadata.gz: de901931d43eabb3c9fdd785a19ae65bbae4ba111492319e9ba21d603bcabfa41fd96687a18a92d8fd95f9f2715afaac9f356e87699d64535180ce66eda05c26
7
+ data.tar.gz: ed23b4e00b79e928365dfe6ca2d6caeb3c85713296d953248da4f90d3be8e5acd9ac7daff90347372e746afea7c213a85cfcc9c0e9f378f994133e76866a238e
data/Gemfile CHANGED
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2017-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  source 'https://rubygems.org'
7
7
  gemspec
8
8
 
9
- gem 'minitest', '~>5.25', require: false
9
+ gem 'minitest', '~>6.0', require: false
10
10
  gem 'minitest-reporters', '~>1.7', require: false
11
11
  gem 'rake', '~>13.2', require: false
12
- gem 'rdoc', '~>6.11', require: false
12
+ gem 'rdoc', '~>7.0', require: false
13
13
  gem 'rubocop', '~>1.71', require: false
14
- gem 'rubocop-minitest', '>0', require: false
15
- gem 'rubocop-performance', '>0', require: false
16
- gem 'rubocop-rake', '>0', require: false
14
+ gem 'rubocop-minitest', '~>0.38', require: false
15
+ gem 'rubocop-performance', '~>1.25', require: false
16
+ gem 'rubocop-rake', '~>0.7', require: false
17
17
  gem 'simplecov', '~>0.22', require: false
18
- gem 'simplecov-cobertura', '~>2.1', require: false
18
+ gem 'simplecov-cobertura', '~>3.0', require: false
19
19
  gem 'webmock', '~>3.24', require: false
20
20
  gem 'yard', '~>0.9', require: false
data/Gemfile.lock CHANGED
@@ -9,68 +9,74 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- addressable (2.8.7)
13
- public_suffix (>= 2.0.2, < 7.0)
14
- ansi (1.5.0)
12
+ addressable (2.9.0)
13
+ public_suffix (>= 2.0.2, < 8.0)
14
+ ansi (1.6.0)
15
15
  ast (2.4.3)
16
16
  base58 (0.2.3)
17
- base64 (0.2.0)
18
- bigdecimal (3.1.9)
17
+ base64 (0.3.0)
18
+ bigdecimal (4.1.2)
19
19
  builder (3.3.0)
20
- crack (1.0.0)
20
+ crack (1.0.1)
21
21
  bigdecimal
22
22
  rexml
23
- date (3.4.1)
23
+ date (3.5.1)
24
24
  docile (1.4.1)
25
- hashdiff (1.1.2)
26
- json (2.12.0)
25
+ drb (2.2.3)
26
+ erb (6.0.2)
27
+ hashdiff (1.2.1)
28
+ json (2.19.4)
27
29
  language_server-protocol (3.17.0.5)
28
30
  lint_roller (1.1.0)
29
- minitest (5.25.5)
30
- minitest-reporters (1.7.1)
31
+ minitest (6.0.5)
32
+ drb (~> 2.0)
33
+ prism (~> 1.5)
34
+ minitest-reporters (1.8.0)
31
35
  ansi
32
36
  builder
33
- minitest (>= 5.0)
37
+ minitest (>= 5.0, < 7)
34
38
  ruby-progressbar
35
- openssl (3.3.0)
36
- parallel (1.27.0)
37
- parser (3.3.8.0)
39
+ openssl (4.0.0)
40
+ parallel (1.28.0)
41
+ parser (3.3.11.1)
38
42
  ast (~> 2.4.1)
39
43
  racc
40
- prism (1.4.0)
41
- psych (5.2.6)
44
+ prism (1.9.0)
45
+ psych (5.3.1)
42
46
  date
43
47
  stringio
44
- public_suffix (6.0.2)
48
+ public_suffix (7.0.5)
45
49
  racc (1.8.1)
46
50
  rainbow (3.1.1)
47
- rake (13.2.1)
48
- rdoc (6.13.1)
51
+ rake (13.4.2)
52
+ rdoc (7.2.0)
53
+ erb
49
54
  psych (>= 4.0.0)
50
- regexp_parser (2.10.0)
51
- rexml (3.4.1)
52
- rubocop (1.75.6)
55
+ tsort
56
+ regexp_parser (2.12.0)
57
+ rexml (3.4.4)
58
+ rubocop (1.86.1)
53
59
  json (~> 2.3)
54
60
  language_server-protocol (~> 3.17.0.2)
55
61
  lint_roller (~> 1.1.0)
56
- parallel (~> 1.10)
62
+ parallel (>= 1.10)
57
63
  parser (>= 3.3.0.2)
58
64
  rainbow (>= 2.2.2, < 4.0)
59
65
  regexp_parser (>= 2.9.3, < 3.0)
60
- rubocop-ast (>= 1.44.0, < 2.0)
66
+ rubocop-ast (>= 1.49.0, < 2.0)
61
67
  ruby-progressbar (~> 1.7)
62
68
  unicode-display_width (>= 2.4.0, < 4.0)
63
- rubocop-ast (1.44.1)
69
+ rubocop-ast (1.49.1)
64
70
  parser (>= 3.3.7.2)
65
- prism (~> 1.4)
66
- rubocop-minitest (0.38.0)
71
+ prism (~> 1.7)
72
+ rubocop-minitest (0.39.1)
67
73
  lint_roller (~> 1.1)
68
74
  rubocop (>= 1.75.0, < 2.0)
69
75
  rubocop-ast (>= 1.38.0, < 2.0)
70
- rubocop-performance (1.25.0)
76
+ rubocop-performance (1.26.1)
71
77
  lint_roller (~> 1.1)
72
78
  rubocop (>= 1.75.0, < 2.0)
73
- rubocop-ast (>= 1.38.0, < 2.0)
79
+ rubocop-ast (>= 1.47.1, < 2.0)
74
80
  rubocop-rake (0.7.1)
75
81
  lint_roller (~> 1.1)
76
82
  rubocop (>= 1.72.1)
@@ -79,20 +85,21 @@ GEM
79
85
  docile (~> 1.1)
80
86
  simplecov-html (~> 0.11)
81
87
  simplecov_json_formatter (~> 0.1)
82
- simplecov-cobertura (2.1.0)
88
+ simplecov-cobertura (3.1.0)
83
89
  rexml
84
90
  simplecov (~> 0.19)
85
- simplecov-html (0.13.1)
91
+ simplecov-html (0.13.2)
86
92
  simplecov_json_formatter (0.1.4)
87
- stringio (3.1.7)
88
- unicode-display_width (3.1.4)
89
- unicode-emoji (~> 4.0, >= 4.0.4)
90
- unicode-emoji (4.0.4)
91
- webmock (3.25.1)
93
+ stringio (3.2.0)
94
+ tsort (0.2.0)
95
+ unicode-display_width (3.2.0)
96
+ unicode-emoji (~> 4.1)
97
+ unicode-emoji (4.2.0)
98
+ webmock (3.26.2)
92
99
  addressable (>= 2.8.0)
93
100
  crack (>= 0.3.2)
94
101
  hashdiff (>= 0.4.0, < 2.0.0)
95
- yard (0.9.37)
102
+ yard (0.9.42)
96
103
 
97
104
  PLATFORMS
98
105
  arm64-darwin-22
@@ -103,16 +110,16 @@ PLATFORMS
103
110
 
104
111
  DEPENDENCIES
105
112
  glogin!
106
- minitest (~> 5.25)
113
+ minitest (~> 6.0)
107
114
  minitest-reporters (~> 1.7)
108
115
  rake (~> 13.2)
109
- rdoc (~> 6.11)
116
+ rdoc (~> 7.0)
110
117
  rubocop (~> 1.71)
111
- rubocop-minitest (> 0)
112
- rubocop-performance (> 0)
113
- rubocop-rake (> 0)
118
+ rubocop-minitest (~> 0.38)
119
+ rubocop-performance (~> 1.25)
120
+ rubocop-rake (~> 0.7)
114
121
  simplecov (~> 0.22)
115
- simplecov-cobertura (~> 2.1)
122
+ simplecov-cobertura (~> 3.0)
116
123
  webmock (~> 3.24)
117
124
  yard (~> 0.9)
118
125
 
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2017-2025 Yegor Bugayenko
3
+ Copyright (c) 2017-2026 Yegor Bugayenko
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the 'Software'), to deal
data/LICENSES/MIT.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2017-2025 Yegor Bugayenko
3
+ Copyright (c) 2017-2026 Yegor Bugayenko
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the 'Software'), to deal
data/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  [![Hits-of-Code](https://hitsofcode.com/github/yegor256/glogin)](https://hitsofcode.com/view/github/yegor256/glogin)
13
13
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/yegor256/glogin/blob/master/LICENSE.txt)
14
14
 
15
- This simple gem will help you enable login/logout through
15
+ This simple gem helps you enable login/logout through
16
16
  [GitHub OAuth][doc]
17
17
  for your web application. This is how it works with
18
18
  [Sinatra](http://www.sinatrarb.com/),
@@ -60,13 +60,13 @@ end
60
60
  ```
61
61
 
62
62
  If the `glogin` cookie is coming in and contains valid data,
63
- a local variable `@user` will be set to something like this:
63
+ a local variable `@user` is set to something like this:
64
64
 
65
65
  ```ruby
66
66
  { 'id' => '526301', 'login' => 'yegor256', 'avatar' => 'http://...' }
67
67
  ```
68
68
 
69
- If the `secret` is an empty string, the encryption will be disabled.
69
+ If the `secret` is an empty string, the encryption is disabled.
70
70
 
71
71
  Next, we need a URL for GitHub OAuth callback:
72
72
 
@@ -94,10 +94,10 @@ It is recommended to provide the third "context" parameter to
94
94
  `GLogin::Cookie::Closed` and `GLogin::Cookie::Open` constructors, in order
95
95
  to enforce stronger security. The context may include the `User-Agent`
96
96
  HTTP header of the user, their IP address, and so on. When anything
97
- changes on the user side, they will be forced to re-login.
97
+ changes on the user side, they are forced to re-login.
98
98
 
99
- One more thing is the login URL you will need for your front page. Here
100
- it is:
99
+ One more thing is the login URL you need for your front page.
100
+ Here it is:
101
101
 
102
102
  ```ruby
103
103
  settings.glogin.login_uri
@@ -105,9 +105,9 @@ settings.glogin.login_uri
105
105
 
106
106
  For unit testing, you can just provide an empty string as a `secret` for
107
107
  `GLogin::Cookie::Open` and `GLogin::Cookie::Closed`
108
- and the encryption will be disabled:
109
- whatever comes from the cookie will be trusted. For testing
110
- it will be convenient to provide a user name in a query string, like:
108
+ and the encryption is disabled: whatever comes from the cookie is trusted.
109
+ For testing it is convenient to provide a user name in a query string,
110
+ like:
111
111
 
112
112
  ```text
113
113
  http://localhost:9292/?glogin=tester
@@ -144,7 +144,7 @@ decrypted = codec.decrypt(encrypted)
144
144
  Read
145
145
  [these guidelines](https://www.yegor256.com/2014/04/15/github-guidelines.html).
146
146
  Make sure your build is green before you contribute
147
- your pull request. You will need to have
147
+ your pull request. You need to have
148
148
  [Ruby](https://www.ruby-lang.org/en/) 2.3+ and
149
149
  [Bundler](https://bundler.io/) installed. Then:
150
150
 
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #
4
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
4
+ # SPDX-FileCopyrightText: Copyright (c) 2017-2026 Yegor Bugayenko
5
5
  # SPDX-License-Identifier: MIT
6
6
 
7
7
  require 'rubygems'
@@ -32,6 +32,7 @@ require 'yard'
32
32
  desc 'Build Yard documentation'
33
33
  YARD::Rake::YardocTask.new do |t|
34
34
  t.files = ['lib/**/*.rb']
35
+ t.options = ['--fail-on-warning']
35
36
  end
36
37
 
37
38
  require 'rubocop/rake_task'
data/glogin.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2017-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require 'English'
@@ -19,8 +19,8 @@ Gem::Specification.new do |s|
19
19
  s.description = 'Enables login/logout functionality for a Ruby web app'
20
20
  s.authors = ['Yegor Bugayenko']
21
21
  s.email = 'yegor256@gmail.com'
22
- s.homepage = 'http://github.com/yegor256/glogin'
23
- s.files = `git ls-files`.split($RS)
22
+ s.homepage = 'https://github.com/yegor256/glogin'
23
+ s.files = `git ls-files | grep -v -E '^(test/|\\.|renovate)'`.split($RS)
24
24
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
25
25
  s.rdoc_options = ['--charset=UTF-8']
26
26
  s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
data/lib/glogin/auth.rb CHANGED
@@ -1,16 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2017-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require 'cgi'
7
7
  require 'json'
8
8
  require 'net/http'
9
+ require 'openssl'
10
+ require 'socket'
9
11
  require 'uri'
12
+ require_relative 'errors'
10
13
 
11
14
  # GLogin main module.
12
15
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
13
- # Copyright:: Copyright (c) 2017-2025 Yegor Bugayenko
16
+ # Copyright:: Copyright (c) 2017-2026 Yegor Bugayenko
14
17
  # License:: MIT
15
18
  module GLogin
16
19
  # GitHub authentication mechanism.
@@ -92,9 +95,9 @@ module GLogin
92
95
  def user(code)
93
96
  if @secret.empty?
94
97
  return {
95
- 'id' => 526_301,
96
- 'login' => 'yegor256',
97
- 'avatar_url' => 'https://github.com/yegor256.png'
98
+ 'id' => 1_024_025,
99
+ 'login' => 'torvalds',
100
+ 'avatar_url' => 'https://github.com/torvalds.png'
98
101
  }
99
102
  end
100
103
  raise 'Code can\'t be nil' if code.nil?
@@ -107,7 +110,7 @@ module GLogin
107
110
  req['Accept-Header'] = 'application/json'
108
111
  token = access_token(code)
109
112
  req['Authorization'] = "token #{token}"
110
- res = http.request(req)
113
+ res = perform(http, req, "user fetch with token #{escape(token)}")
111
114
  raise "HTTP error ##{res.code} with token #{escape(token)}: #{res.body}" unless res.code == '200'
112
115
  JSON.parse(res.body)
113
116
  end
@@ -128,7 +131,7 @@ module GLogin
128
131
  'client_secret' => @secret
129
132
  )
130
133
  req['Accept'] = 'application/json'
131
- res = http.request(req)
134
+ res = perform(http, req, "token exchange with code #{escape(code)}")
132
135
  raise "HTTP error ##{res.code} with code #{escape(code)}: #{res.body}" unless res.code == '200'
133
136
  json = JSON.parse(res.body)
134
137
  token = json['access_token']
@@ -136,6 +139,28 @@ module GLogin
136
139
  token
137
140
  end
138
141
 
142
+ # Low-level network errors that should be wrapped in
143
+ # GLogin::ConnectionError instead of leaking out as provider-specific
144
+ # classes like Socket::ResolutionError. SystemCallError covers the
145
+ # full Errno:: family (ECONNREFUSED, ECONNRESET, ETIMEDOUT, etc.).
146
+ CONNECTION_ERRORS = [
147
+ SocketError,
148
+ SystemCallError,
149
+ OpenSSL::SSL::SSLError,
150
+ Net::OpenTimeout,
151
+ Net::ReadTimeout,
152
+ Net::WriteTimeout,
153
+ EOFError,
154
+ IOError
155
+ ].freeze
156
+ private_constant :CONNECTION_ERRORS
157
+
158
+ def perform(http, req, context)
159
+ http.request(req)
160
+ rescue *CONNECTION_ERRORS => e
161
+ raise GLogin::ConnectionError, "#{e.class}: #{e.message} (during #{context})"
162
+ end
163
+
139
164
  def escape(txt)
140
165
  prefix = 4
141
166
  [
data/lib/glogin/codec.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2017-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require 'base58'
@@ -11,7 +11,7 @@ require 'securerandom'
11
11
 
12
12
  # Codec.
13
13
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
14
- # Copyright:: Copyright (c) 2017-2025 Yegor Bugayenko
14
+ # Copyright:: Copyright (c) 2017-2026 Yegor Bugayenko
15
15
  # License:: MIT
16
16
  module GLogin
17
17
  # The codec for encrypting and decrypting text.
@@ -87,6 +87,7 @@ module GLogin
87
87
  # end
88
88
  def decrypt(text)
89
89
  raise 'Text can\'t be nil' if text.nil?
90
+ text = text.to_s.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
90
91
  if @secret.empty?
91
92
  text
92
93
  else
@@ -154,7 +155,7 @@ module GLogin
154
155
  private
155
156
 
156
157
  def digest(len)
157
- Digest::SHA1.hexdigest(@secret)[0..len - 1]
158
+ Digest::SHA1.hexdigest(@secret)[0..(len - 1)]
158
159
  end
159
160
 
160
161
  def cipher
data/lib/glogin/cookie.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2017-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require 'base64'
@@ -10,7 +10,7 @@ require_relative 'codec'
10
10
 
11
11
  # GLogin main module.
12
12
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
13
- # Copyright:: Copyright (c) 2017-2025 Yegor Bugayenko
13
+ # Copyright:: Copyright (c) 2017-2026 Yegor Bugayenko
14
14
  # License:: MIT
15
15
  module GLogin
16
16
  # Split symbol inside the cookie text
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2017-2026 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ # GLogin main module.
7
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
8
+ # Copyright:: Copyright (c) 2017-2026 Yegor Bugayenko
9
+ # License:: MIT
10
+ module GLogin
11
+ # Base class for all GLogin-specific errors.
12
+ #
13
+ # @example Rescuing any GLogin error
14
+ # begin
15
+ # auth.user(code)
16
+ # rescue GLogin::Error => e
17
+ # logger.error("GLogin failed: #{e.message}")
18
+ # end
19
+ class Error < StandardError
20
+ end
21
+
22
+ # Raised when GLogin cannot reach GitHub due to a low-level networking
23
+ # issue (DNS resolution failure, connection refused, TLS handshake
24
+ # failure, read timeout, etc.).
25
+ #
26
+ # The original exception is preserved via Ruby's exception chaining
27
+ # mechanism and is accessible through +#cause+.
28
+ #
29
+ # @example Handling a connection failure gracefully
30
+ # begin
31
+ # auth.user(code)
32
+ # rescue GLogin::ConnectionError => e
33
+ # logger.warn("GitHub unreachable: #{e.message} (cause: #{e.cause.class})")
34
+ # halt(503, 'GitHub is unreachable, please try again later')
35
+ # end
36
+ class ConnectionError < Error
37
+ end
38
+ end
@@ -1,22 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2017-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  # GLogin main module.
7
7
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
8
- # Copyright:: Copyright (c) 2017-2025 Yegor Bugayenko
8
+ # Copyright:: Copyright (c) 2017-2026 Yegor Bugayenko
9
9
  # License:: MIT
10
10
  module GLogin
11
11
  # Current version of the GLogin gem.
12
12
  #
13
13
  # @example Check the gem version
14
14
  # puts GLogin::VERSION
15
- # # => "0.17.0"
15
+ # # => "0.17.2"
16
16
  #
17
17
  # @example Version comparison
18
18
  # if Gem::Version.new(GLogin::VERSION) >= Gem::Version.new('0.1.0')
19
19
  # puts "Using GLogin version 0.1.0 or newer"
20
20
  # end
21
- VERSION = '0.17.0'
21
+ VERSION = '0.17.2'
22
22
  end
data/lib/glogin.rb CHANGED
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2017-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
+ require_relative 'glogin/errors'
6
7
  require_relative 'glogin/auth'
7
8
  require_relative 'glogin/cookie'
8
9
  require_relative 'glogin/version'
@@ -33,7 +34,7 @@ require_relative 'glogin/version'
33
34
  # end
34
35
  #
35
36
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
36
- # Copyright:: Copyright (c) 2017-2025 Yegor Bugayenko
37
+ # Copyright:: Copyright (c) 2017-2026 Yegor Bugayenko
37
38
  # License:: MIT
38
39
  module GLogin
39
40
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glogin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.0
4
+ version: 0.17.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
@@ -59,22 +59,6 @@ extra_rdoc_files:
59
59
  - LICENSE.txt
60
60
  - README.md
61
61
  files:
62
- - ".0pdd.yml"
63
- - ".gitattributes"
64
- - ".github/workflows/actionlint.yml"
65
- - ".github/workflows/codecov.yml"
66
- - ".github/workflows/copyrights.yml"
67
- - ".github/workflows/markdown-lint.yml"
68
- - ".github/workflows/pdd.yml"
69
- - ".github/workflows/rake.yml"
70
- - ".github/workflows/reuse.yml"
71
- - ".github/workflows/typos.yml"
72
- - ".github/workflows/xcop.yml"
73
- - ".github/workflows/yamllint.yml"
74
- - ".gitignore"
75
- - ".pdd"
76
- - ".rubocop.yml"
77
- - ".rultor.yml"
78
62
  - Gemfile
79
63
  - Gemfile.lock
80
64
  - LICENSE.txt
@@ -87,15 +71,10 @@ files:
87
71
  - lib/glogin/auth.rb
88
72
  - lib/glogin/codec.rb
89
73
  - lib/glogin/cookie.rb
74
+ - lib/glogin/errors.rb
90
75
  - lib/glogin/version.rb
91
76
  - logo.svg
92
- - renovate.json
93
- - test/glogin/test_auth.rb
94
- - test/glogin/test_codec.rb
95
- - test/glogin/test_cookie.rb
96
- - test/test__helper.rb
97
- - test/test_glogin.rb
98
- homepage: http://github.com/yegor256/glogin
77
+ homepage: https://github.com/yegor256/glogin
99
78
  licenses:
100
79
  - MIT
101
80
  metadata:
@@ -115,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
115
94
  - !ruby/object:Gem::Version
116
95
  version: '0'
117
96
  requirements: []
118
- rubygems_version: 3.6.7
97
+ rubygems_version: 3.6.9
119
98
  specification_version: 4
120
99
  summary: Login/logout via GitHub OAuth for your web app
121
100
  test_files: []
data/.0pdd.yml DELETED
@@ -1,8 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
- ---
4
- errors:
5
- - yegor256@gmail.com
6
- # alerts:
7
- # github:
8
- # - yegor256
data/.gitattributes DELETED
@@ -1,7 +0,0 @@
1
- # Check out all text files in UNIX format, with LF as end of line
2
- # Don't change this file. If you have any ideas about it, please
3
- # submit a separate issue about it and we'll discuss.
4
-
5
- * text=auto eol=lf
6
- *.java ident
7
- *.xml ident
@@ -1,25 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
- ---
4
- # yamllint disable rule:line-length
5
- name: actionlint
6
- 'on':
7
- push:
8
- branches:
9
- - master
10
- pull_request:
11
- branches:
12
- - master
13
- jobs:
14
- actionlint:
15
- timeout-minutes: 15
16
- runs-on: ubuntu-24.04
17
- steps:
18
- - uses: actions/checkout@v4
19
- - name: Download actionlint
20
- id: get_actionlint
21
- run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
22
- shell: bash
23
- - name: Check workflow files
24
- run: ${{ steps.get_actionlint.outputs.executable }} -color
25
- shell: bash
@@ -1,27 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
- ---
4
- # yamllint disable rule:line-length
5
- name: codecov
6
- 'on':
7
- push:
8
- branches:
9
- - master
10
- jobs:
11
- codecov:
12
- timeout-minutes: 15
13
- runs-on: ubuntu-24.04
14
- steps:
15
- - uses: actions/checkout@v4
16
- - uses: ruby/setup-ruby@v1
17
- with:
18
- ruby-version: 3.3
19
- bundler-cache: true
20
- - run: bundle config set --global path "$(pwd)/vendor/bundle"
21
- - run: bundle install --no-color
22
- - run: bundle exec rake
23
- - uses: codecov/codecov-action@v5
24
- with:
25
- token: ${{ secrets.CODECOV_TOKEN }}
26
- files: coverage/.resultset.json
27
- fail_ci_if_error: true
@@ -1,15 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
- ---
4
- # yamllint disable rule:line-length
5
- name: copyrights
6
- 'on':
7
- push:
8
- pull_request:
9
- jobs:
10
- copyrights:
11
- timeout-minutes: 15
12
- runs-on: ubuntu-24.04
13
- steps:
14
- - uses: actions/checkout@v4
15
- - uses: yegor256/copyrights-action@0.0.8
@@ -1,23 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
- ---
4
- # yamllint disable rule:line-length
5
- name: markdown-lint
6
- 'on':
7
- push:
8
- branches:
9
- - master
10
- pull_request:
11
- branches:
12
- - master
13
- paths-ignore: ['paper/**', 'sandbox/**']
14
- concurrency:
15
- group: markdown-lint-${{ github.ref }}
16
- cancel-in-progress: true
17
- jobs:
18
- markdown-lint:
19
- timeout-minutes: 15
20
- runs-on: ubuntu-24.04
21
- steps:
22
- - uses: actions/checkout@v4
23
- - uses: articulate/actions-markdownlint@v1
@@ -1,19 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
- ---
4
- # yamllint disable rule:line-length
5
- name: pdd
6
- 'on':
7
- push:
8
- branches:
9
- - master
10
- pull_request:
11
- branches:
12
- - master
13
- jobs:
14
- pdd:
15
- timeout-minutes: 15
16
- runs-on: ubuntu-24.04
17
- steps:
18
- - uses: actions/checkout@v4
19
- - uses: volodya-lombrozo/pdd-action@master
@@ -1,28 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
- ---
4
- # yamllint disable rule:line-length
5
- name: rake
6
- 'on':
7
- push:
8
- branches:
9
- - master
10
- pull_request:
11
- branches:
12
- - master
13
- jobs:
14
- rake:
15
- strategy:
16
- matrix:
17
- os: [ubuntu-24.04, macos-15]
18
- ruby: [3.3]
19
- runs-on: ${{ matrix.os }}
20
- steps:
21
- - uses: actions/checkout@v4
22
- - uses: ruby/setup-ruby@v1
23
- with:
24
- ruby-version: ${{ matrix.ruby }}
25
- bundler-cache: true
26
- - run: bundle config set --global path "$(pwd)/vendor/bundle"
27
- - run: bundle install --no-color
28
- - run: bundle exec rake
@@ -1,19 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
- ---
4
- # yamllint disable rule:line-length
5
- name: reuse
6
- 'on':
7
- push:
8
- branches:
9
- - master
10
- pull_request:
11
- branches:
12
- - master
13
- jobs:
14
- reuse:
15
- timeout-minutes: 15
16
- runs-on: ubuntu-24.04
17
- steps:
18
- - uses: actions/checkout@v4
19
- - uses: fsfe/reuse-action@v5
@@ -1,19 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
- ---
4
- # yamllint disable rule:line-length
5
- name: typos
6
- 'on':
7
- push:
8
- branches:
9
- - master
10
- pull_request:
11
- branches:
12
- - master
13
- jobs:
14
- typos:
15
- timeout-minutes: 15
16
- runs-on: ubuntu-24.04
17
- steps:
18
- - uses: actions/checkout@v4
19
- - uses: crate-ci/typos@v1.32.0
@@ -1,15 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
- ---
4
- # yamllint disable rule:line-length
5
- name: xcop
6
- 'on':
7
- push:
8
- pull_request:
9
- jobs:
10
- xcop:
11
- timeout-minutes: 15
12
- runs-on: ubuntu-24.04
13
- steps:
14
- - uses: actions/checkout@v4
15
- - uses: g4s8/xcop-action@master
@@ -1,19 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
- ---
4
- # yamllint disable rule:line-length
5
- name: yamllint
6
- 'on':
7
- push:
8
- branches:
9
- - master
10
- pull_request:
11
- branches:
12
- - master
13
- jobs:
14
- yamllint:
15
- timeout-minutes: 15
16
- runs-on: ubuntu-24.04
17
- steps:
18
- - uses: actions/checkout@v4
19
- - uses: ibiqlik/action-yamllint@v3
data/.gitignore DELETED
@@ -1,10 +0,0 @@
1
- .bundle/
2
- .DS_Store
3
- .idea/
4
- .yardoc/
5
- *.gem
6
- coverage/
7
- doc/
8
- node_modules/
9
- rdoc/
10
- vendor/
data/.pdd DELETED
@@ -1,7 +0,0 @@
1
- --source=.
2
- --verbose
3
- --exclude target/**/*
4
- --exclude coverage/**/*
5
- --rule min-words:20
6
- --rule min-estimate:15
7
- --rule max-estimate:90
data/.rubocop.yml DELETED
@@ -1,36 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
- ---
4
- AllCops:
5
- Exclude:
6
- - 'bin/**/*'
7
- - 'assets/**/*'
8
- - 'vendor/**/*'
9
- DisplayCopNames: true
10
- TargetRubyVersion: 2.3
11
- SuggestExtensions: false
12
- NewCops: enable
13
- plugins:
14
- - rubocop-minitest
15
- - rubocop-performance
16
- - rubocop-rake
17
- Minitest/EmptyLineBeforeAssertionMethods:
18
- Enabled: false
19
- Gemspec/RequiredRubyVersion:
20
- Enabled: false
21
- Metrics/MethodLength:
22
- Enabled: false
23
- Style/ClassAndModuleChildren:
24
- Enabled: false
25
- Layout/MultilineMethodCallIndentation:
26
- Enabled: false
27
- Metrics/AbcSize:
28
- Enabled: false
29
- Metrics/BlockLength:
30
- Max: 30
31
- Metrics/CyclomaticComplexity:
32
- Max: 20
33
- Metrics/PerceivedComplexity:
34
- Max: 20
35
- Layout/EmptyLineAfterGuardClause:
36
- Enabled: false
data/.rultor.yml DELETED
@@ -1,26 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
- ---
4
- # yamllint disable rule:line-length
5
- docker:
6
- image: yegor256/ruby
7
- assets:
8
- rubygems.yml: yegor256/home#assets/rubygems.yml
9
- install: |
10
- pdd -f /dev/null
11
- bundle install --no-color
12
- release:
13
- pre: false
14
- script: |-
15
- [[ "${tag}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || exit -1
16
- bundle exec rake
17
- rm -rf *.gem
18
- sed -i "s/0\.0\.0/${tag}/g" lib/glogin/version.rb
19
- git add lib/glogin/version.rb
20
- git commit -m "version set to ${tag}"
21
- gem build glogin.gemspec
22
- chmod 0600 ../rubygems.yml
23
- gem push *.gem --config-file ../rubygems.yml
24
- merge:
25
- script: |-
26
- bundle exec rake
data/renovate.json DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
- "extends": [
4
- "config:base"
5
- ]
6
- }
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
4
- # SPDX-License-Identifier: MIT
5
-
6
- require_relative '../../lib/glogin/cookie'
7
- require_relative '../test__helper'
8
-
9
- class TestAuth < Minitest::Test
10
- def test_authenticate_via_https
11
- auth = GLogin::Auth.new('1234', '4433', 'https://example.org')
12
- stub_request(:post, 'https://github.com/login/oauth/access_token').to_return(
13
- body: {
14
- access_token: 'some-token'
15
- }.to_json
16
- )
17
- stub_request(:get, 'https://api.github.com/user').to_return(
18
- body: {
19
- auth_code: '437849732894732',
20
- login: 'yegor256'
21
- }.to_json
22
- )
23
- user = auth.user('437849732894732')
24
- assert_equal('yegor256', user['login'])
25
- end
26
-
27
- def test_login_uri
28
- auth = GLogin::Auth.new(
29
- 'client_id', 'client_secret', 'http://www.example.com/github-oauth'
30
- )
31
- assert(
32
- auth.login_uri.start_with?(
33
- 'https://github.com/login/oauth/authorize'
34
- )
35
- )
36
- end
37
-
38
- def test_get_fake_user
39
- auth = GLogin::Auth.new('99999', '', 'http://www.example.com/github-oauth')
40
- assert_equal('yegor256', auth.user('1234567890')['login'])
41
- end
42
-
43
- def test_failed_authentication
44
- auth = GLogin::Auth.new('1234', '4433', 'https://example.org')
45
- stub_request(:post, 'https://github.com/login/oauth/access_token').to_return(status: 401)
46
- e = assert_raises(StandardError) { auth.user('437849732894732') }
47
- assert_includes(e.message, 'with code "43784***')
48
- end
49
-
50
- def test_broken_json
51
- auth = GLogin::Auth.new('1234', '4433', 'https://example.org')
52
- stub_request(:post, 'https://github.com/login/oauth/access_token').to_return(body: 'Hello!')
53
- e = assert_raises(StandardError) { auth.user('47839893') }
54
- assert_includes(e.message, 'unexpected', e)
55
- end
56
-
57
- def test_no_token_in_json
58
- auth = GLogin::Auth.new('1234', '4433', 'https://example.org')
59
- stub_request(:post, 'https://github.com/login/oauth/access_token').to_return(body: '{}')
60
- e = assert_raises(StandardError) { auth.user('47839893') }
61
- assert_includes(e.message, 'There is no \'access_token\'', e)
62
- end
63
- end
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
4
- # SPDX-License-Identifier: MIT
5
-
6
- require 'base64'
7
- require_relative '../../lib/glogin/codec'
8
- require_relative '../test__helper'
9
-
10
- class TestCodec < Minitest::Test
11
- def test_encodes_and_decodes
12
- text = 'This is the text, дорогой товарищ'
13
- assert_equal(
14
- text,
15
- Base64.decode64(Base64.encode64(text)).force_encoding('UTF-8')
16
- )
17
- end
18
-
19
- def test_encrypts_and_decrypts
20
- crypt = GLogin::Codec.new('this is the secret key!')
21
- text = 'This is the text, товарищ'
22
- assert_equal(text, crypt.decrypt(crypt.encrypt(text)))
23
- end
24
-
25
- def test_decrypts_with_invalid_password
26
- assert_raises GLogin::Codec::DecodingError do
27
- GLogin::Codec.new('the wrong key').decrypt(
28
- GLogin::Codec.new('the right key').encrypt('the text')
29
- )
30
- end
31
- end
32
-
33
- def test_decrypts_broken_base58
34
- %w[abc0 abcO abcl abcI].each do |t|
35
- assert_raises GLogin::Codec::DecodingError do
36
- GLogin::Codec.new('some-key').decrypt(t)
37
- end
38
- end
39
- end
40
-
41
- def test_encrypts_into_plain_string
42
- text = GLogin::Codec.new('6hFGrte5LLmwi').encrypt("K&j\n\n\tuIpwp00{]=")
43
- assert_match(/^[a-zA-Z0-9]+$/, text, text)
44
- refute_includes(text, "\n", text)
45
- end
46
-
47
- def test_encrypts_using_base64
48
- codec = GLogin::Codec.new('6hFGrte5LLmwi', base64: true)
49
- text = 'Hello, world!'
50
- enc = codec.encrypt(text)
51
- assert_equal(text, codec.decrypt(enc))
52
- end
53
-
54
- def test_decrypts_broken_text
55
- assert_raises GLogin::Codec::DecodingError do
56
- GLogin::Codec.new('the key').decrypt('этот текст не был зашифрован')
57
- end
58
- end
59
-
60
- def test_decrypts_broken_text_with_empty_key
61
- assert_raises GLogin::Codec::DecodingError do
62
- GLogin::Codec.new('key').decrypt('')
63
- end
64
- end
65
-
66
- def test_encrypts_and_decrypts_with_empty_key
67
- crypt = GLogin::Codec.new
68
- text = 'This is the text, дорогой друг!'
69
- assert_equal(text, crypt.decrypt(crypt.encrypt(text)))
70
- end
71
- end
@@ -1,88 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
4
- # SPDX-License-Identifier: MIT
5
-
6
- require_relative '../../lib/glogin/cookie'
7
- require_relative '../test__helper'
8
-
9
- class TestCookie < Minitest::Test
10
- def test_encrypts_and_decrypts
11
- secret = 'this&84-- (832=_'
12
- user = GLogin::Cookie::Closed.new(
13
- GLogin::Cookie::Open.new(
14
- JSON.parse(
15
- "{\"id\":123,\"node_id\":\"how are you?\",
16
- \"login\":\"yegor256\",
17
- \"avatar_url\":\"https://avatars1.githubusercontent.com/u/526301\"}"
18
- ),
19
- secret
20
- ).to_s,
21
- secret
22
- ).to_user
23
- assert_equal('yegor256', user['login'])
24
- assert_equal('https://avatars1.githubusercontent.com/u/526301', user['avatar_url'])
25
- end
26
-
27
- def test_encrypts_and_decrypts_with_context
28
- secret = 'kfdj7hjsywhs6hjshr7shsw990s'
29
- context = '127.0.0.1'
30
- user = GLogin::Cookie::Closed.new(
31
- GLogin::Cookie::Open.new(
32
- JSON.parse('{"id":"123","login":"jeffrey","avatar_url":"#"}'),
33
- secret,
34
- context
35
- ).to_s,
36
- secret,
37
- context
38
- ).to_user
39
- assert_equal('123', user['id'])
40
- assert_equal('jeffrey', user['login'])
41
- assert_equal('#', user['avatar_url'])
42
- end
43
-
44
- def test_decrypts_in_test_mode
45
- user = GLogin::Cookie::Closed.new(
46
- '123|test|http://example.com', ''
47
- ).to_user
48
- assert_equal('123', user['id'])
49
- assert_equal('test', user['login'])
50
- assert_equal('http://example.com', user['avatar_url'])
51
- end
52
-
53
- def test_decrypts_in_test_mode_with_context
54
- user = GLogin::Cookie::Closed.new(
55
- '123', '', 'some context'
56
- ).to_user
57
- assert_equal('123', user['id'])
58
- assert_nil(user['login'])
59
- assert_nil(user['avatar_url'])
60
- end
61
-
62
- def test_fails_on_broken_text
63
- assert_raises GLogin::Codec::DecodingError do
64
- GLogin::Cookie::Closed.new(
65
- GLogin::Cookie::Open.new(
66
- JSON.parse('{"login":"x","avatar_url":"x","id":"1"}'),
67
- 'secret-1'
68
- ).to_s,
69
- 'secret-2'
70
- ).to_user
71
- end
72
- end
73
-
74
- def test_fails_on_wrong_context
75
- secret = 'fdjruewoijs789fdsufds89f7ds89fs'
76
- assert_raises GLogin::Codec::DecodingError do
77
- GLogin::Cookie::Closed.new(
78
- GLogin::Cookie::Open.new(
79
- JSON.parse('{"login":"x","avatar_url":"x","id":""}'),
80
- secret,
81
- 'context-1'
82
- ).to_s,
83
- secret,
84
- 'context-2'
85
- ).to_user
86
- end
87
- end
88
- end
data/test/test__helper.rb DELETED
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
4
- # SPDX-License-Identifier: MIT
5
-
6
- $stdout.sync = true
7
-
8
- require 'simplecov'
9
- require 'simplecov-cobertura'
10
- unless SimpleCov.running || ENV['PICKS']
11
- SimpleCov.command_name('test')
12
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
13
- [
14
- SimpleCov::Formatter::HTMLFormatter,
15
- SimpleCov::Formatter::CoberturaFormatter
16
- ]
17
- )
18
- SimpleCov.minimum_coverage 100
19
- SimpleCov.minimum_coverage_by_file 95
20
- SimpleCov.start do
21
- add_filter 'test/'
22
- add_filter 'vendor/'
23
- add_filter 'target/'
24
- track_files 'lib/**/*.rb'
25
- track_files '*.rb'
26
- end
27
- end
28
-
29
- require 'minitest/autorun'
30
- require 'minitest/reporters'
31
- require 'webmock/minitest'
32
- Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]
data/test/test_glogin.rb DELETED
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
4
- # SPDX-License-Identifier: MIT
5
-
6
- require_relative '../lib/glogin'
7
- require_relative 'test__helper'
8
-
9
- # GLogin main module test.
10
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
11
- # Copyright:: Copyright (c) 2017-2025 Yegor Bugayenko
12
- # License:: MIT
13
- class TestGLogin < Minitest::Test
14
- def test_basic
15
- # Nothing yet
16
- end
17
- end