Skip to content

Commit

Permalink
Refactored code.
Browse files Browse the repository at this point in the history
Better variable names.
Documentation updated as per guideline.
  • Loading branch information
azilentech committed Mar 12, 2022
1 parent f952324 commit 3d1e27b
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 80 deletions.
141 changes: 72 additions & 69 deletions apisix/plugins/authz-keycloak.lua
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ local schema = {
access_token_expires_leeway = {type = "integer", minimum = 0, default = 0},
refresh_token_expires_in = {type = "integer", minimum = 1, default = 3600},
refresh_token_expires_leeway = {type = "integer", minimum = 0, default = 0},
token_generation_endpoint = {type = "string", minLength = 1, maxLength = 4096},
password_grant_token_generation_incoming_uri = {type = "string", minLength = 1, maxLength = 4096},
},
allOf = {
-- Require discovery or token endpoint.
Expand Down Expand Up @@ -697,102 +697,105 @@ local function fetch_jwt_token(ctx)
end
-- This function is used to split data from array by respective delimitter and return array in result
local function stringsplit(s, delimiter)
local result = {};
local result = {}
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match);
table.insert(result, match)
end
return result;
return result
end

-- To get new access token by calling get token api
local function generate_token(conf,ctx)
log.warn("------------Generate Access Token Function Called---------------")
local function generate_token_using_password_grant(conf,ctx)
log.warn("------------generate_token_using_password_grant Function Called---------------")
--Read Body
ngx.req.read_body(); --To read requestbody first
ngx.req.read_body()
--Get Body Data
local RequestBody=ngx.req.get_body_data();
local UserName = ""; local Password = "";
local request_body=ngx.req.get_body_data()
local username = nil
local password = nil
--split by &
local strBodyArr = stringsplit(RequestBody, "&");
local parameters_array = stringsplit(request_body, "&")
if strBodyArr then
for k, strBodyValue in ipairs(strBodyArr) do
if string.find(strBodyValue, "username") then
for k, parameter in ipairs(parameters_array) do
if string.find(parameter, "username") then
--split by =
local usr = stringsplit(strBodyValue, "=");
UserName = usr[2];
local username_value_array = stringsplit(parameter, "=")
username = username_value_array[2]
end
if string.find(strBodyValue, "password") then
local psw = stringsplit(strBodyValue, "=");
Password = psw[2];
if string.find(parameter, "password") then
--split by =
local password_value_array = stringsplit(parameter, "=")
password = password_value_array[2]
end
end
end

if not username then
err = "username is missing"
log.error(err)
return 422, err
end
if not password then
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
log.error("Unable to determine token endpoint.")
return 500, "Unable to determine token endpoint."
err = "Unable to determine token endpoint."
log.error(err)
return 500, err
end
local httpc = authz_keycloak_get_http_client(conf)
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"
}
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)
params = authz_keycloak_configure_params(params, conf)

local res, err = httpc:request_uri(token_endpoint, params)
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 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

log.warn(core.json.encode(json))
if not json then
err = "Could not decode JSON from token endpoint"
.. (err and (": " .. err) or '.')
log.error(err)
return 401, {message = err}
end

return res.status, res.body;
return res.status, res.body
end

function _M.access(conf, ctx)
-- Get Requested URI
local RequestURI = string.upper(ngx.var.request_uri);

if conf.token_generation_endpoint then
-- Get Token generation end point of key cloak which we have mession in keyclock plugin config
local token_generation_endpoint = string.upper(conf.token_generation_endpoint);
local curr_req_method = string.upper(ctx.curr_req_matched["_method"]);
--Call Generate Access Token function if "\Token" found in URI based on configuration
if RequestURI == token_generation_endpoint then
if curr_req_method ~= "POST" then
log.error("Invalid request type")
return 400, {message = "Request method must be POST only.!"}
end

return generate_token(conf,ctx);

if conf.password_grant_token_generation_incoming_uri then
if string.upper(ngx.var.request_uri) == string.upper(conf.password_grant_token_generation_incoming_uri) then
if string.upper(ctx.curr_req_matched["_method"]) == "POST" then
return generate_token_using_password_grant(conf,ctx)
end
end
log.debug("hit keycloak-auth access")
Expand Down
24 changes: 13 additions & 11 deletions docs/en/latest/plugins/authz-keycloak.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ For more information on Keycloak, refer to [Keycloak Authorization Docs](https:/
| keepalive_timeout | integer | optional | 60000 | positive integer >= 1000 | Idle timeout after which established HTTP connections will be closed. |
| keepalive_pool | integer | optional | 5 | positive integer >= 1 | Maximum number of connections in the connection pool. |
| access_denied_redirect_uri | string | optional | | [1, 2048] | Redirect unauthorized user with the given uri like "http://127.0.0.1/test", instead of returning `"error_description":"not_authorized"`. |
| token_generation_endpoint | string | optional | | | Endpoint path to identify URL pattern based on Path configuration. So that we can generate token if Request Uri match with this path. |
| password_grant_token_generation_incoming_uri | string | optional | | /api/token | You can set this uri value to generate token using password grant type. Plugin will compare incoming request uri with this value. |

### Discovery and Endpoints

Expand Down Expand Up @@ -123,24 +123,26 @@ 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.

### Token generation endpoint

If user wants to generate a token based on user name and password with the support of grant type `password`.
### Password Grant Token Generation Incoming URI

The user have to configure URI path (E.g. `/api/Token`) in `token_generation_endpoint` which will match with incomming Request URI path and it will generate a new token with using `token_endpoint`.
If you want to generate a token using `password` grant, you can set value of `password_grant_token_generation_incoming_uri`.

And for other route config, if will check token and redirect to the resource which are allocated to users.
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`.

The user must have to pass Content-Type header as `application/x-www-form-urlencoded` and `username & password` in body part of request.
You need to pass `application/x-www-form-urlencoded` as `Content-Type` header and `username`, `password` as parameters.

## Token generation example
**Sample request**

```cURL Code
curl --location --request POST 'http://127.0.0.1:9080/api/Token' \
If value of `password_grant_token_generation_incoming_uri` is `/api/token`, you can use following curl request.

```shell
curl --location --request POST 'http://127.0.0.1:9080/api/token' \
--header 'Accept: application/json, text/plain, */*' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'username=User Name' \
--data-urlencode 'password=Password'
--data-urlencode 'username=<User_Name>' \
--data-urlencode 'password=<Password>'
```

## How To Enable
Expand Down

0 comments on commit 3d1e27b

Please sign in to comment.