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 e555a00
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 79 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
21 changes: 11 additions & 10 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,25 @@ 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`.
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`.
You need to pass `application/x-www-form-urlencoded` as `Content-Type` header and `username`, `password` as parameters.

And for other route config, if will check token and redirect to the resource which are allocated to users.

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

## Token generation example
The value of `password_grant_token_generation_incoming_uri` is `/api/token`.

```cURL Code
curl --location --request POST 'http://127.0.0.1:9080/api/Token' \
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 e555a00

Please sign in to comment.