这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
bc6c31f
add new optional field `claims_namespace_path` in JWT config
codingkarthik Apr 10, 2020
a5cb5db
modify the tests to include the claims_namespace_path
codingkarthik Apr 10, 2020
2e74586
return value when empty array is found in executeJSONPath
codingkarthik Apr 10, 2020
35cb5fe
modify the tests to incorporate claims_namespace_path in JWT
codingkarthik Apr 10, 2020
920e360
Merge remote-tracking branch 'upstream/master' into jwt-claims-ns-pat…
codingkarthik Apr 10, 2020
b741282
fix the syntax error in the tests
codingkarthik Apr 10, 2020
0ffeb29
update the docs related to claims_namespace_path
codingkarthik Apr 10, 2020
733ae61
fix the keyword argument syntax in test_jwt.py
codingkarthik Apr 10, 2020
ababdd3
set HASURA_GRAPHQL_JWT_SECRET before running the tests
codingkarthik Apr 10, 2020
fe19fd7
make a new data type JWTConfigClaims
codingkarthik Apr 13, 2020
1d7b5a1
modify the encodeJSONPath function to be more robust
codingkarthik Apr 13, 2020
4fabbcf
remove unused import from JSONPath.hs
codingkarthik Apr 13, 2020
8bca456
revert back encodeJSONPath to what it was earlier
codingkarthik Apr 13, 2020
90ae3f2
modify the test_api_config test to include the claims_namespace_path
codingkarthik Apr 13, 2020
63a5df6
handle the case when hge_jwt_conf is None
codingkarthik Apr 13, 2020
716caed
Merge remote-tracking branch 'upstream/master' into jwt-claims-ns-pat…
codingkarthik Apr 13, 2020
deffb86
Update docs/graphql/manual/auth/authentication/jwt.rst
codingkarthik Apr 13, 2020
d8989b1
Update docs/graphql/manual/auth/authentication/jwt.rst
codingkarthik Apr 13, 2020
4e4e367
Update docs/graphql/manual/auth/authentication/jwt.rst
codingkarthik Apr 13, 2020
bd3f07d
Update docs/graphql/manual/auth/authentication/jwt.rst
codingkarthik Apr 13, 2020
9a0dca5
omit claims_namespace from /v1alpha1/config if claims_namespace_path …
codingkarthik Apr 13, 2020
6496667
modify the jwt tests and refactor executeJSONPath
codingkarthik Apr 13, 2020
d622775
modify the JWTInfo to make claims_namespace as an optional field
codingkarthik Apr 15, 2020
e4ba432
refactor the JWT tests
codingkarthik Apr 15, 2020
e1b08b3
refactor the FromJSON instance of JWTConfig
codingkarthik Apr 15, 2020
c9b84e8
use JWTConfigClaims in the JWTInfo data type
codingkarthik Apr 15, 2020
bf8dda0
remove hge-jwt-claims-ns-path from CI claims_namespace_path test
codingkarthik Apr 15, 2020
b129fa9
Merge branch 'master' into jwt-claims-ns-path-4349
codingkarthik Apr 15, 2020
30c1cb5
improve encodeJSONPath, add property tests for parseJSONPath
rakeshkky Apr 15, 2020
0dec4f0
throw error if both claims_namespace_path and claims_namespace are set
codingkarthik Apr 15, 2020
f81062d
refactor the Data.Parser.JsonPath to Data.Parser.JSONPathSpec
codingkarthik Apr 15, 2020
0fa2a02
update the JWT docs
codingkarthik Apr 15, 2020
736955f
get claims_namespace_path from JWT config only if it's set
codingkarthik Apr 15, 2020
2cb4729
Merge branch 'master' into jwt-claims-ns-path-4349
codingkarthik Apr 15, 2020
ba444ff
modify the JWT docs
codingkarthik Apr 16, 2020
179e2be
Merge branch 'master' into jwt-claims-ns-path-4349
codingkarthik Apr 16, 2020
966c6a5
modify the JWT docs
codingkarthik Apr 16, 2020
7123319
Update docs/graphql/manual/auth/authentication/jwt.rst
tirumaraiselvan Apr 16, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .circleci/test-server.sh
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,20 @@ kill_hge_servers

unset HASURA_GRAPHQL_JWT_SECRET

##########
echo -e "\n$(time_elapsed): <########## TEST GRAPHQL-ENGINE WITH ADMIN SECRET AND JWT (with claims_namespace_path) #####################################>\n"
TEST_TYPE="jwt-with-claims-namespace-path"

export HASURA_GRAPHQL_JWT_SECRET="$(jq -n --arg key "$(cat $OUTPUT_FOLDER/ssl/jwt_public.key)" '{ type: "RS512", key: $key , claims_namespace_path: "$.hasuraClaims"}')"

run_hge_with_args serve
wait_for_port 8080

pytest -n 1 -vv --hge-urls "$HGE_URL" --pg-urls "$HASURA_GRAPHQL_DATABASE_URL" --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --hge-jwt-key-file="$OUTPUT_FOLDER/ssl/jwt_private.key" --hge-jwt-conf="$HASURA_GRAPHQL_JWT_SECRET" test_jwt.py

kill_hge_servers

unset HASURA_GRAPHQL_JWT_SECRET

# test with CORS modes

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ The order, collapsed state of columns and page size is now persisted across page
- server: fix an edge case where some events wouldn't be processed because of internal erorrs (#4213)
- server: fix downgrade not working to version v1.1.1 (#4354)
- server: `type` field is not required if `jwk_url` is provided in JWT config
- server: add a new field `claims_namespace_path` which accepts a JSON Path for looking up hasura claim in the JWT token (#4349)

## `v1.2.0-beta.3`

Expand Down
36 changes: 35 additions & 1 deletion docs/graphql/manual/auth/authentication/jwt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ etc.) JWT claims, as well as Hasura specific claims inside a custom namespace
(or key) i.e. ``https://hasura.io/jwt/claims``.

The ``https://hasura.io/jwt/claims`` is the custom namespace where all Hasura
specific claims have to be present. This value can be configured in the JWT
specific claims have to be present. This value can be configured using
``claims_namespace`` or ``claims_namespace_path`` in the JWT
config while starting the server.

**Note**: ``x-hasura-default-role`` and ``x-hasura-allowed-roles`` are
Expand Down Expand Up @@ -129,6 +130,7 @@ JSON object:
"key": "<optional-key-as-string>",
"jwk_url": "<optional-url-to-refresh-jwks>",
"claims_namespace": "<optional-key-name-in-claims>",
"claims_namespace_path":"<optional-json-path-to-the-claims>",
"claims_format": "json|stringified_json",
"audience": <optional-string-or-list-of-strings-to-verify-audience>,
"issuer": "<optional-string-to-verify-issuer>"
Expand Down Expand Up @@ -220,6 +222,38 @@ inside which the Hasura specific claims will be present, e.g. ``https://mydomain

**Default value** is: ``https://hasura.io/jwt/claims``.

``claims_namespace_path``
^^^^^^^^^^^^^^^^^^^^^^^^^
An optional JSON path value to the Hasura claims in the JWT token.

Example values are ``$.hasura.claims`` or ``$`` (i.e. root of the payload)

The JWT token should be in this format if the ``claims_namespace_path`` is
set to ``$.hasura.claims``:

.. code-block:: json

{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"hasura": {
"claims": {
"x-hasura-allowed-roles": ["editor","user", "mod"],
"x-hasura-default-role": "user",
"x-hasura-user-id": "1234567890",
"x-hasura-org-id": "123",
"x-hasura-custom": "custom-value"
}
}
}

.. note::

The JWT config can only have one of ``claims_namespace`` or ``claims_namespace_path``
values set. If neither keys are set, then the default value of
``claims_namespace`` i.e. https://hasura.io/jwt/claims will be used.

``claims_format``
^^^^^^^^^^^^^^^^^
Expand Down
4 changes: 3 additions & 1 deletion server/graphql-engine.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ library
, Hasura.Server.PGDump
-- Exposed for testing:
, Hasura.Server.Telemetry.Counters
, Data.Parser.JSONPath

, Hasura.RQL.Types
, Hasura.RQL.Types.Run
Expand Down Expand Up @@ -374,7 +375,6 @@ library
, Data.List.Extended
, Data.HashMap.Strict.Extended
, Data.HashMap.Strict.InsOrd.Extended
, Data.Parser.JSONPath
, Data.Sequence.NonEmpty
, Data.TByteString
, Data.Text.Extended
Expand Down Expand Up @@ -426,11 +426,13 @@ test-suite graphql-engine-tests
, time
, transformers-base
, unordered-containers
, text
hs-source-dirs: src-test
main-is: Main.hs
other-modules:
Data.Parser.CacheControlSpec
Data.Parser.URLTemplate
Data.Parser.JSONPathSpec
Data.TimeSpec
Hasura.IncrementalSpec
Hasura.RQL.MetadataSpec
Expand Down
69 changes: 32 additions & 37 deletions server/src-lib/Data/Parser/JSONPath.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,45 @@ import Control.Applicative ((<|>))
import Data.Aeson.Internal (JSONPath, JSONPathElement (..))
import Data.Attoparsec.Text
import Data.Bool (bool)
import Data.Char (isDigit)
import qualified Data.Text as T
import Prelude hiding (takeWhile)
import Text.Read (readMaybe)
import qualified Data.Text as T

parseKey :: Parser T.Text
parseKey = do
firstChar <- letter
<?> "the first character of property name must be a letter."
name <- many' (letter
<|> digit
<|> satisfy (`elem` ("-_" :: String))
)
return $ T.pack (firstChar:name)
parseSimpleKeyText :: Parser T.Text
parseSimpleKeyText = takeWhile1 (inClass "a-zA-Z0-9_-")

parseIndex :: Parser Int
parseIndex = skip (== '[') *> anyChar >>= parseDigits
where
parseDigits :: Char -> Parser Int
parseDigits firstDigit
| firstDigit == ']' = fail "empty array index"
| not $ isDigit firstDigit =
fail $ "invalid array index: " ++ [firstDigit]
| otherwise = do
remain <- many' (notChar ']')
skip (== ']')
let content = firstDigit:remain
case (readMaybe content :: Maybe Int) of
Nothing -> fail $ "invalid array index: " ++ content
Just v -> return v
parseKey :: Parser JSONPathElement
parseKey = Key <$>
( (char '.' *> parseSimpleKeyText) -- Parse `.key`
<|> T.pack <$> ((string ".['" <|> string "['") *> manyTill anyChar (string "']")) -- Parse `['key']` or `.['key']`
<|> fail "invalid key element"
)

parseElement :: Parser JSONPathElement
parseElement = do
dotLen <- T.length <$> takeWhile (== '.')
if dotLen > 1
then fail "multiple dots in json path"
else peekChar >>= \case
Nothing -> fail "empty json path"
Just '[' -> Index <$> parseIndex
_ -> Key <$> parseKey
parseIndex :: Parser JSONPathElement
parseIndex = Index <$>
( ((char '[' *> manyTill anyChar (char ']')) >>= maybe (fail "invalid array index") pure . readMaybe) -- Parse `[Int]`
<|> fail "invalid index element"
)

parseElements :: Parser JSONPath
parseElements = skipWhile (== '$') *> many1 parseElement
parseElements = skipWhile (== '$') *> parseRemaining
where
parseFirstKey = Key <$> parseSimpleKeyText
parseElements' = many1 (parseIndex <|> parseKey)
parseRemaining = do
maybeFirstChar <- peekChar
case maybeFirstChar of
Nothing -> pure []
Just firstChar ->
-- If first char is not any of '.' and '[', then parse first key
-- Eg:- Parse "key1.key2[0]"
if firstChar `notElem` (".[" :: String) then do
firstKey <- parseFirstKey
remainingElements <- parseElements'
pure $ firstKey:remainingElements
else parseElements'

-- | Parse jsonpath String value
parseJSONPath :: T.Text -> Either String JSONPath
parseJSONPath = parseResult . parse parseElements
where
Expand All @@ -64,6 +59,6 @@ parseJSONPath = parseResult . parse parseElements
Left $ invalidMessage remain
else
Right r

invalidMessage s = "invalid property name: " ++ T.unpack s
++ ". Accept letters, digits, underscore (_) or hyphen (-) only"
++ ". Use single quotes enclosed in bracket if there are any special characters"
4 changes: 2 additions & 2 deletions server/src-lib/Data/URL/Template.hs
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,12 @@ renderURLTemplate template = do

-- QuickCheck generators
instance Arbitrary Variable where
arbitrary = Variable . T.pack <$> listOf1 (elements $ alphaNumerics <> "-_")
arbitrary = Variable . T.pack <$> listOf1 (elements $ alphaNumerics <> " -_")

instance Arbitrary URLTemplate where
arbitrary = URLTemplate <$> listOf (oneof [genText, genVariable])
where
genText = (TIText . T.pack) <$> listOf1 (elements $ alphaNumerics <> "://")
genText = (TIText . T.pack) <$> listOf1 (elements $ alphaNumerics <> " ://")
genVariable = TIVariable <$> arbitrary

genURLTemplate :: Gen URLTemplate
Expand Down
2 changes: 1 addition & 1 deletion server/src-lib/Hasura/Prelude.hs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ import qualified GHC.Clock as Clock
import qualified Test.QuickCheck as QC

alphaNumerics :: String
alphaNumerics = ['a'..'z'] ++ ['A'..'Z'] ++ "0123456789 "
alphaNumerics = ['a'..'z'] ++ ['A'..'Z'] ++ "0123456789"

instance Arbitrary Text where
arbitrary = T.pack <$> QC.listOf (QC.elements alphaNumerics)
Expand Down
7 changes: 5 additions & 2 deletions server/src-lib/Hasura/RQL/Types/Error.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module Hasura.RQL.Types.Error
, QErr(..)
, encodeQErr
, encodeGQLErr
, encodeJSONPath
, noInternalQErrEnc
, err400
, err404
Expand Down Expand Up @@ -197,8 +198,10 @@ encodeJSONPath = format "$"
format pfx (Key key:parts) = format (pfx ++ "." ++ formatKey key) parts

formatKey key
| T.any (=='.') key = "['" ++ T.unpack key ++ "']"
| otherwise = T.unpack key
| T.any specialChar key = "['" ++ T.unpack key ++ "']"
| otherwise = T.unpack key
where
specialChar = flip notElem (alphaNumerics ++ "_-")

instance Q.FromPGConnErr QErr where
fromPGConnErr c =
Expand Down
Loading