You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
9.0 KiB
274 lines
9.0 KiB
/**
|
|
* \file
|
|
* This is the generic application router....
|
|
* Here RBAC can take place as every resource is always requested
|
|
* via an HTTP request.
|
|
*
|
|
* \author Georg Hopp
|
|
*
|
|
* \copyright
|
|
* Copyright © 2013 Georg Hopp
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// for strchr and others.
|
|
#include <string.h>
|
|
|
|
// for size_t
|
|
#include <sys/types.h>
|
|
|
|
// for dlopen, dlsym
|
|
#include <dlfcn.h>
|
|
|
|
// for toupper
|
|
#include <ctype.h>
|
|
|
|
#include "router.h"
|
|
#include "hash.h"
|
|
#include "session.h"
|
|
#include "http/request.h"
|
|
#include "http/response.h"
|
|
#include "application/application.h"
|
|
|
|
#include "utils/memory.h"
|
|
#include "commons.h"
|
|
|
|
|
|
#define COMMAND_LEN 128
|
|
|
|
|
|
HttpResponse
|
|
routerRoute(
|
|
Router this,
|
|
HttpRequest request,
|
|
Session sess)
|
|
{
|
|
char functionName[COMMAND_LEN + this->nprefix * 10];
|
|
Hash args = NULL;
|
|
fptr_routable function;
|
|
|
|
char * tmp;
|
|
char * command;
|
|
size_t ncommand;
|
|
char * response_data;
|
|
HttpResponse response;
|
|
|
|
if ('/' != request->uri[0]) {
|
|
/*
|
|
* we only support absolute paths within our
|
|
* application
|
|
*/
|
|
return NULL;
|
|
}
|
|
|
|
command = &(request->uri[1]);
|
|
command[0] = toupper(command[0]);
|
|
|
|
/*
|
|
* find end of command
|
|
*/
|
|
tmp = strchr(command, '/');
|
|
if (NULL == tmp) {
|
|
ncommand = strlen(command);
|
|
} else {
|
|
ncommand = tmp - command;
|
|
}
|
|
|
|
memcpy(functionName, this->prefix, this->nprefix);
|
|
memcpy(&(functionName[this->nprefix]),
|
|
command, MIN(COMMAND_LEN, ncommand));
|
|
|
|
/**
|
|
* \todo
|
|
* now get all arguments if we have some
|
|
*/
|
|
|
|
/*
|
|
* following the crud pattern we map the first part
|
|
* of the uri and the request method to according
|
|
* function names.
|
|
*/
|
|
switch (request->method_id) {
|
|
case HTTP_GET:
|
|
args = new(Hash);
|
|
strcpy(&(functionName[this->nprefix + ncommand]), "Read");
|
|
break;
|
|
|
|
case HTTP_POST:
|
|
args = request->post;
|
|
strcpy(&(functionName[this->nprefix + ncommand]), "Create");
|
|
break;
|
|
|
|
case HTTP_PUT:
|
|
strcpy(&(functionName[this->nprefix + ncommand]), "Update");
|
|
break;
|
|
|
|
case HTTP_DELETE:
|
|
strcpy(&(functionName[this->nprefix + ncommand]), "Delete");
|
|
break;
|
|
|
|
default:
|
|
/* other methods are not subject of REST */
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* \todo for the moment I don't cache the found symbol...
|
|
* I don't even check if there was an error...the only thing
|
|
* I do is checking a NULL symbol and in that case don't
|
|
* handle the request here.
|
|
*/
|
|
dlerror();
|
|
function = dlsym(this->handle, functionName);
|
|
|
|
/**
|
|
* \todo somewhere here or above access control have to take place
|
|
* Default policy should be deny, anyway, there are a few resource
|
|
* that should be accessible even when not logged in...the are at
|
|
* least most of the assets as well as functions like version or
|
|
* sessinfo and in fact currentuse to have a way to find out that
|
|
* one is not logged in.
|
|
* In general a deny will be handled by storing an error message in
|
|
* some stash and then trigger a redirect to the login page.
|
|
* To be really rbac it seems neccessary to me to create a user
|
|
* "not logged in" and assign him the exceptions to the default
|
|
* deny policy.
|
|
* For the moment I assume that if there is no resource for the
|
|
* URL in the application it must be an asset and just return NULL
|
|
* indication that we still have no response for the request.
|
|
* Another thought... resources will be created dynamically by
|
|
* creating tasks or users or anything.
|
|
* Each of these resources may have options to admin them. This means
|
|
* most of the time to be able to modify them but additionally the
|
|
* creater of the resource might need the right to modify the
|
|
* rbac rules that apply to that resource.
|
|
* So, if I keep the real resources and their rbac configuration
|
|
* separated as planned it might be neccessary to give the creater
|
|
* of a resource the ability to modify both.
|
|
*
|
|
* So lets assume user georg creates a task that might be identified
|
|
* by /task/uuid(task). Then additionally an rbac resource will be
|
|
* created identified by /rbac_resource/uuid(/task/uuid(task)).
|
|
* User georg will have all rights on both resources.
|
|
* This means that rbac resources are resources by their own but how
|
|
* to control the access to these, I can't build another rbac resource
|
|
* and another and and and... so I think it is neccessary that every
|
|
* resource as it is has to hold their access in itself.
|
|
* The creating user will gain access to all REST operations as well
|
|
* as the right to change access control (which again are REST operations
|
|
* on these.
|
|
*
|
|
* Sidenote: I use a slightly differen naming than the ansi spec uses
|
|
* I the term resource for object and action for operation.
|
|
*
|
|
* So most resources end up with the following set of possible actions:
|
|
* - create: (well obviously this is only useful for list resources
|
|
* eg. the tasklist of a new project)
|
|
* - read: be able to display the resource...
|
|
* (again there is a special thing with lists here. This
|
|
* only gives the right to see the list at all. When
|
|
* generating the list the access rights on each entry
|
|
* has to be checked and if there is no read right for it
|
|
* it should not be included in the list.)
|
|
* - update: be able to update a resource.
|
|
* (this makes no sense for list resources as the change when
|
|
* their members change)
|
|
* - delete: be able to remove a resource.
|
|
* (on list resources this should only be allowed if the list
|
|
* is empty, this is the only consistent behaviour I can think
|
|
* of because you can't always assume that by removing a
|
|
* list ii's associated members should also be removed)
|
|
* - rbac_read:
|
|
* - rbac_update:
|
|
*
|
|
* Well, rbac assignes only roles to resources... in that case, how can I
|
|
* achieve per user rights for specific resources... one way would be
|
|
* to give every user its own role, which makes the whole concept kind
|
|
* of useless.
|
|
*
|
|
* Then I could allow everyone to create new roles on demand. Then
|
|
* a user would create a role that allows others to view the resource
|
|
* and then add user to this role. This role creation could be done
|
|
* automatically and in the UI the user simply only adds the users
|
|
* that should have access to the specific action.
|
|
* On the other hand the user might associate an action on the resource
|
|
* to an existing role.
|
|
* thus giving, for example, all team members the right to use the
|
|
* according action. Again in the UI this would be a simple select
|
|
* from a list.
|
|
* Still it seems neccessary to have a suer_private role where only
|
|
* this one user is in and that has full access to all resource actions
|
|
* of each resource the user is creating...and if there is such a thing
|
|
* no new roles will be created when allowing others to take actions
|
|
* on specific resources...simply add the action to the private role of
|
|
* the other user.
|
|
* This private roles can be almost automatic.
|
|
* (created when user is created, removed when he is removed, etc. etc)
|
|
* Regarding the session...I hink it ok to use our sessions to store
|
|
* The resulting access rights defined by the roles the user is in.
|
|
* On the other hand...if we store them stere no immediate feedback is
|
|
* possible when one of the roles have been changed....well, maybe
|
|
* there is...each existing session for users that are associated with
|
|
* the changed role have to be updated. That is in any case better
|
|
* than calculating all the access right on every reqeust.
|
|
* So, what we have in place right now are users and sessions. Both
|
|
* can be extended to the needs for rbac.
|
|
* What we still need is a definition of resources and actions that
|
|
* build up a permission and roles in it self that will associate user
|
|
* with permissions.
|
|
*/
|
|
|
|
if (NULL == function) {
|
|
/**
|
|
* nothing there to handle the request ... so leave it to the
|
|
* caller...
|
|
*/
|
|
char * error;
|
|
|
|
if (NULL != (error = dlerror())) {
|
|
/**
|
|
* \todo add logging...maybe.
|
|
*/
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* function has to allocate the memory for reponse_date by using
|
|
* memMalloc.
|
|
*/
|
|
response_data = function(this->application, sess, args);
|
|
|
|
switch (request->method_id) {
|
|
case HTTP_GET:
|
|
delete(args);
|
|
break;
|
|
|
|
case HTTP_POST:
|
|
case HTTP_PUT:
|
|
case HTTP_DELETE:
|
|
default:
|
|
/* other methods are not subject of REST */
|
|
break;
|
|
}
|
|
|
|
response = httpResponseJson(response_data, strlen(response_data));
|
|
MEM_FREE(response_data);
|
|
|
|
return response;
|
|
}
|
|
|
|
// vim: set ts=4 sw=4:
|