You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ro...@apache.org on 2023/02/16 20:00:19 UTC
[couchdb] branch jwt-roles-from-string created (now 28b2a835e)
This is an automated email from the ASF dual-hosted git repository.
ronny pushed a change to branch jwt-roles-from-string
in repository https://gitbox.apache.org/repos/asf/couchdb.git
at 28b2a835e Allow definition of JWT roles claim as comma-seperated list
This branch includes the following new commits:
new 28b2a835e Allow definition of JWT roles claim as comma-seperated list
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
[couchdb] 01/01: Allow definition of JWT roles claim as comma-seperated list
Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ronny pushed a commit to branch jwt-roles-from-string
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 28b2a835e07d05eff2bdced159d0853fdd09081c
Author: Ronny Berndt <ro...@apache.org>
AuthorDate: Thu Feb 16 20:58:54 2023 +0100
Allow definition of JWT roles claim as comma-seperated list
Now it is possible to define a JWT roles claim as a comma-seperated
list or as a JSON array of strings (the only allowed old behavior).
---
src/couch/src/couch_httpd_auth.erl | 9 ++++++-
src/docs/src/api/server/authn.rst | 25 ++++++++++++++++++-
src/docs/src/config/auth.rst | 2 +-
test/elixir/test/jwt_roles_claim_test.exs | 40 ++++++++++++++++++++++++++++++-
4 files changed, 72 insertions(+), 4 deletions(-)
diff --git a/src/couch/src/couch_httpd_auth.erl b/src/couch/src/couch_httpd_auth.erl
index 4a7b217d1..89203bf4f 100644
--- a/src/couch/src/couch_httpd_auth.erl
+++ b/src/couch/src/couch_httpd_auth.erl
@@ -261,7 +261,7 @@ get_roles_claim(Claims) ->
RolesClaimPath = config:get(
"jwt_auth", "roles_claim_path"
),
- Result =
+ Roles =
case RolesClaimPath of
undefined ->
couch_util:get_value(
@@ -284,6 +284,13 @@ get_roles_claim(Claims) ->
TokenizedJsonPath = tokenize_json_path(RolesClaimPath, MatchPositions),
couch_util:get_nested_json_value({Claims}, TokenizedJsonPath)
end,
+ Result =
+ case is_list(Roles) of
+ true ->
+ Roles;
+ false ->
+ re:split(Roles, "\\s*,\\s*", [trim, {return, binary}])
+ end,
case lists:all(fun erlang:is_binary/1, Result) of
true ->
Result;
diff --git a/src/docs/src/api/server/authn.rst b/src/docs/src/api/server/authn.rst
index bffe0bf27..982c931fd 100644
--- a/src/docs/src/api/server/authn.rst
+++ b/src/docs/src/api/server/authn.rst
@@ -397,9 +397,32 @@ is valid.
You can set the user roles claim name through the config setting
:config:option:`roles_claim_name <jwt_auth/roles_claim_name>`. If you don't set
an explicit value, then ``_couchdb.roles`` will be set as the default claim name.
-If presented, as a JSON array of strings, it is used as the CouchDB user's roles
+If presented, it is used as the CouchDB user's roles
list as long as the JWT token is valid.
+.. note::
+
+ Before CouchDB v3.3.2 it was only possible to define roles as a JSON
+ array of strings. Now you can also use a comma-seperated list to define
+ the user roles in your JWT token. The following declarations
+ are equal:
+
+ JSON array of strings:
+
+ .. code-block:: json
+
+ {
+ "_couchdb.roles": ["accounting-role", "view-role"]
+ }
+
+ JSON comma-seperated strings:
+
+ .. code-block:: json
+
+ {
+ "_couchdb.roles": "accounting-role, view-role"
+ }
+
.. warning::
``roles_claim_name`` is deprecated in CouchDB 3.3, and will be removed later.
diff --git a/src/docs/src/config/auth.rst b/src/docs/src/config/auth.rst
index d43810054..87f181afe 100644
--- a/src/docs/src/config/auth.rst
+++ b/src/docs/src/config/auth.rst
@@ -398,7 +398,7 @@ Authentication Configuration
``roles_claim_name`` is deprecated in CouchDB 3.3, and will be removed later.
Please migrate to ``roles_claim_path``.
- If presented, as a JSON array of strings, it is used as the CouchDB user's roles
+ If presented, it is used as the CouchDB user's roles
list as long as the JWT token is valid. The default value for ``roles_claim_name``
is ``_couchdb.roles``.
diff --git a/test/elixir/test/jwt_roles_claim_test.exs b/test/elixir/test/jwt_roles_claim_test.exs
index cd23a3c25..28b280e9c 100644
--- a/test/elixir/test/jwt_roles_claim_test.exs
+++ b/test/elixir/test/jwt_roles_claim_test.exs
@@ -17,7 +17,15 @@ defmodule JwtRolesClaimTest do
:value => ~w(
NTNv7j0TuYARvmNMmWXo6fKvM4o6nv/aUi9ryX38ZH+L1bkrnD1ObOQ8JAUmHCBq7
Iy7otZcyAagBLHVKvvYaIpmMuxmARQ97jUVG16Jkpkp1wXOPsrF9zwew6TpczyH
- kHgX5EuLg2MeBuiT/qJACs1J0apruOOJCg/gOtkjB4c=) |> Enum.join()
+ kHgX5EuLg2MeBuiT/qJACs1J0apruOOJCg/gOtkjB4c=
+ ) |> Enum.join()
+ },
+ %{
+ :section => "jwt_keys",
+ :key => "hmac:myjwttestkey2",
+ :value => ~w(
+ VW5kb3VidGVkbHktRW5nYWdpbmctUm9hZHdheS0wMjk=
+ ) |> Enum.join()
}
]
@@ -26,6 +34,7 @@ defmodule JwtRolesClaimTest do
run_on_modified_server(server_config, fn ->
test_roles(["_couchdb.roles_1", "_couchdb.roles_2"])
+ test_roles_as_string(["_couchdb_string.roles_1", "_couchdb_string.roles_2"])
end)
end
@@ -41,6 +50,7 @@ defmodule JwtRolesClaimTest do
run_on_modified_server(server_config, fn ->
test_roles(["my._couchdb.roles_1", "my._couchdb.roles_2"])
+ test_roles_as_string(["my._couchdb_string.roles_1", "my._couchdb_string.roles_2"])
end)
end
@@ -56,6 +66,7 @@ defmodule JwtRolesClaimTest do
run_on_modified_server(server_config, fn ->
test_roles(["my_nested_role_1", "my_nested_role_2"])
+ test_roles_as_string(["my_nested_string_role_1", "my_nested_string_role_2"])
end)
end
@@ -76,6 +87,7 @@ defmodule JwtRolesClaimTest do
run_on_modified_server(server_config, fn ->
test_roles(["my_nested_role_1", "my_nested_role_2"])
+ test_roles_as_string(["my_nested_string_role_1", "my_nested_string_role_2"])
end)
end
@@ -143,6 +155,32 @@ defmodule JwtRolesClaimTest do
assert resp.body["info"]["authenticated"] == "jwt"
end
+ def test_roles_as_string(roles) do
+ # Different token
+ token = ~w(
+ eyJ0eXAiOiJKV1QiLCJraWQiOiJteWp3dHRlc3RrZXkyIiwiYWxnIjoiSFMyNTYifQ.
+ eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWU
+ sImlhdCI6MTY1NTI5NTgxMCwiZXhwIjoxNzU1Mjk5NDEwLCJteSI6eyJuZXN0ZWQiOn
+ siX2NvdWNoZGIucm9sZXMiOiJteV9uZXN0ZWRfY291Y2hkYl9zdHJpbmcucm9sZXNfM
+ SwgbXlfbmVzdGVkX2NvdWNoZGJfc3RyaW5nLnJvbGVzXzEifX0sIl9jb3VjaGRiLnJv
+ bGVzIjoiX2NvdWNoZGJfc3RyaW5nLnJvbGVzXzEsX2NvdWNoZGJfc3RyaW5nLnJvbGV
+ zXzIiLCJteS5fY291Y2hkYi5yb2xlcyI6Im15Ll9jb3VjaGRiX3N0cmluZy5yb2xlc1
+ 8xLCBteS5fY291Y2hkYl9zdHJpbmcucm9sZXNfMiIsImZvbyI6eyJiYXIuem9uayI6e
+ yJiYXouYnV1Ijp7ImJhYSI6eyJiYWEuYmVlIjp7InJvbGVzIjoibXlfbmVzdGVkX3N0
+ cmluZ19yb2xlXzEsIG15X25lc3RlZF9zdHJpbmdfcm9sZV8yIn19fX19fQ.rzaLmcA2
+ 0R291XuGYNNTM9ypGL3UD_GlVp3DmBtWrZI
+ ) |> Enum.join()
+
+ resp =
+ Couch.get("/_session",
+ headers: [authorization: "Bearer #{token}"]
+ )
+
+ assert resp.body["userCtx"]["name"] == "1234567890"
+ assert resp.body["userCtx"]["roles"] == roles
+ assert resp.body["info"]["authenticated"] == "jwt"
+ end
+
def test_roles_with_bad_input() do
token = ~w(
eyJ0eXAiOiJKV1QiLCJraWQiOiJteWp3dHRlc3RrZXkiLCJhbGciOiJIUzI1NiJ9.