dfabric / dppm-rest-api Goto Github PK
View Code? Open in Web Editor NEWA REST API in Kemal for interacting with DPPM
Home Page: https://dfabric.github.io/dppm-rest-api
License: ISC License
A REST API in Kemal for interacting with DPPM
Home Page: https://dfabric.github.io/dppm-rest-api
License: ISC License
The current implementation of build_json
requires that no exceptions be thrown while the response is being built. Until now, the only resolution to this I could think of was building to an intermediary IO and copying to context.response
after it's built. That would be a big performance hit in a crucial spot. However, talking another look at my implementation I was thinking that there is one alternative, although it is less than ideal....suggestions are welcome, but I'd recommend we discuss this in a separate PR, so that I can also begin work on other routes while we work out the details.
def build_json(response : IO)
error : Exception? = nil
JSON.build response do |json|
json.object do
json.field "data" do
json.object do
yield json
rescue e
error = e
end
end
if error
json.field "errors" do
ErrorResponse.new(error).errors.to_json json
end
end
end
end
end
I realize this would require specifying a partial succesful response, but there's no way to rewind an HTTP::Server::Response
because it's streamed out to the client on the fly.
Originally posted by @dscottboggs in #10
For the package routes, I (think) I successfully avoided cases which raise, so that's an option as well, but it's...fragile, and I don't like it, especially not long-term
Since the beginning of the project, I have been using the convention of returning an JSON object which has at its top-level some meaningful keys which our client can look for (I.E. {"errors": ..., "data": ...}
). The "errors" key is always present -- in the case of no errors it is an empty list. The reason for it being a list is so that we can express to the user (or the author of a client library) a more meaningful error, by allowing the server to note a list of errors rather than a single one.
However, the built-in error handling solutions in Crystal (raising exceptions) and in Kemal (exceptions, halt
) are ideally designed to stop the execution of the context upon an error, without going on. My initial idea was to store a list of errors on HTTP::Server::Context
, but after looking through the overrides Kemal offers on Context
, I thought of this solution.
Now that I think about it though, neither solution works at all. The result would look something like...
{
"data": { "some": "data" }
}
{
"errors": ["an error", "Not Found"]
}
... which isn't even valid JSON.
Maybe if we check if context.response
is empty (hasn't yet been written to) before writing to it in the error handler. Idk, that still doesn't solve the issue of what to do inside Actions.throw_error
and the deny_access!
macro.
This issue is here to note the necessary steps we need to take to validate any and all received data from the API before passing it through to the library. A high-level discussion of implementation may be appropriate, but details should be worked out in a PR that will later be linked to this issue. This is an important security step (especially since many parameters translate directly to file-paths!) and we should be careful to be well-organized and methodical in our implementation of this feature.
TODO
TODO
Feel free to edit this summary as needed to update to-do lists, documentation, etc.
To allow editing user permissions from the web UI or other end-user application.
/user
[ "name", "groups" ]
/users
[ "match_name", "match_groups", "api_key", "new_name", "add_groups", "remove_groups" ]
/user
edit the current user[ "new_name", "add_groups", "remove_groups" ]
/users
[ "match_name", "match_groups", "api_key" ]
/user
/users
/user/rekey
/users/rekey
/groups
/groups/:id/route
/groups/:id/param
/groups/:id/param
/groups/:id
crystal spec
hangs indefinitely. Ideally, it should work out of the box.spec/permissions.json
changes, this is a bit annoying. It may be linked to the upper command, so adding to .gitignore
isn't a proper solution.Inspired by HashiCorp Vault policies.
This allows to finely manage API route access, using patterns and permission composition.
The default permission is to deny the access to the resource.
This implementation isn't optimized, we can cache the roles in to the user struct in the future.
In the future we can even allow/disallow parameters like ?host=
or ?port=
require "json"
struct Document
include JSON::Serializable
getter groups : Array(Group)
getter users : Array(User)
struct User
include JSON::Serializable
getter name : String,
id : String,
group_ids : Set(String)
end
struct Group
include JSON::Serializable
getter name : String,
id : String,
description : String,
path_policies : Hash(String, PathPolicy)
def path_permissions(path : String, &block : Set(PathPolicy::Permission) ->)
path_policies.each do |path_key, policy|
if File.match? path_key, path
yield policy.permissions
end
end
end
struct PathPolicy
include JSON::Serializable
getter permissions : Set(Permission)
enum Permission
Create
Read
Update
Delete
def self.new(permission : String)
{% begin %}
case permission
{% for const in @type.constants %}
when {{const.downcase}} then {{const}}
{% end %}
else raise "invalid permission: #{permission}"
end
{% end %}
end
def to_json(json : JSON::Builder)
{% begin %}
case self
{% for const in @type.constants %}
when {{const}} then json.string "{{const.downcase}}"
{% end %}
else raise "unkown permission: #{value}"
end
{% end %}
end
end
end
end
def path_permissions(user : String, path : String) : Set(Group::PathPolicy::Permission)?
puts path
permissions = nil
if user = @users.find &.name.== "Tom"
@groups.each do |group|
if user.group_ids.includes? group.id
group.path_permissions path do |group_permissions|
if permissions
permissions = permissions & group_permissions
else
permissions = group_permissions
end
end
end
end
end
if !permissions || permissions.try &.empty?
puts "access to `#{path}` denied to #{user}"
return
end
puts permissions
end
end
json = <<-JSON
{ "users" : [
{
"name": "Tom",
"id": "092",
"group_ids": ["123"]
}
],
"groups": [
{
"name": "User",
"id": "123",
"description": "Default user group",
"path_policies": {
"/**": {
"permissions": ["create", "read", "update", "delete"]
},
"/app/**": {
"permissions": ["read", "update"]
},
"/app/*-private": {
"permissions": []
},
"/app/dppm": {
"permissions": []
}
}
}
]
}
JSON
doc = Document.from_json json
doc.path_permissions "Tom", "/app"
doc.path_permissions "Tom", "/app/dppm"
doc.path_permissions "Tom", "/app/new-app"
doc.path_permissions "Tom", "/app/new-private"
Output:
/app
Set{Create, Read, Update, Delete}
/app/dppm
access to `/app/dppm` denied to Document::User(@name="Tom", @id="092", @group_ids=Set{"123"})
/app/new-app
Set{Read, Update}
/app/new-private
access to `/app/new-private` denied to Document::User(@name="Tom", @id="092", @group_ids=Set{"123"})
If you wouldn't mind, I could use a hand setting up the config file/CLI/internal config structure to match up with the general style of DPPM and integrate with it. See my update to cli.cr
in a402837d.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.