Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/jwt_authenticatable_spec.rb')
-rw-r--r--spec/lib/gitlab/jwt_authenticatable_spec.rb163
1 files changed, 133 insertions, 30 deletions
diff --git a/spec/lib/gitlab/jwt_authenticatable_spec.rb b/spec/lib/gitlab/jwt_authenticatable_spec.rb
index 36bb46cb250..92d5feceb75 100644
--- a/spec/lib/gitlab/jwt_authenticatable_spec.rb
+++ b/spec/lib/gitlab/jwt_authenticatable_spec.rb
@@ -14,17 +14,12 @@ RSpec.describe Gitlab::JwtAuthenticatable do
end
before do
- begin
- File.delete(test_class.secret_path)
- rescue Errno::ENOENT
- end
+ FileUtils.rm_f(test_class.secret_path)
test_class.write_secret
end
- describe '.secret' do
- subject(:secret) { test_class.secret }
-
+ shared_examples 'reading secret from the secret path' do
it 'returns 32 bytes' do
expect(secret).to be_a(String)
expect(secret.length).to eq(32)
@@ -32,62 +27,170 @@ RSpec.describe Gitlab::JwtAuthenticatable do
end
it 'accepts a trailing newline' do
- File.open(test_class.secret_path, 'a') { |f| f.write "\n" }
+ File.open(secret_path, 'a') { |f| f.write "\n" }
expect(secret.length).to eq(32)
end
it 'raises an exception if the secret file cannot be read' do
- File.delete(test_class.secret_path)
+ File.delete(secret_path)
expect { secret }.to raise_exception(Errno::ENOENT)
end
it 'raises an exception if the secret file contains the wrong number of bytes' do
- File.truncate(test_class.secret_path, 0)
+ File.truncate(secret_path, 0)
expect { secret }.to raise_exception(RuntimeError)
end
end
+ describe '.secret' do
+ it_behaves_like 'reading secret from the secret path' do
+ subject(:secret) { test_class.secret }
+
+ let(:secret_path) { test_class.secret_path }
+ end
+ end
+
+ describe '.read_secret' do
+ it_behaves_like 'reading secret from the secret path' do
+ subject(:secret) { test_class.read_secret(secret_path) }
+
+ let(:secret_path) { test_class.secret_path }
+ end
+ end
+
describe '.write_secret' do
- it 'uses mode 0600' do
- expect(File.stat(test_class.secret_path).mode & 0777).to eq(0600)
+ context 'without an input' do
+ it 'uses mode 0600' do
+ expect(File.stat(test_class.secret_path).mode & 0777).to eq(0600)
+ end
+
+ it 'writes base64 data' do
+ bytes = Base64.strict_decode64(File.read(test_class.secret_path))
+
+ expect(bytes).not_to be_empty
+ end
end
- it 'writes base64 data' do
- bytes = Base64.strict_decode64(File.read(test_class.secret_path))
+ context 'with an input' do
+ let(:another_path) do
+ Rails.root.join('tmp', 'tests', '.jwt_another_shared_secret')
+ end
- expect(bytes).not_to be_empty
+ after do
+ File.delete(another_path)
+ rescue Errno::ENOENT
+ end
+
+ it 'uses mode 0600' do
+ test_class.write_secret(another_path)
+ expect(File.stat(another_path).mode & 0777).to eq(0600)
+ end
+
+ it 'writes base64 data' do
+ test_class.write_secret(another_path)
+ bytes = Base64.strict_decode64(File.read(another_path))
+
+ expect(bytes).not_to be_empty
+ end
end
end
- describe '.decode_jwt_for_issuer' do
- let(:payload) { { 'iss' => 'test_issuer' } }
+ describe '.decode_jwt' do |decode|
+ let(:payload) { {} }
+
+ context 'use included class secret' do
+ it 'accepts a correct header' do
+ encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
+
+ expect { test_class.decode_jwt(encoded_message) }.not_to raise_error
+ end
+
+ it 'raises an error when the JWT is not signed' do
+ encoded_message = JWT.encode(payload, nil, 'none')
+
+ expect { test_class.decode_jwt(encoded_message) }.to raise_error(JWT::DecodeError)
+ end
- it 'accepts a correct header' do
- encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
+ it 'raises an error when the header is signed with the wrong secret' do
+ encoded_message = JWT.encode(payload, 'wrongsecret', 'HS256')
- expect { test_class.decode_jwt_for_issuer('test_issuer', encoded_message) }.not_to raise_error
+ expect { test_class.decode_jwt(encoded_message) }.to raise_error(JWT::DecodeError)
+ end
end
- it 'raises an error when the JWT is not signed' do
- encoded_message = JWT.encode(payload, nil, 'none')
+ context 'use an input secret' do
+ let(:another_secret) { 'another secret' }
+
+ it 'accepts a correct header' do
+ encoded_message = JWT.encode(payload, another_secret, 'HS256')
+
+ expect { test_class.decode_jwt(encoded_message, another_secret) }.not_to raise_error
+ end
- expect { test_class.decode_jwt_for_issuer('test_issuer', encoded_message) }.to raise_error(JWT::DecodeError)
+ it 'raises an error when the JWT is not signed' do
+ encoded_message = JWT.encode(payload, nil, 'none')
+
+ expect { test_class.decode_jwt(encoded_message, another_secret) }.to raise_error(JWT::DecodeError)
+ end
+
+ it 'raises an error when the header is signed with the wrong secret' do
+ encoded_message = JWT.encode(payload, 'wrongsecret', 'HS256')
+
+ expect { test_class.decode_jwt(encoded_message, another_secret) }.to raise_error(JWT::DecodeError)
+ end
end
- it 'raises an error when the header is signed with the wrong secret' do
- encoded_message = JWT.encode(payload, 'wrongsecret', 'HS256')
+ context 'issuer option' do
+ let(:payload) { { 'iss' => 'test_issuer' } }
+
+ it 'returns decoded payload if issuer is correct' do
+ encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
+ payload = test_class.decode_jwt(encoded_message, issuer: 'test_issuer')
- expect { test_class.decode_jwt_for_issuer('test_issuer', encoded_message) }.to raise_error(JWT::DecodeError)
+ expect(payload[0]).to match a_hash_including('iss' => 'test_issuer')
+ end
+
+ it 'raises an error when the issuer is incorrect' do
+ payload['iss'] = 'somebody else'
+ encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
+
+ expect { test_class.decode_jwt(encoded_message, issuer: 'test_issuer') }.to raise_error(JWT::DecodeError)
+ end
end
- it 'raises an error when the issuer is incorrect' do
- payload['iss'] = 'somebody else'
- encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
+ context 'iat_after option' do
+ it 'returns decoded payload if iat is valid' do
+ freeze_time do
+ encoded_message = JWT.encode(payload.merge(iat: (Time.current - 10.seconds).to_i), test_class.secret, 'HS256')
+ payload = test_class.decode_jwt(encoded_message, iat_after: Time.current - 20.seconds)
+
+ expect(payload[0]).to match a_hash_including('iat' => be_a(Integer))
+ end
+ end
+
+ it 'raises an error if iat is invalid' do
+ encoded_message = JWT.encode(payload.merge(iat: 'wrong'), test_class.secret, 'HS256')
- expect { test_class.decode_jwt_for_issuer('test_issuer', encoded_message) }.to raise_error(JWT::DecodeError)
+ expect { test_class.decode_jwt(encoded_message, iat_after: true) }.to raise_error(JWT::DecodeError)
+ end
+
+ it 'raises an error if iat is absent' do
+ encoded_message = JWT.encode(payload, test_class.secret, 'HS256')
+
+ expect { test_class.decode_jwt(encoded_message, iat_after: true) }.to raise_error(JWT::DecodeError)
+ end
+
+ it 'raises an error if iat is too far in the past' do
+ freeze_time do
+ encoded_message = JWT.encode(payload.merge(iat: (Time.current - 30.seconds).to_i), test_class.secret, 'HS256')
+ expect do
+ test_class.decode_jwt(encoded_message, iat_after: Time.current - 20.seconds)
+ end.to raise_error(JWT::ExpiredSignature, 'Token has expired')
+ end
+ end
end
end
end