route.c
9.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
/**
* \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:
args = request->post;
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;
}
if (NULL != response_data) {
response = httpResponseJson(response_data, strlen(response_data));
MEM_FREE(response_data);
} else {
response = httpResponse404();
}
return response;
}
// vim: set ts=4 sw=4: