Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[rubygems/rubygems] Introduce
Gem::PrintableUri
that would redact U…
…RIs to be used on outputs We need to redact URI credential in several places and copy pasting the code into each part of it is not ideal. This class is responsible for parsing URI strings and redacting credential from it. Also, it will handle URI object in the same manner. We will be reusing this class whenever we need to print/display a URI to users. URI with the following format will be redacted: - Token: `http://my-secure-token@example.com` => `http://REDACTED@example.com` - Username & Password: `http://my-username:my-secure-password@example.com` => `http://my-username:REDACTED@example.com` - x-oauth-basic: `http://my-secure-token:x-oauth-basic@example.com` => `http://REDACTED:x-oauth-basic@example.com` rubygems/rubygems@f1e45d3a89
- Loading branch information
1 parent
14a9e24
commit b418024
Showing
2 changed files
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'uri' | ||
require_relative 'uri_parser' | ||
|
||
class Gem::PrintableUri | ||
def self.parse_uri(uri) | ||
new(uri).parse_uri | ||
end | ||
|
||
def initialize(original_uri) | ||
@credential_redacted = false | ||
@original_uri = original_uri | ||
end | ||
|
||
def parse_uri | ||
@original_uri = Gem::UriParser.parse_uri(@original_uri) | ||
@uri = @original_uri.clone | ||
redact_credential | ||
|
||
self | ||
end | ||
|
||
def parsed_uri? | ||
@uri.is_a? URI::Generic | ||
end | ||
|
||
def credential_redacted? | ||
@credential_redacted | ||
end | ||
|
||
def original_password | ||
return unless parsed_uri? | ||
|
||
@original_uri.password | ||
end | ||
|
||
def to_s | ||
@uri.to_s | ||
end | ||
|
||
private | ||
|
||
def redact_credential | ||
return unless redactable_credential? | ||
|
||
if token? | ||
@uri.user = 'REDACTED' | ||
elsif oauth_basic? | ||
@uri.user = 'REDACTED' | ||
elsif password? | ||
@uri.password = 'REDACTED' if password? | ||
end | ||
|
||
@credential_redacted = true | ||
end | ||
|
||
def redactable_credential? | ||
return false unless parsed_uri? | ||
|
||
password? || oauth_basic? || token? | ||
end | ||
|
||
def password? | ||
return false unless parsed_uri? | ||
|
||
!!@uri.password | ||
end | ||
|
||
def oauth_basic? | ||
return false unless parsed_uri? | ||
|
||
@uri.password == 'x-oauth-basic' | ||
end | ||
|
||
def token? | ||
return false unless parsed_uri? | ||
|
||
!@uri.user.nil? && @uri.password.nil? | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
require_relative 'helper' | ||
require 'uri' | ||
require 'rubygems/printable_uri' | ||
|
||
class TestPrintableUri < Gem::TestCase | ||
def test_parsed_uri | ||
assert_equal true, Gem::PrintableUri.parse_uri("https://www.example.com").parsed_uri? | ||
end | ||
|
||
def test_parsed_uri_with_empty_uri_object | ||
assert_equal true, Gem::PrintableUri.parse_uri(URI("")).parsed_uri? | ||
end | ||
|
||
def test_parsed_uri_with_valid_uri_object | ||
assert_equal true, Gem::PrintableUri.parse_uri(URI("https://www.example.com")).parsed_uri? | ||
end | ||
|
||
def test_parsed_uri_with_other_objects | ||
assert_equal false, Gem::PrintableUri.parse_uri(Object.new).parsed_uri? | ||
end | ||
|
||
def test_parsed_uri_with_invalid_uri | ||
assert_equal false, Gem::PrintableUri.parse_uri("https://www.example.com:80index").parsed_uri? | ||
end | ||
|
||
def test_credential_redacted_with_user_pass | ||
assert_equal true, Gem::PrintableUri.parse_uri("https://user:pass@example.com").credential_redacted? | ||
end | ||
|
||
def test_credential_redacted_with_token | ||
assert_equal true, Gem::PrintableUri.parse_uri("https://token@example.com").credential_redacted? | ||
end | ||
|
||
def test_credential_redacted_with_user_x_oauth_basic | ||
assert_equal true, Gem::PrintableUri.parse_uri("https://token:x-oauth-basic@example.com").credential_redacted? | ||
end | ||
|
||
def test_credential_redacted_without_credential | ||
assert_equal false, Gem::PrintableUri.parse_uri("https://www.example.com").credential_redacted? | ||
end | ||
|
||
def test_credential_redacted_with_empty_uri_object | ||
assert_equal false, Gem::PrintableUri.parse_uri(URI("")).credential_redacted? | ||
end | ||
|
||
def test_credential_redacted_with_valid_uri_object | ||
assert_equal true, Gem::PrintableUri.parse_uri(URI("https://user:pass@example.com")).credential_redacted? | ||
end | ||
|
||
def test_credential_redacted_with_other_objects | ||
assert_equal false, Gem::PrintableUri.parse_uri(Object.new).credential_redacted? | ||
end | ||
|
||
def test_original_password_user_pass | ||
assert_equal "pass", Gem::PrintableUri.parse_uri("https://user:pass@example.com").original_password | ||
end | ||
|
||
def test_original_password_with_token | ||
assert_equal nil, Gem::PrintableUri.parse_uri("https://token@example.com").original_password | ||
end | ||
|
||
def test_original_password_without_credential | ||
assert_equal nil, Gem::PrintableUri.parse_uri("https://www.example.com").original_password | ||
end | ||
|
||
def test_original_password_with_invalid_uri | ||
assert_equal nil, Gem::PrintableUri.parse_uri("https://www.example.com:80index").original_password | ||
end | ||
|
||
def test_original_password_with_empty_uri_object | ||
assert_equal nil, Gem::PrintableUri.parse_uri(URI("")).original_password | ||
end | ||
|
||
def test_original_password_with_valid_uri_object | ||
assert_equal "pass", Gem::PrintableUri.parse_uri(URI("https://user:pass@example.com")).original_password | ||
end | ||
|
||
def test_original_password_with_other_objects | ||
assert_equal nil, Gem::PrintableUri.parse_uri(Object.new).original_password | ||
end | ||
|
||
def test_to_s_with_user_pass | ||
assert_equal "https://user:REDACTED@example.com", Gem::PrintableUri.parse_uri("https://user:pass@example.com").to_s | ||
end | ||
|
||
def test_to_s_with_token | ||
assert_equal "https://REDACTED@example.com", Gem::PrintableUri.parse_uri("https://token@example.com").to_s | ||
end | ||
|
||
def test_to_s_with_user_x_oauth_basic | ||
assert_equal "https://REDACTED:x-oauth-basic@example.com", Gem::PrintableUri.parse_uri("https://token:x-oauth-basic@example.com").to_s | ||
end | ||
|
||
def test_to_s_without_credential | ||
assert_equal "https://www.example.com", Gem::PrintableUri.parse_uri("https://www.example.com").to_s | ||
end | ||
|
||
def test_to_s_with_invalid_uri | ||
assert_equal "https://www.example.com:80index", Gem::PrintableUri.parse_uri("https://www.example.com:80index").to_s | ||
end | ||
|
||
def test_to_s_with_empty_uri_object | ||
assert_equal "", Gem::PrintableUri.parse_uri(URI("")).to_s | ||
end | ||
|
||
def test_to_s_with_valid_uri_object | ||
assert_equal "https://user:REDACTED@example.com", Gem::PrintableUri.parse_uri(URI("https://user:pass@example.com")).to_s | ||
end | ||
|
||
def test_to_s_with_other_objects | ||
obj = Object.new | ||
obj.stub(:to_s, "my-to-s") do | ||
assert_equal "my-to-s", Gem::PrintableUri.parse_uri(obj).to_s | ||
end | ||
end | ||
end |