Implement app to test CAPI and simulate server side
[platform/core/security/askuser.git] / src / capi / test / privacy_privilege_manager_test.cpp
1 /*
2  *  Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License
15  */
16
17 /**
18  * @file        privacy_privilege_manager.c
19  * @author      Piotr Sawicki <p.sawicki2@partner.samsung.com>
20  * @version     1.0
21  * @brief       This file contains test of CAPI for Privacy Privilege Manager
22  */
23
24 #include <iostream>
25 #include <map>
26 #include <set>
27 #include <stdio.h>
28 #include <string.h>
29 #include <glib.h>
30 #include <unistd.h>
31
32 #include <attributes/attributes.h>
33 #include <askuser-notification/ask-user-client-channel.h>
34 #include <askuser-notification/ask-user-server-channel.h>
35 #include <askuser-notification/ask-user-types.h>
36 #include <privacy_privilege_manager.h>
37
38 using namespace AskUser::Protocol;
39
40 struct ServerPrivilegeRequest {
41
42     ServerPrivilegeRequest(RequestId reqId)
43     : m_id(reqId)
44     {}
45
46     ServerPrivilegeRequest(RequestId reqId, const Privilege &privilege)
47     : m_id(reqId)
48     , m_privilege(privilege)
49     {}
50
51     bool operator<(const ServerPrivilegeRequest &other) const {
52         return m_id < other.m_id;
53     }
54
55     RequestId m_id;
56     Privilege m_privilege;
57 };
58
59 typedef std::set<ServerPrivilegeRequest> PrivilegeRequestSet;
60
61 struct ClientRequest {
62     RequestId m_id;
63     Privilege m_privilege;
64 };
65
66 struct Connection
67 {
68     GIOChannel *gio_channel;
69     GIOCondition condition;
70     guint watch_id;
71 };
72
73 enum class Mode {
74     CLIENT,
75     SERVER,
76     UNKNOWN
77 };
78
79 const GIOCondition GIO_ERROR_CONDITION = static_cast<GIOCondition>(G_IO_ERR | G_IO_HUP | G_IO_NVAL);
80
81 GIOCondition maskToGIOCondition(int mask)
82 {
83     int ret = (mask & FdMask::READ ? G_IO_IN : 0) |
84               (mask & FdMask::WRITE ? G_IO_OUT : 0);
85     return static_cast<GIOCondition>(ret);
86 }
87
88 GIOCondition makeCondition(GIOCondition condition)
89 {
90     return static_cast<GIOCondition>(condition | GIO_ERROR_CONDITION);
91 }
92
93 bool isErrorCondition(GIOCondition condition)
94 {
95     return !!(GIO_ERROR_CONDITION & condition);
96 }
97
98 void print_prompt(Mode mode)
99 {
100     switch (mode) {
101         case Mode::CLIENT:
102             printf("Client ");
103             break;
104         case Mode::SERVER:
105             printf("Server ");
106             break;
107         case Mode::UNKNOWN:
108             printf("Unknown ");
109             break;
110     }
111
112     printf("> ");
113     fflush(stdout);
114 }
115
116 struct ServerSimulator : public IServerCallbacks {
117
118     ServerSimulator()
119     : m_channel(nullptr)
120     {
121     }
122
123     virtual void newConnection(ConnectionFd fd, const Credentials &creds) {
124         printf("\nnew connection fd: %d credentials = { label: \"%s\" uid: %s }",
125                fd, creds.label.c_str(), creds.uid.c_str());
126     }
127
128     static gboolean server_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
129     {
130         ServerSimulator *handle = static_cast<ServerSimulator *>(data);
131
132         int fd = g_io_channel_unix_get_fd(src);
133         int events = (cond & G_IO_IN ? FdMask::READ : 0) |
134                      (cond & G_IO_OUT ? FdMask::WRITE : 0);
135
136         handle->m_channel->process(fd, isErrorCondition(cond) ? 0 : events);
137
138         return TRUE;
139     }
140
141     virtual void updateConnection(ConnectionFd fd, int mask) {
142         if (fd == -1) {
143             printf("ERROR: updateConnection() called with fd == -1\n");
144             return;
145         }
146
147         GIOCondition gio_condition = maskToGIOCondition(mask);
148
149         auto it = m_connections.find(fd);
150
151         if (mask == 0) {
152             if (it == m_connections.end())
153                 return;
154
155             auto &connection = it->second;
156
157             g_source_remove(connection.watch_id);
158             g_io_channel_unref(connection.gio_channel);
159
160             m_connections.erase(fd);
161             m_requests.erase(fd);
162
163             printf("Connection for fd: %d has been closed\n", fd);
164             print_prompt(Mode::SERVER);
165             return;
166         }
167
168         if (it == m_connections.end()) {
169             GIOChannel *gio_channel = g_io_channel_unix_new(fd);
170
171             g_io_channel_set_encoding (gio_channel, nullptr, nullptr);
172             g_io_channel_set_close_on_unref(gio_channel, FALSE);
173
174             guint watch_id = g_io_add_watch(gio_channel,
175                                       makeCondition(gio_condition),
176                                       server_gio_cb,
177                                       this);
178
179             m_connections.insert({ fd, Connection{gio_channel, gio_condition, watch_id }});
180
181             return;
182         }
183
184         auto &connection = it->second;
185
186         if (connection.condition != gio_condition) {
187             connection.condition = gio_condition;
188             g_source_remove(connection.watch_id);
189             connection.watch_id = g_io_add_watch(connection.gio_channel,
190                                                  makeCondition(connection.condition),
191                                                  server_gio_cb,
192                                                  this);
193         }
194     }
195
196     virtual void popup(ConnectionFd fd, RequestId id, Privilege &&privilege) {
197         printf("\npopup request fd: %d requestId: %d privilege: \"%s\"\n", fd, id, privilege.c_str());
198         addRequest(fd, id, privilege);
199         print_prompt(Mode::SERVER);
200     }
201
202     void addRequest(ConnectionFd fd, RequestId id, const Privilege &privilege) {
203         auto fdIt = m_requests.find(fd);
204         if (fdIt == m_requests.end()) {
205             m_requests.insert({ fd, PrivilegeRequestSet() });
206         }
207
208         auto &s = m_requests[fd];
209
210         auto reqIt = s.find(id);
211         if (reqIt != s.end()) {
212             printf("ERROR: We already have request id: %d for fd: %d\n", id, fd);
213             return;
214         }
215
216         s.insert(ServerPrivilegeRequest(id, privilege));
217     }
218
219     void sendResponse(ConnectionFd fd, RequestId id, int response) {
220         auto fdIt = m_requests.find(fd);
221         if (fdIt == m_requests.end()) {
222             printf("ERROR: Connection for fd: %d doesn't exist!\n", fd);
223             return;
224         }
225
226         auto &s = fdIt->second;
227
228         auto reqIt = s.find(id);
229         if (reqIt == s.end()) {
230             printf("ERROR: Request %d doesn't exist for fd: %d\n", id, fd);
231             return;
232         }
233
234         if (m_channel) {
235             m_channel->popupResponse(fd, id, response);
236
237             // delete request
238             s.erase(reqIt);
239             if (s.empty()) {
240                 m_requests.erase(fdIt);
241             }
242         }
243     }
244
245     void showPrivilegeRequests() {
246         printf("fd          requestId   privilege\n");
247         for (const auto &con : m_requests) {
248             for (const auto &req : con.second) {
249                 printf("%-11d %-11d %-20s\n", con.first, req.m_id, req.m_privilege.c_str());
250             }
251         }
252     }
253
254     void closeConnection(int fd) {
255         auto it = m_connections.find(fd);
256         if (it == m_connections.end()) {
257             printf("ERROR: Connection fd: %d not found\n", fd);
258             print_prompt(Mode::SERVER);
259             return;
260         }
261
262         m_requests.erase(fd);
263         m_channel->process(fd, 0);
264     }
265
266     void showConnections() {
267         printf("fd\n");
268         for (const auto &con : m_connections) {
269             printf("%d\n", con.first);
270         }
271     }
272
273     void setServerChannel(ServerChannel *ptr) {
274         m_channel = ptr;
275     }
276
277 private:
278     ServerChannel *m_channel;
279     std::map<ConnectionFd, Connection> m_connections;
280     std::map<ConnectionFd,  PrivilegeRequestSet> m_requests;
281 };
282
283 struct AppContext {
284     AppContext()
285     : m_serverSimulator(nullptr)
286     , m_channel(nullptr)
287     , m_mode(Mode::UNKNOWN)
288     {}
289
290     ~AppContext() {
291         if (m_channel != nullptr) {
292             delete m_channel;
293         }
294     }
295
296     void initialize() {
297         if (m_mode == Mode::SERVER) {
298             m_serverSimulator = new ServerSimulator();
299             m_channel = new ServerChannel(ServerCallbacksPtr(m_serverSimulator));
300             m_serverSimulator->setServerChannel(m_channel);
301         }
302     }
303
304     ServerSimulator *m_serverSimulator;
305     ServerChannel *m_channel;
306     Mode m_mode;
307 };
308
309 const char *popup_result_to_str(ppm_popup_result_e result)
310 {
311     switch (result) {
312         case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_ALLOW_FOREVER:
313             return "ALLOW_FOREVER";
314         case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_FOREVER:
315             return "DENY_FOREVER";
316         case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_ONCE:
317             return "DENY_ONCE";
318     }
319
320     return "UNKNOWN";
321 }
322
323 void popup_response_cb(ppm_call_cause_e cause, ppm_popup_result_e result, void *user_data)
324 {
325     ClientRequest *request = static_cast<ClientRequest *>(user_data);
326     if (request == nullptr) {
327         printf("ERROR: User data is nullptr\n");
328         return;
329     }
330
331     printf("localId: %d privilege: \"%s\" ", request->m_id, request->m_privilege.c_str());
332
333     switch (cause) {
334         case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER:
335             printf("popup response ANSWER: %s\n", popup_result_to_str(result));
336             break;
337         case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR:
338             printf("popup response ERROR cause: PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR\n");
339             break;
340     }
341
342     delete request;
343
344     print_prompt(Mode::CLIENT);
345 }
346
347 void print_client_help()
348 {
349     printf("c <privilege>   check privilege status\n");
350     printf("r <privilege>   send popup request\n");
351     printf("h               print help\n");
352     printf("q               quit\n");
353     print_prompt(Mode::CLIENT);
354 }
355
356 void print_client_error(ppm_error_e error)
357 {
358     switch (error) {
359         case PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE:
360             break;
361         case PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR:
362             printf("I/O error\n");
363             break;
364         case  PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER:
365             printf("Invalid parameters\n");
366             break;
367         case PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY:
368             printf("Out of memory\n");
369             break;
370         case PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN:
371             printf("Unknown error\n");
372             break;
373     }
374 }
375
376 static void print_client_check_result(ppm_check_result_e result)
377 {
378     printf("res: ");
379
380     switch (result) {
381         case  PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ALLOW:
382             printf("ALLOW\n");
383             break;
384         case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_DENY:
385             printf("DENY\n");
386             break;
387         case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ASK:
388             printf("ASK\n");
389             break;
390     }
391
392     print_prompt(Mode::CLIENT);
393 }
394
395 void handle_client(char *buffer)
396 {
397     ppm_check_result_e result;
398     ppm_error_e err = PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
399     ClientRequest *request = nullptr;
400     char *privilege = nullptr;
401     static RequestId clientRequestId;
402
403     switch (buffer[0]) {
404         case 'c':
405             if (strlen(buffer) < 3) {
406                 print_client_help();
407                 break;
408             }
409             err = static_cast<ppm_error_e>(ppm_check_privilege(&buffer[2], &result));
410             if (err == PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) {
411                 print_client_check_result(result);
412             }
413             break;
414         case 'r':
415             if (strlen(buffer) < 3) {
416                 print_client_help();
417                 break;
418             }
419
420             buffer[strlen(buffer)] = '\0'; // erase new line
421             privilege = &buffer[2];
422
423             printf("sending localId: %d privilege: \"%s\"\n", clientRequestId, privilege);
424
425             request = new ClientRequest{ clientRequestId++, privilege };
426             err = static_cast<ppm_error_e>(ppm_popup_request(privilege, popup_response_cb, request));
427             if (err != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) {
428                 delete request;
429             }
430
431             break;
432         case 'q':
433             exit(EXIT_SUCCESS);
434         case 'h':
435         default:
436             print_client_help();
437             return;
438     }
439
440     print_client_error(err);
441     print_prompt(Mode::CLIENT);
442 }
443
444 void print_server_help()
445 {
446     printf("l                       list all connections\n");
447     printf("s                       list all requests\n");
448     printf("ra <fd> <request id>    send ALLOW FOREVER response\n");
449     printf("rd <fd> <request id>    send DENY FOREVER response\n");
450     printf("ro <fd> <request id>    send DENY ONCE response\n");
451     printf("c  <fd>                 close connection\n");
452     printf("h                       print help\n");
453     printf("q                       quit\n");
454     print_prompt(Mode::SERVER);
455 }
456
457 void handle_server(const char *buffer, ServerSimulator *serverSimulator)
458 {
459     int res;
460     ConnectionFd fd;
461     RequestId requestId;
462
463     switch (buffer[0]) {
464         case 'l':
465             serverSimulator->showConnections();
466             break;
467         case 's':
468             serverSimulator->showPrivilegeRequests();
469             break;
470         case 'r':
471             res = sscanf(&buffer[2], "%d %d", &fd, &requestId);
472             if (res != 2) {
473                 print_server_help();
474                 return;
475             }
476             switch (buffer[1]) {
477                 case 'a':
478                     serverSimulator->sendResponse(fd, requestId, ASKUSER_ALLOW_FOREVER);
479                     break;
480                 case 'd':
481                     serverSimulator->sendResponse(fd, requestId, ASKUSER_DENY_FOREVER);
482                     break;
483                 case 'o':
484                     serverSimulator->sendResponse(fd, requestId, ASKUSER_DENY_ONCE);
485                     break;
486                 default:
487                     print_server_help();
488                     return;
489             }
490             break;
491         case 'c':
492             res = sscanf(&buffer[1], "%d", &fd);
493             if (res != 1) {
494                 print_server_help();
495                 return;
496             }
497             serverSimulator->closeConnection(fd);
498             return;
499         case 'h':
500             print_server_help();
501             return;
502         case 'q':
503             exit(EXIT_SUCCESS);
504     }
505
506     print_prompt(Mode::SERVER);
507 }
508
509 gboolean main_loop_cb(UNUSED GIOChannel *channel, UNUSED GIOCondition cond, gpointer user_data)
510 {
511     char buffer[4096];
512
513     int ret = read(0, buffer, sizeof(buffer));
514     if (ret <= 0) {
515         printf("read from stdin failed\n");
516         exit(EXIT_FAILURE);
517     }
518
519     buffer[ret - 1] = '\0';
520
521     AppContext *ctx = static_cast<AppContext *>(user_data);
522     switch (ctx->m_mode) {
523         case Mode::CLIENT:
524             handle_client(buffer);
525             break;
526         case Mode::SERVER:
527             handle_server(buffer, ctx->m_serverSimulator);
528             break;
529         case Mode::UNKNOWN:
530         default:
531             printf("Mode not set, exiting.\n");
532             exit(EXIT_FAILURE);
533     }
534
535     return TRUE;
536 }
537
538 void print_help(const char *programName)
539 {
540     printf("Usage: %s [OPTIONS]\n", programName);
541     printf("Allows to test CAPI for Privacy Privilege Manager.\n");
542     printf("This program can run in two modes: as a client and as a server.\n");
543     printf("You can use it in two scenarios:\n");
544     printf("1. Run as a client (%s -c) to trigger popups in the askuser-notification daemon.\n", programName);
545     printf("2. Run one part as a client (%s -c) and other one as a server (%s -s).\n",
546            programName, programName);
547     printf("If you want to run this program as a root you need to create /var/run/user_ext/0 folder.\n");
548     printf("To run this program in context of some Tizen's application, type in the console:\n");
549     printf("#echo <SMACK label of the application> > /proc/self/attr/current\n");
550     printf("#su - <user name>\n\n");
551     printf("Options:\n");
552     printf("-s  run as a server\n");
553     printf("-c  run as a client (uses CAPI)\n");
554     printf("-h  display this help end exit\n");
555 }
556
557 int main(int argc, char **argv)
558 {
559     AppContext ctx;
560     int c;
561
562     opterr = 0;
563     while ((c = getopt (argc, argv, "sch")) != -1) {
564         switch (c) {
565             case 's':
566                 ctx.m_mode = Mode::SERVER;
567                 print_server_help();
568                 break;
569             case 'c':
570                 ctx.m_mode = Mode::CLIENT;
571                 print_client_help();
572                 break;
573             case 'h':
574             default:
575                 break;
576         }
577     }
578
579     if (ctx.m_mode == Mode::UNKNOWN) {
580         print_help(argv[0]);
581         exit(EXIT_SUCCESS);
582     }
583
584     try {
585         ctx.initialize();
586     }
587     catch (...) {
588         printf("Unknwon exception occured, exiting.\n");
589         exit(EXIT_FAILURE);
590     }
591
592     GMainLoop *mainloop = g_main_loop_new(nullptr, FALSE);
593
594     GIOChannel *gio_channel = g_io_channel_unix_new(0);
595     g_io_add_watch(gio_channel, static_cast<GIOCondition>(G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL),
596                    main_loop_cb, &ctx);
597
598     g_main_loop_run(mainloop);
599
600     return 0;
601 }