taskrambler  0.1.9
Web server and task management solution.
route.c
Go to the documentation of this file.
1 /**
2  * \file
3  * This is the generic application router....
4  * Here RBAC can take place as every resource is always requested
5  * via an HTTP request.
6  *
7  * \author Georg Hopp
8  *
9  * \copyright
10  * Copyright © 2013 Georg Hopp
11  *
12  * This program is free software: you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation, either version 3 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 // for strchr and others.
27 #include <string.h>
28 
29 // for size_t
30 #include <sys/types.h>
31 
32 // for dlopen, dlsym
33 #include <dlfcn.h>
34 
35 // for toupper
36 #include <ctype.h>
37 
38 #include "router.h"
39 #include "hash.h"
40 #include "session.h"
41 #include "http/request.h"
42 #include "http/response.h"
44 
45 #include "utils/memory.h"
46 #include "commons.h"
47 
48 
49 #define COMMAND_LEN 128
50 
51 
52 HttpResponse
54  Router this,
55  HttpRequest request,
56  Session sess)
57 {
58  char functionName[COMMAND_LEN + this->nprefix * 10];
59  Hash args = NULL;
60  fptr_routable function;
61 
62  char * tmp;
63  char * command;
64  size_t ncommand;
65  char * response_data;
66  HttpResponse response;
67 
68  if ('/' != request->uri[0]) {
69  /*
70  * we only support absolute paths within our
71  * application
72  */
73  return NULL;
74  }
75 
76  command = &(request->uri[1]);
77  command[0] = toupper(command[0]);
78 
79  /*
80  * find end of command
81  */
82  tmp = strchr(command, '/');
83  if (NULL == tmp) {
84  ncommand = strlen(command);
85  } else {
86  ncommand = tmp - command;
87  }
88 
89  memcpy(functionName, this->prefix, this->nprefix);
90  memcpy(&(functionName[this->nprefix]),
91  command, MIN(COMMAND_LEN, ncommand));
92 
93  /**
94  * \todo
95  * now get all arguments if we have some
96  */
97 
98  /*
99  * following the crud pattern we map the first part
100  * of the uri and the request method to according
101  * function names.
102  */
103  switch (request->method_id) {
104  case HTTP_GET:
105  args = new(Hash);
106  strcpy(&(functionName[this->nprefix + ncommand]), "Read");
107  break;
108 
109  case HTTP_POST:
110  args = request->post;
111  strcpy(&(functionName[this->nprefix + ncommand]), "Create");
112  break;
113 
114  case HTTP_PUT:
115  args = request->post;
116  strcpy(&(functionName[this->nprefix + ncommand]), "Update");
117  break;
118 
119  case HTTP_DELETE:
120  strcpy(&(functionName[this->nprefix + ncommand]), "Delete");
121  break;
122 
123  default:
124  /* other methods are not subject of REST */
125  return NULL;
126  }
127 
128  /*
129  * \todo for the moment I don't cache the found symbol...
130  * I don't even check if there was an error...the only thing
131  * I do is checking a NULL symbol and in that case don't
132  * handle the request here.
133  */
134  dlerror();
135  function = dlsym(this->handle, functionName);
136 
137  /**
138  * \todo somewhere here or above access control have to take place
139  * Default policy should be deny, anyway, there are a few resource
140  * that should be accessible even when not logged in...the are at
141  * least most of the assets as well as functions like version or
142  * sessinfo and in fact currentuse to have a way to find out that
143  * one is not logged in.
144  * In general a deny will be handled by storing an error message in
145  * some stash and then trigger a redirect to the login page.
146  * To be really rbac it seems neccessary to me to create a user
147  * "not logged in" and assign him the exceptions to the default
148  * deny policy.
149  * For the moment I assume that if there is no resource for the
150  * URL in the application it must be an asset and just return NULL
151  * indication that we still have no response for the request.
152  * Another thought... resources will be created dynamically by
153  * creating tasks or users or anything.
154  * Each of these resources may have options to admin them. This means
155  * most of the time to be able to modify them but additionally the
156  * creater of the resource might need the right to modify the
157  * rbac rules that apply to that resource.
158  * So, if I keep the real resources and their rbac configuration
159  * separated as planned it might be neccessary to give the creater
160  * of a resource the ability to modify both.
161  *
162  * So lets assume user georg creates a task that might be identified
163  * by /task/uuid(task). Then additionally an rbac resource will be
164  * created identified by /rbac_resource/uuid(/task/uuid(task)).
165  * User georg will have all rights on both resources.
166  * This means that rbac resources are resources by their own but how
167  * to control the access to these, I can't build another rbac resource
168  * and another and and and... so I think it is neccessary that every
169  * resource as it is has to hold their access in itself.
170  * The creating user will gain access to all REST operations as well
171  * as the right to change access control (which again are REST operations
172  * on these.
173  *
174  * Sidenote: I use a slightly differen naming than the ansi spec uses
175  * I the term resource for object and action for operation.
176  *
177  * So most resources end up with the following set of possible actions:
178  * - create: (well obviously this is only useful for list resources
179  * eg. the tasklist of a new project)
180  * - read: be able to display the resource...
181  * (again there is a special thing with lists here. This
182  * only gives the right to see the list at all. When
183  * generating the list the access rights on each entry
184  * has to be checked and if there is no read right for it
185  * it should not be included in the list.)
186  * - update: be able to update a resource.
187  * (this makes no sense for list resources as the change when
188  * their members change)
189  * - delete: be able to remove a resource.
190  * (on list resources this should only be allowed if the list
191  * is empty, this is the only consistent behaviour I can think
192  * of because you can't always assume that by removing a
193  * list ii's associated members should also be removed)
194  * - rbac_read:
195  * - rbac_update:
196  *
197  * Well, rbac assignes only roles to resources... in that case, how can I
198  * achieve per user rights for specific resources... one way would be
199  * to give every user its own role, which makes the whole concept kind
200  * of useless.
201  *
202  * Then I could allow everyone to create new roles on demand. Then
203  * a user would create a role that allows others to view the resource
204  * and then add user to this role. This role creation could be done
205  * automatically and in the UI the user simply only adds the users
206  * that should have access to the specific action.
207  * On the other hand the user might associate an action on the resource
208  * to an existing role.
209  * thus giving, for example, all team members the right to use the
210  * according action. Again in the UI this would be a simple select
211  * from a list.
212  * Still it seems neccessary to have a suer_private role where only
213  * this one user is in and that has full access to all resource actions
214  * of each resource the user is creating...and if there is such a thing
215  * no new roles will be created when allowing others to take actions
216  * on specific resources...simply add the action to the private role of
217  * the other user.
218  * This private roles can be almost automatic.
219  * (created when user is created, removed when he is removed, etc. etc)
220  * Regarding the session...I hink it ok to use our sessions to store
221  * The resulting access rights defined by the roles the user is in.
222  * On the other hand...if we store them stere no immediate feedback is
223  * possible when one of the roles have been changed....well, maybe
224  * there is...each existing session for users that are associated with
225  * the changed role have to be updated. That is in any case better
226  * than calculating all the access right on every reqeust.
227  * So, what we have in place right now are users and sessions. Both
228  * can be extended to the needs for rbac.
229  * What we still need is a definition of resources and actions that
230  * build up a permission and roles in it self that will associate user
231  * with permissions.
232  */
233 
234  if (NULL == function) {
235  /**
236  * nothing there to handle the request ... so leave it to the
237  * caller...
238  */
239  char * error;
240 
241  if (NULL != (error = dlerror())) {
242  /**
243  * \todo add logging...maybe.
244  */
245  }
246 
247  return NULL;
248  }
249 
250  /*
251  * function has to allocate the memory for reponse_date by using
252  * memMalloc.
253  */
254  response_data = function(this->application, sess, args);
255 
256  switch (request->method_id) {
257  case HTTP_GET:
258  delete(args);
259  break;
260 
261  case HTTP_POST:
262  case HTTP_PUT:
263  case HTTP_DELETE:
264  default:
265  /* other methods are not subject of REST */
266  break;
267  }
268 
269  if (NULL != response_data) {
270  response = httpResponseJson(response_data, strlen(response_data));
271  MEM_FREE(response_data);
272  } else {
273  response = httpResponse404();
274  }
275 
276  return response;
277 }
278 
279 // vim: set ts=4 sw=4:
#define MEM_FREE(seg)
Definition: memory.h:28
#define COMMAND_LEN
Definition: route.c:49
HttpResponse httpResponseJson(const char *, size_t)
Definition: json.c:40
#define MIN(a, b)
Definition: commons.h:35
HttpResponse routerRoute(Router this, HttpRequest request, Session sess)
Definition: route.c:53
char *(* fptr_routable)(Application, Session, Hash)
Definition: router.h:43
HttpResponse httpResponse404()
Definition: 404.c:47