FEATURE: Allow JSON paths with literal dots in the keys (#33)

This is achieved by surrounding a key with double quotes, or by escaping
the dot character with a backslash.

For example, given the JSON

```
{ "www.example.com/uid": "myuid" }
```

Previously, there was no way to access this value. The dots would make
the parser try to access `json["www"]["example"]["com/uid"]`.

Now, this value can be accessed by using a `oauth2_json_user_id_path` like:

```
www\.example\.com/uid
```
or alternatively:
```
"www.example.com/uid"
```
This commit is contained in:
David Taylor 2021-03-10 11:48:58 +00:00 committed by GitHub
parent 0be01f0afd
commit 96a0bde0aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 55 additions and 1 deletions

View File

@ -166,12 +166,34 @@ class ::OAuth2BasicAuthenticator < Auth::ManagedAuthenticator
if path.present?
#this.[].that is the same as this.that, allows for both this[0].that and this.[0].that path styles
path = path.gsub(".[].", ".").gsub(".[", "[")
segments = path.split('.')
segments = parse_segments(path)
val = walk_path(user_json, segments)
result[prop] = val if val.present?
end
end
def parse_segments(path)
segments = [+""]
quoted = false
escaped = false
path.split("").each do |char|
next_char_escaped = false
if !escaped && (char == '"')
quoted = !quoted
elsif !escaped && !quoted && (char == '.')
segments.append +""
elsif !escaped && (char == '\\')
next_char_escaped = true
else
segments.last << char
end
escaped = next_char_escaped
end
segments
end
def log(info)
Rails.logger.warn("OAuth2 Debugging: #{info}") if SiteSetting.oauth2_debug_auth
end

View File

@ -177,6 +177,38 @@ describe OAuth2BasicAuthenticator do
expect(result).to eq "test@example.com"
end
it 'allows keys containing dots, if wrapped in quotes' do
authenticator = OAuth2BasicAuthenticator.new
json_string = '{"www.example.com/uid": "myuid"}'
SiteSetting.oauth2_json_user_id_path = '"www.example.com/uid"'
result = authenticator.json_walk({}, JSON.parse(json_string), :user_id)
expect(result).to eq "myuid"
end
it 'allows keys containing dots, if escaped' do
authenticator = OAuth2BasicAuthenticator.new
json_string = '{"www.example.com/uid": "myuid"}'
SiteSetting.oauth2_json_user_id_path = 'www\.example\.com/uid'
result = authenticator.json_walk({}, JSON.parse(json_string), :user_id)
expect(result).to eq "myuid"
end
it 'allows keys containing literal backslashes, if escaped' do
authenticator = OAuth2BasicAuthenticator.new
# This 'single quoted heredoc' syntax means we don't have to escape backslashes in Ruby
# What you see is exactly what the user would enter in the site settings
json_string = <<~'_'.chomp
{"www.example.com/uid\\": "myuid"}
_
SiteSetting.oauth2_json_user_id_path = <<~'_'.chomp
www\.example\.com/uid\\
_
result = authenticator.json_walk({}, JSON.parse(json_string), :user_id)
expect(result).to eq "myuid"
end
it 'can walk json that contains an array' do
authenticator = OAuth2BasicAuthenticator.new
json_string = '{"email":"test@example.com","identities":[{"user_id":"123456789","provider":"auth0","isSocial":false}]}'