You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by GitBox <gi...@apache.org> on 2022/03/11 14:26:25 UTC

[GitHub] [apisix] azilentech opened a new pull request #6586: feat-6574-keycloak-plugin-password-grant-support

azilentech opened a new pull request #6586:
URL: https://github.com/apache/apisix/pull/6586


   Support of password grant type for token generation (#6574)
   
   Committing changes after adding new feature for generating token based on `user id/password` and also taking support of grant type `password`
   
   feat: #6574
   
   ### Pre-submission checklist:
   
   <!--
   Please follow the PR manners:
   1. Use Draft if the PR is not ready to be reviewed
   2. Test is required for the feat/fix PR, unless you have a good reason
   3. Doc is required for the feat PR
   4. Use a new commit to resolve review instead of `push -f`
   5. If you need to resolve merge conflicts after the PR is reviewed, please merge master but do not rebase
   6. Use "request review" to notify the reviewer once you have resolved the review
   7. Only reviewer can click "Resolve conversation" to mark the reviewer's review resolved
   -->
   
   * [ ] Did you explain what problem does this PR solve? Or what new features have been added?
   * [ ] Have you added corresponding test cases?
   * [ ] Have you modified the corresponding document?
   * [ ] Is this PR backward compatible? **If it is not backward compatible, please discuss on the [mailing list](https://github.com/apache/apisix/tree/master#community) first**
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] starsz commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
starsz commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r825536697



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +703,107 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.warn("generate_token_using_password_grant Function Called")
+    --Read Body
+    ngx.req.read_body()
+    --Get Body Data
+    local request_body=ngx.req.get_body_data()

Review comment:
       You can use `core.request.get_body()` instead.

##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +703,107 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.warn("generate_token_using_password_grant Function Called")

Review comment:
       Why should we use `warn` level ?

##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +703,107 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.warn("generate_token_using_password_grant Function Called")
+    --Read Body
+    ngx.req.read_body()
+    --Get Body Data
+    local request_body=ngx.req.get_body_data()
+    local username = nil
+    local password = nil
+    --split by &
+    local parameters_array = util.split(request_body, "&")
+
+    if #parameters_array == 2 then
+        for k, parameter in ipairs(parameters_array) do
+            if str_find(parameter, "username") then
+                --split by =
+                local username_value_array = util.split(parameter, "=")
+                if #username_value_array == 2 then
+                    username = username_value_array[2]
+                end
+            end
+            if str_find(parameter, "password") then
+                --split by =
+                local password_value_array = util.split(parameter, "=")
+                if #password_value_array == 2 then
+                    password = password_value_array[2]
+                end
+            end
+        end
+    end
+
+    if not username then
+        local err = "username is missing"
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing"
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return  res.status, res.body
+end
 
 function _M.access(conf, ctx)
+
+    if conf.password_grant_token_generation_incoming_uri then
+        if ngx.var.request_uri:upper()
+                == conf.password_grant_token_generation_incoming_uri:upper() then

Review comment:
       Use `and` would be better.

##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +703,107 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.warn("generate_token_using_password_grant Function Called")
+    --Read Body
+    ngx.req.read_body()
+    --Get Body Data
+    local request_body=ngx.req.get_body_data()
+    local username = nil
+    local password = nil
+    --split by &
+    local parameters_array = util.split(request_body, "&")
+
+    if #parameters_array == 2 then
+        for k, parameter in ipairs(parameters_array) do
+            if str_find(parameter, "username") then
+                --split by =
+                local username_value_array = util.split(parameter, "=")
+                if #username_value_array == 2 then
+                    username = username_value_array[2]
+                end
+            end
+            if str_find(parameter, "password") then
+                --split by =
+                local password_value_array = util.split(parameter, "=")
+                if #password_value_array == 2 then
+                    password = password_value_array[2]
+                end
+            end
+        end
+    end
+
+    if not username then
+        local err = "username is missing"
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing"
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return  res.status, res.body

Review comment:
       ```suggestion
       return res.status, res.body
   ```

##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +703,107 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.warn("generate_token_using_password_grant Function Called")
+    --Read Body
+    ngx.req.read_body()
+    --Get Body Data
+    local request_body=ngx.req.get_body_data()
+    local username = nil
+    local password = nil
+    --split by &
+    local parameters_array = util.split(request_body, "&")

Review comment:
       Why the username, password in the request body and use & to concat.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
spacewander commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r828692698



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,87 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({

Review comment:
       ```suggestion
           body = ngx.encode_args({
   ```

##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,87 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err

Review comment:
       ```suggestion
           return 503, err
   ```
   
   New feature is encouraged to avoid 500

##########
File path: t/plugin/authz-keycloak.t
##########
@@ -621,3 +622,104 @@ GET /t
 --- response_headers
 Location: http://127.0.0.1/test
 --- error_code: 307
+
+
+
+=== TEST 18: Add https endpoint with password_grant_token_generation_incoming_uri
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "plugins": {
+                            "authz-keycloak": {
+                                "token_endpoint": "https://127.0.0.1:8443/auth/realms/University/protocol/openid-connect/token",
+                                "permissions": ["course_resource#view"],
+                                "client_id": "course_management",
+                                "client_secret": "d1ec69e9-55d2-4109-a3ea-befa071579d5",
+                                "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
+                                "timeout": 3000,
+                                "ssl_verify": false,
+                                "password_grant_token_generation_incoming_uri": "/api/token"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1982": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/api/token"
+                }]],
+                [[{

Review comment:
       We need to remove the echo response data as https://github.com/apache/apisix/pull/6545

##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,87 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    if conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri:upper() ==
+        conf.password_grant_token_generation_incoming_uri:upper() and

Review comment:
       Is it required to compare case-insensitive?
   Also, the `var.request_uri` will contain `?argument=part`, is it desirable?

##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,87 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    if conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri:upper() ==
+        conf.password_grant_token_generation_incoming_uri:upper() and
+        core.request.get_method() == "POST" then

Review comment:
       We should also check the "Content-Type"?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] starsz commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
starsz commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r827025532



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,88 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+
+    if conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri:upper() ==
+        conf.password_grant_token_generation_incoming_uri:upper() and
+        ctx.curr_req_matched["_method"]:upper() == "POST" then

Review comment:
       `ctx.curr_req_matched["_method"]:upper()` looks strange.
   I think you can use `core.req.get_method` instead.

##########
File path: docs/en/latest/plugins/authz-keycloak.md
##########
@@ -122,6 +123,27 @@ of the same name. The scope is then added to every permission to check.
 If `lazy_load_paths` is `false`, the plugin adds the mapped scope to any of the static permissions configured
 in the `permissions` attribute, even if they contain one or more scopes already.
 
+### Password Grant Token Generation Incoming URI
+
+If you want to generate a token using `password` grant, you can set value of `password_grant_token_generation_incoming_uri`.
+
+Incoming request URI will be matched with this value and if matched, it will generate token using `Token Endpoint`.
+It will also check, if REST method is `POST`.

Review comment:
       ```suggestion
   It will also check if the request method is `POST`.
   ```

##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,88 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+

Review comment:
       redundant line ?

##########
File path: docs/en/latest/plugins/authz-keycloak.md
##########
@@ -122,6 +123,27 @@ of the same name. The scope is then added to every permission to check.
 If `lazy_load_paths` is `false`, the plugin adds the mapped scope to any of the static permissions configured
 in the `permissions` attribute, even if they contain one or more scopes already.
 
+### Password Grant Token Generation Incoming URI
+
+If you want to generate a token using `password` grant, you can set value of `password_grant_token_generation_incoming_uri`.

Review comment:
       ```suggestion
   If you want to generate a token using `password` grant, you can set the value of `password_grant_token_generation_incoming_uri`.
   ```

##########
File path: docs/en/latest/plugins/authz-keycloak.md
##########
@@ -122,6 +123,27 @@ of the same name. The scope is then added to every permission to check.
 If `lazy_load_paths` is `false`, the plugin adds the mapped scope to any of the static permissions configured
 in the `permissions` attribute, even if they contain one or more scopes already.
 
+### Password Grant Token Generation Incoming URI
+
+If you want to generate a token using `password` grant, you can set value of `password_grant_token_generation_incoming_uri`.
+
+Incoming request URI will be matched with this value and if matched, it will generate token using `Token Endpoint`.

Review comment:
       ```suggestion
   Incoming request URI will be matched with this value and if matched, it will generate a token using `Token Endpoint`.
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r828757694



##########
File path: t/plugin/authz-keycloak.t
##########
@@ -621,3 +622,104 @@ GET /t
 --- response_headers
 Location: http://127.0.0.1/test
 --- error_code: 307
+
+
+
+=== TEST 18: Add https endpoint with password_grant_token_generation_incoming_uri
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "plugins": {
+                            "authz-keycloak": {
+                                "token_endpoint": "https://127.0.0.1:8443/auth/realms/University/protocol/openid-connect/token",
+                                "permissions": ["course_resource#view"],
+                                "client_id": "course_management",
+                                "client_secret": "d1ec69e9-55d2-4109-a3ea-befa071579d5",
+                                "grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
+                                "timeout": 3000,
+                                "ssl_verify": false,
+                                "password_grant_token_generation_incoming_uri": "/api/token"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1982": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/api/token"
+                }]],
+                [[{

Review comment:
       Alright. I removed it from this new test case.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r825596710



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +703,107 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.warn("generate_token_using_password_grant Function Called")

Review comment:
       Agree... it should be debug level log. I changed it.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] soulbird commented on pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
soulbird commented on pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#issuecomment-1065757694


   you should sing the CLA first


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] tzssangglass commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
tzssangglass commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r829678553



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,89 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 503, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body = ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    local headers = core.request.headers(ctx)
+    if conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri ==
+        conf.password_grant_token_generation_incoming_uri and
+        headers["content-type"] == "application/x-www-form-urlencoded" and
+        core.request.get_method() == "POST" then

Review comment:
       ```suggestion
       local headers = core.request.headers(ctx)
       local need_grant_token = conf.password_grant_token_generation_incoming_uri and
               ngx.var.request_uri ==
                       conf.password_grant_token_generation_incoming_uri and
               headers["content-type"] == "application/x-www-form-urlencoded" and
               core.request.get_method() == "POST"
       if need_grant_token then
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] soulbird commented on pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
soulbird commented on pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#issuecomment-1065777377


   Missing unit tests


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r825597553



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +703,107 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.warn("generate_token_using_password_grant Function Called")
+    --Read Body
+    ngx.req.read_body()
+    --Get Body Data
+    local request_body=ngx.req.get_body_data()
+    local username = nil
+    local password = nil
+    --split by &
+    local parameters_array = util.split(request_body, "&")

Review comment:
       As url encoded parameters were coming, they were automatically appended using '&'. Having said that, your comment helped me to utilize "ngx.decode_args" which makes code much cleaner. Thanks!




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r825596812



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +703,107 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.warn("generate_token_using_password_grant Function Called")
+    --Read Body
+    ngx.req.read_body()
+    --Get Body Data
+    local request_body=ngx.req.get_body_data()

Review comment:
       Done. I changed it.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] tzssangglass commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
tzssangglass commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r829678553



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,89 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 503, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body = ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    local headers = core.request.headers(ctx)
+    if conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri ==
+        conf.password_grant_token_generation_incoming_uri and
+        headers["content-type"] == "application/x-www-form-urlencoded" and
+        core.request.get_method() == "POST" then

Review comment:
       ```suggestion
       local headers = core.request.headers(ctx)
       local need_grant_token = conf.password_grant_token_generation_incoming_uri and
               ngx.var.request_uri ==
                       conf.password_grant_token_generation_incoming_uri and
               headers["content-type"] == "application/x-www-form-urlencoded" and
               core.request.get_method() == "POST"
       if need_grant_token then
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
spacewander commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r829659989



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,87 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    if conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri:upper() ==
+        conf.password_grant_token_generation_incoming_uri:upper() and

Review comment:
       The `request_uri` will contain the query string part of the uri. For example,
   With request `/path?query=string`
   The `ngx.var.request_uri` will return `/path?query=string`, and `ctx.var.uri` will return `/path`. By looking at the doc of `password_grant_token_generation_incoming_uri`, I guess we can use `ctx.var.uri` so both `/path?query=string` and `/path` can match.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r830639264



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,89 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 503, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body = ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    local headers = core.request.headers(ctx)
+    local need_grant_token = conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri == conf.password_grant_token_generation_incoming_uri and

Review comment:
       Done. Made the change.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#issuecomment-1067901042


   @sshniro 
   I didn't test that way earlier, but just now, I did that and it is working. Also when I see the above checks, that test case is starting to pass now. 
   
   I have got the idea what things might cause that. Earlier I was using '&' to split the incoming parameters. but now I am using "ngx.decode_args" ( https://github.com/apache/apisix/pull/6586#discussion_r825597553 ).
   
   Because of that earlier, username (which is email) wouldn't parse properly.
   That should be the root cause of the same.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r828756255



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,87 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({

Review comment:
       done and also incorporated this in the same file at other places.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r825597007



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +703,107 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.warn("generate_token_using_password_grant Function Called")
+    --Read Body
+    ngx.req.read_body()
+    --Get Body Data
+    local request_body=ngx.req.get_body_data()
+    local username = nil
+    local password = nil
+    --split by &
+    local parameters_array = util.split(request_body, "&")
+
+    if #parameters_array == 2 then
+        for k, parameter in ipairs(parameters_array) do
+            if str_find(parameter, "username") then
+                --split by =
+                local username_value_array = util.split(parameter, "=")
+                if #username_value_array == 2 then
+                    username = username_value_array[2]
+                end
+            end
+            if str_find(parameter, "password") then
+                --split by =
+                local password_value_array = util.split(parameter, "=")
+                if #password_value_array == 2 then
+                    password = password_value_array[2]
+                end
+            end
+        end
+    end
+
+    if not username then
+        local err = "username is missing"
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing"
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return  res.status, res.body
+end
 
 function _M.access(conf, ctx)
+
+    if conf.password_grant_token_generation_incoming_uri then
+        if ngx.var.request_uri:upper()
+                == conf.password_grant_token_generation_incoming_uri:upper() then

Review comment:
       Good Suggestion and changed it to use and for all three conditions.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r828756762



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,87 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    if conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri:upper() ==
+        conf.password_grant_token_generation_incoming_uri:upper() and
+        core.request.get_method() == "POST" then

Review comment:
       Yes. it was missed. I introduced that now.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r829957937



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,87 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    if conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri:upper() ==
+        conf.password_grant_token_generation_incoming_uri:upper() and

Review comment:
       @spacewander I understood your idea. I am curious, as to why we should treat both strings (/path?query=string or /path) the same way and proceed with token generation.
   
   Token generation is a very specific use case, and shouldn't we say if and only if a specific URL will be submitted then we will generate token and submit result back.
    
   if the request is made with /path?query=string, it shouldn't be assumed for token generation and it should be treated the same as any other request. (e.g. proceed to forward to upstream for further processing).
   
   Please share your view. Accordingly, I can make changes, if needed.
   
   




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r825596837



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +703,107 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.warn("generate_token_using_password_grant Function Called")
+    --Read Body
+    ngx.req.read_body()
+    --Get Body Data
+    local request_body=ngx.req.get_body_data()
+    local username = nil
+    local password = nil
+    --split by &
+    local parameters_array = util.split(request_body, "&")
+
+    if #parameters_array == 2 then
+        for k, parameter in ipairs(parameters_array) do
+            if str_find(parameter, "username") then
+                --split by =
+                local username_value_array = util.split(parameter, "=")
+                if #username_value_array == 2 then
+                    username = username_value_array[2]
+                end
+            end
+            if str_find(parameter, "password") then
+                --split by =
+                local password_value_array = util.split(parameter, "=")
+                if #password_value_array == 2 then
+                    password = password_value_array[2]
+                end
+            end
+        end
+    end
+
+    if not username then
+        local err = "username is missing"
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing"
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return  res.status, res.body

Review comment:
       Done.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#issuecomment-1065934827


   Hi,
   
   I will need little guidance for the CLA signing process from your side.
   
   In PR, I have made the following changes.
   
   - Added test cases. As these test cases require keycloak installation, I tested with my local environment by adjusting parameters and configuration settings. Please let me know If I need to perform further steps.
   - I updated code by using better configuration setting "password_grant_token_generation_incoming_uri" instead of using "token_generation_endpoint" which can be confusing with "token_endpoint".
   - I updated the documentation by following contribution guidelines.
   - I also ran "make lint" and followed all the suggestions.
   
   Please let me know the next actions for me.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] niketrk commented on pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
niketrk commented on pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#issuecomment-1066141219


   @sshniro 
   
   I checked the logs of above test runs.
   error is: "status=401, body={\"error\":\"invalid_grant\",\"error_description\":\"Invalid user credentials\"}"
   
   I have used following settings in my test scenario:
   "client_id": "course_management"
   "client_secret": "d1ec69e9-55d2-4109-a3ea-befa071579d5"
   username = "teacher@gmail.com"
   password = "123456"
   grant_type = "password"
   
   I took the above settings from other Keycloak test cases.
   I am doubting that these settings are not proper for the test Keycloak instance. In my local environment, I put my Keycloak settings and then run test scenario via
   `TEST_NGINX_BINARY=/usr/local/bin/openresty prove -Itest-nginx/lib -r t/plugin/authz-keycloak.t`
   
   And it worked properly.
   
   -------------
   Another issue is reported as: "ERROR: apisix/plugins/authz-keycloak.lua: line 719: getting the Lua global "string""
   
   For which I made fix "local str_find = string.find", so it should be fixed now.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r829959869



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,89 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 503, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body = ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    local headers = core.request.headers(ctx)
+    if conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri ==
+        conf.password_grant_token_generation_incoming_uri and
+        headers["content-type"] == "application/x-www-form-urlencoded" and
+        core.request.get_method() == "POST" then

Review comment:
       Done. This one is giving better readability. Thanks!




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
spacewander commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r829659989



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,87 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    if conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri:upper() ==
+        conf.password_grant_token_generation_incoming_uri:upper() and

Review comment:
       The `request_uri` will contain the query string part of the uri. For example,
   With request `/path?query=string`
   The `ngx.var.request_uri` will return `/path?query=string`, and `ctx.var.uri` will return `/path`. By looking at the doc of `password_grant_token_generation_incoming_uri`, I guess we can use `ctx.var.uri` so both `/path?query=string` and `/path` can match.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] sshniro commented on pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
sshniro commented on pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#issuecomment-1067864904


   > scenario
   
   Hi, are you getting the same error when you run the docker image instance (sshniro/keycloak-apisix:latest) locally?


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
spacewander commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r830565569



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,89 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 503, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body = ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    local headers = core.request.headers(ctx)
+    local need_grant_token = conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri == conf.password_grant_token_generation_incoming_uri and

Review comment:
       ```suggestion
           ctx.var.request_uri == conf.password_grant_token_generation_incoming_uri and
   ```
   would be better, which provides a cache of the var.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander commented on pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
spacewander commented on pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#issuecomment-1066089223


   > I will need little guidance for the CLA signing process from your side.
   
   There is no CLA file for APISIX :)
   
   > Added test cases. As these test cases require keycloak installation, I tested with my local environment by adjusting parameters and configuration settings. Please let me know If I need to perform further steps.
   
   As the keycloak environment is set up by @sshniro,
   @sshniro 
   Could you provide some help for @azilentech to test this feature? Thanks!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r829957937



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,87 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    if conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri:upper() ==
+        conf.password_grant_token_generation_incoming_uri:upper() and

Review comment:
       @spacewander I understood your idea. I am curious, as to why we should treat both strings (/path?query=string or /path) the same way and proceed with token generation.
   
   Token generation is a very specific use case, and shouldn't we say if and only if a specific URL will be submitted then we will generate token and submit result back.
    
   if the request is made with /path?query=string, it shouldn't be assumed for token generation and it should be treated the same as any other request. (e.g. proceed to forward to upstream for further processing).
   
   Please share your view. Accordingly, I can make changes, if needed.
   
   

##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,89 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 503, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body = ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    local headers = core.request.headers(ctx)
+    if conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri ==
+        conf.password_grant_token_generation_incoming_uri and
+        headers["content-type"] == "application/x-www-form-urlencoded" and
+        core.request.get_method() == "POST" then

Review comment:
       Done. This one is giving better readability. Thanks!




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r828756255



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,87 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({

Review comment:
       done and also incorporated this in same file at other places.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r828757524



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,87 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err
+    end
+    local httpc = authz_keycloak_get_http_client(conf)
+
+    local params = {
+        method = "POST",
+        body =  ngx.encode_args({
+            grant_type = "password",
+            client_id = client_id,
+            client_secret = conf.client_secret,
+            username = username,
+            password = password
+        }),
+        headers = {
+            ["Content-Type"] = "application/x-www-form-urlencoded"
+        }
+    }
+
+    params = authz_keycloak_configure_params(params, conf)
+
+    local res, err = httpc:request_uri(token_endpoint, params)
+
+    if not res then
+        err = "Accessing token endpoint URL (" .. token_endpoint
+              .. ") failed: " .. err
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    log.debug("Response data: " .. res.body)
+    local json, err = authz_keycloak_parse_json_response(res)
+
+    if not json then
+        err = "Could not decode JSON from response"
+              .. (err and (": " .. err) or '.')
+        log.error(err)
+        return 401, {message = err}
+    end
+
+    return res.status, res.body
+end
 
 function _M.access(conf, ctx)
+    if conf.password_grant_token_generation_incoming_uri and
+        ngx.var.request_uri:upper() ==
+        conf.password_grant_token_generation_incoming_uri:upper() and

Review comment:
       URL should be case sensitive so removed that "upper" from code.
   For the part of request_uri, I believe, as we are checking uri, method, and header, all three parts, this should be fine.
   Basically for the operation of token generation, exact uri matching will be helpful.
   Your thoughts?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] azilentech commented on a change in pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
azilentech commented on a change in pull request #6586:
URL: https://github.com/apache/apisix/pull/6586#discussion_r828756638



##########
File path: apisix/plugins/authz-keycloak.lua
##########
@@ -695,8 +700,87 @@ local function fetch_jwt_token(ctx)
     return token
 end
 
+-- To get new access token by calling get token api
+local function generate_token_using_password_grant(conf,ctx)
+    log.debug("generate_token_using_password_grant Function Called")
+
+    local body, err = core.request.get_body()
+    if err or not body then
+        log.error("Failed to get request body: ", err)
+        return 503
+    end
+    local parameters = ngx.decode_args(body)
+
+    local username = parameters["username"]
+    local password = parameters["password"]
+
+    if not username then
+        local err = "username is missing."
+        log.error(err)
+        return 422, err
+    end
+    if not password then
+        local err = "password is missing."
+        log.error(err)
+        return 422, err
+    end
+
+    local client_id = authz_keycloak_get_client_id(conf)
+
+    local token_endpoint = authz_keycloak_get_token_endpoint(conf)
+
+    if not token_endpoint then
+        local err = "Unable to determine token endpoint."
+        log.error(err)
+        return 500, err

Review comment:
       Valid point. I updated it. I didn't update at other places because there can be backward compatibility issues and there should be separate PR for the same.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [apisix] spacewander merged pull request #6586: feat: add support for password grant in keycloak plugin

Posted by GitBox <gi...@apache.org>.
spacewander merged pull request #6586:
URL: https://github.com/apache/apisix/pull/6586


   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@apisix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org