diff options
| author | mo khan <mo@mokhan.ca> | 2025-03-05 11:10:34 -0700 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-03-05 11:10:34 -0700 |
| commit | 502228f90f6e3e7b03d2c3165a9b8b8f00e29dce (patch) | |
| tree | f53507beb32ef880b9571826d989683f7ba2ee59 | |
| parent | 98512feda282c2138e0bad54eb491f00e1cd5105 (diff) | |
feat: add a REST API service
| -rwxr-xr-x | bin/idp | 2 | ||||
| -rwxr-xr-x | bin/rest-api | 106 | ||||
| -rwxr-xr-x | bin/sp | 2 | ||||
| -rw-r--r-- | magefile.go | 7 |
4 files changed, 114 insertions, 3 deletions
@@ -320,5 +320,5 @@ if __FILE__ == $0 run IdentityProvider.new end.to_app - Rackup::Server.start(app: app, Port: 8282) + Rackup::Server.start(app: app, Port: ENV.fetch('PORT', 8282).to_i) end diff --git a/bin/rest-api b/bin/rest-api new file mode 100755 index 00000000..ca0a8916 --- /dev/null +++ b/bin/rest-api @@ -0,0 +1,106 @@ +#!/usr/bin/env ruby + +require 'bundler/inline' + +gemfile do + source 'https://rubygems.org' + + gem "erb", "~> 4.0" + gem "json", "~> 2.0" + gem "rack", "~> 3.0" + gem "rackup", "~> 2.0" + gem "securerandom", "~> 0.1" + gem "webrick", "~> 1.0" +end + +class Project + class << self + def all + @projects ||= [] + end + + def create!(attributes) + new({ id: SecureRandom.uuid }.merge(attributes)).tap do |item| + all << item + end + end + end + + def initialize(attributes = {}) + @attributes = attributes + end + + def to_h + @attributes + end +end + +class RESTAPI + def initialize + @storage = {} + end + + def call(env) + request = Rack::Request.new(env) + path = env['PATH_INFO'] + case env['REQUEST_METHOD'] + when 'GET' + case path + when '/projects.json' + return json_ok(Project.all.map(&:to_h)) + else + return json_not_found + end + when 'POST' + case path + when "/projects" + if authorized?(request, :create_project) + return json_created(Project.create!(JSON.parse(request.body.read, symbolize_names: true))) + else + return json_unauthorized(:create_project) + end + else + return json_not_found + end + end + json_not_found + end + + private + + def authorized?(request, permission) + # TODO:: Check the JWT for the appropriate claim + # Connect to the Authz RPC endpoint Ability.allowed?(subject, permission, resource) + true + end + + def json_not_found + [404, { 'X-Backend-Server' => 'REST', 'Content-Type' => 'application/json' }, []] + end + + def json_ok(body) + [200, { 'Content-Type' => 'application/json' }, [JSON.pretty_generate(body)]] + end + + def json_created(body) + [201, { 'Content-Type' => 'application/json' }, [JSON.pretty_generate(body.to_h)]] + end + + def json_unauthorized(permission) + [401, { 'Content-Type' => 'application/json' }, [JSON.pretty_generate({ + error: { + code: 401, + message: "`#{permission}` is required", + } + })]] + end +end + +if __FILE__ == $0 + app = Rack::Builder.new do + use Rack::Reloader + run RESTAPI.new + end.to_app + + Rackup::Server.start(app: app, Port: ENV.fetch('PORT', 8284).to_i) +end @@ -159,5 +159,5 @@ if __FILE__ == $0 run ServiceProvider.new end.to_app - Rackup::Server.start(app: app, Port: 8283) + Rackup::Server.start(app: app, Port: ENV.fetch('PORT', 8283).to_i) end diff --git a/magefile.go b/magefile.go index 39afbf93..40df5ade 100644 --- a/magefile.go +++ b/magefile.go @@ -30,6 +30,11 @@ func RunGateway() error { return sh.RunV("go", "run", "./cmd/gtwy/main.go") } +// Run the REST API +func RunApi() error { + return sh.RunV("ruby", "./bin/rest-api") +} + // Open a web browser to the login page func Browser() error { if runtime.GOOS == "linux" { @@ -41,5 +46,5 @@ func Browser() error { // Run All the servers func Run(ctx context.Context) { - mg.CtxDeps(ctx, RunIdp, RunSp, RunGateway, Browser) + mg.CtxDeps(ctx, RunIdp, RunSp, RunApi, RunGateway) } |
