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.