Skip to content

Commit

Permalink
Merge pull request #55 from holaplex/mpw/credits-plugin
Browse files Browse the repository at this point in the history
add credits plugin
  • Loading branch information
mpwsh authored Apr 25, 2023
2 parents 308bfbe + 7c55123 commit 7f6220e
Show file tree
Hide file tree
Showing 16 changed files with 314 additions and 66 deletions.
6 changes: 3 additions & 3 deletions charts/hub-gateway/Chart.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
dependencies:
- name: apisix
repository: https://charts.apiseven.com
version: 1.1.0
digest: sha256:0fe161042c77815e8e36847494825ed1232afdccc032635dbaaf347625cb3bfc
generated: "2023-01-30T10:53:37.535884-03:00"
version: 1.3.1
digest: sha256:f5e4c06ee49ce8bdf2ee3cf997ece55fb80071c8e122fad43197d9be8ddd32f3
generated: "2023-04-22T00:12:14.986923-03:00"
4 changes: 2 additions & 2 deletions charts/hub-gateway/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type: application
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)

version: '0.10.0'
version: "0.11.0"

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
Expand All @@ -31,6 +31,6 @@ sources:

dependencies:
- name: apisix
version: 1.1.0
version: 1.3.1
repository: https://charts.apiseven.com
condition: apisix.enabled
Binary file removed charts/hub-gateway/charts/apisix-1.1.0.tgz
Binary file not shown.
Binary file added charts/hub-gateway/charts/apisix-1.3.1.tgz
Binary file not shown.
31 changes: 14 additions & 17 deletions charts/hub-gateway/plugins/authz-helper.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,15 @@

local core = require("apisix.core")
local get_service = require("apisix.http.service").get
local http = require("resty.http")
local json = require("apisix.core.json")
local ngx = ngx
local ngx_time = ngx.time
local re_match = ngx.re.match
local type = type
local _M = {}


-- build a table of Nginx variables with some generality
-- between http subsystem and stream subsystem
local function build_var(conf, ctx)
local function build_var(ctx)
return {
server_addr = ctx.var.server_addr,
server_port = ctx.var.server_port,
Expand All @@ -37,7 +34,7 @@ local function build_var(conf, ctx)
}
end

local function build_http_request(conf, ctx)
local function build_http_request(ctx)
return {
scheme = core.request.get_scheme(ctx),
method = core.request.get_method(),
Expand All @@ -50,7 +47,7 @@ local function build_http_request(conf, ctx)
end


local function build_http_route(conf, ctx, remove_upstream)
local function build_http_route(ctx, remove_upstream)
local route = core.table.clone(ctx.matched_route).value

if remove_upstream and route and route.upstream then
Expand All @@ -61,7 +58,7 @@ local function build_http_route(conf, ctx, remove_upstream)
end


local function build_http_service(conf, ctx)
local function build_http_service(ctx)
local service_id = ctx.service_id

-- possible that there is no service bound to the route
Expand All @@ -81,7 +78,7 @@ local function build_http_service(conf, ctx)
return nil
end

local function build_graphql_data(conf, ctx)
local function build_graphql_data(ctx)
local input = json.decode(core.request.get_body())
return {
query = input['query'],
Expand All @@ -91,7 +88,7 @@ local function build_graphql_data(conf, ctx)
}
end

local function build_http_consumer(conf, ctx)
local function build_http_consumer(ctx)
-- possible that there is no consumer bound to the route
if ctx.consumer then
return core.table.clone(ctx.consumer)
Expand All @@ -103,24 +100,24 @@ end
function _M.build_opa_input(conf, ctx, subsystem)
local data = {
type = subsystem,
request = build_http_request(conf, ctx),
var = build_var(conf, ctx),
graphql = build_graphql_data(conf, ctx),
request = build_http_request(ctx),
var = build_var(ctx),
graphql = build_graphql_data(ctx),
keto_endpoint = conf.keto_endpoint
}

if conf.with_route then
data.route = build_http_route(conf, ctx, true)
data.route = build_http_route(ctx, true)
end

if conf.with_consumer then
data.consumer = build_http_consumer(conf, ctx)
data.consumer = build_http_consumer(ctx)
end

if conf.with_service then
data.service = build_http_service(conf, ctx)
data.service = build_http_service(ctx)
end

return {
input = data,
}
Expand Down
6 changes: 3 additions & 3 deletions charts/hub-gateway/plugins/authz.lua
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,16 @@ function _M.access(conf, ctx)
local res, err = httpc:request_uri(endpoint, params)

-- block by default when decision is unavailable
if not res then
if err then
return 403, json.encode({
message = "OPA Decision is unavailable. Errors found in policy"
})
end

-- parse the results of the decision
local data, err = json.decode(res.body)
local data, err_json = json.decode(res.body)

if not data then
if err_json then
return 503, json.encode({
message = "Invalid response body"
})
Expand Down
125 changes: 125 additions & 0 deletions charts/hub-gateway/plugins/credits.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local core = require("apisix.core")
local http = require("resty.http")
local json = require("apisix.core.json")

local schema = {
type = "object",
properties = {
host = {
type = "string"
},
ssl_verify = {
type = "boolean",
default = true
},
timeout = {
type = "integer",
minimum = 1,
maximum = 60000,
default = 3000,
description = "timeout in milliseconds"
},
keepalive = {
type = "boolean",
default = true
},
keepalive_timeout = {
type = "integer",
minimum = 1000,
default = 60000
},
keepalive_pool = {
type = "integer",
minimum = 1,
default = 5
},
},
required = {"host"}
}

local _M = {
version = 0.1,
priority = 3,
name = "credits",
schema = schema
}

function _M.check_schema(conf)
return core.schema.check(schema, conf)
end

function _M.access(conf, ctx)

-- return early if operation is query
local operation = ctx.var.graphql_operation
if not operation or operation == "query" then
return
end

local input = json.decode(core.request.get_body())
local graphql_query = input['query']

-- Check if query is createOrganization
if graphql_query:match("createOrganization") then
core.log.info("Skipping credits plugin - Creating Organization")
return
end

local org_id = core.request.header(ctx, "X-Organization-Id")

local params = {
method = "GET",
keepalive = conf.keepalive,
ssl_verify = conf.ssl_verify
}

if conf.keepalive then
params.keepalive_timeout = conf.keepalive_timeout
params.keepalive_pool = conf.keepalive_pool
end

local endpoint = conf.host .. "/internal/organizations/" .. org_id

local httpc = http.new()
httpc:set_timeout(conf.timeout)
local res, err = httpc:request_uri(endpoint, params)

-- return internal server error if unable to contact credits service
if err then
return 500, json.encode({ message = err })
end

local data, err_json = json.decode(res.body)

if err_json then
return 500, json.encode({ message = err })
end

if not data.balance then
return 500, json.encode({ message = err })
end

-- respond the credit balance to the user too
if conf.expose_credit_balance then
core.request.set_header(ctx, "X-Credit-Balance", data.balance)
core.response.set_header(ctx, "X-Credit-Balance", data.balance)
end
end

return _M
30 changes: 18 additions & 12 deletions charts/hub-gateway/plugins/oauth2.lua
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ local schema = {

local _M = {
version = 0.1,
priority = 3,
priority = 6,
name = "oauth2",
schema = schema
}
Expand Down Expand Up @@ -103,15 +103,16 @@ function _M.access(conf, ctx)
local res, err = httpc:request_uri(endpoint, params)

-- block by default when introspection failed
if not res then
if err then
return 401, json.encode({
message = err
})
end

-- parse the introspection data
local data, err = json.decode(res.body)
if not data then
local data, err_json = json.decode(res.body)

if err_json then
return 401, err
end

Expand All @@ -123,10 +124,10 @@ function _M.access(conf, ctx)
end

if conf.expose_client_id then
core.request.set_header(ctx, "X-CLIENT-ID", data.client_id)
core.response.set_header("X-CLIENT-ID", data.client_id)
core.request.set_header(ctx, "X-Client-Id", data.client_id)
core.response.set_header("X-Client-Id", data.client_id)
end

-- Lookup full hydra client data
local params = {
method = "GET",
Expand All @@ -146,21 +147,26 @@ function _M.access(conf, ctx)

local res, err = httpc:request_uri(endpoint, params)

-- block by default when unable to get client data
if err then
return 401, json.encode({message = err})
end

local data, err = json.decode(res.body)
if not data then
if err then
return 401, err
end
end

-- Expose hydra client owner id on request header
if conf.expose_owner then
if not data.owner then
core.log.error("unable to get owner from response:", json.encode(data))
end
core.request.set_header(ctx, "X-CLIENT-OWNER-ID", data.owner)
core.request.set_header(ctx, "X-Client-Owner-Id", data.owner)

-- Get kratos user id from hydra client contacts and expose on x-user-id header
core.request.set_header(ctx, "X-USER-ID", data.contacts[1])
core.response.set_header("X-USER-ID", data.contacts[1])
core.request.set_header(ctx, "X-User-Id", data.contacts[1])
core.response.set_header("X-User-Id", data.contacts[1])
end
end

Expand Down
Loading

0 comments on commit 7f6220e

Please sign in to comment.