Implement --set-policy option in Cyad
[platform/core/security/cynara.git] / src / cyad / CommandlineParser / CyadCommandlineParser.cpp
1 /*
2  * Copyright (c) 2014 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  * @file        src/cyad/CommandlineParser/CyadCommandlineParser.cpp
18  * @author      Aleksander Zdyb <a.zdyb@samsung.com>
19  * @version     1.0
20  * @brief       Commandline parser for Cyad
21  */
22
23 #include <cstring>
24 #include <getopt.h>
25 #include <map>
26 #include <sstream>
27 #include <string>
28
29 #include <cyad/CommandlineParser/PolicyParsingException.h>
30
31 #include "CyadCommandlineParser.h"
32
33 namespace Cynara {
34
35 namespace CyadCmdlineArgs {
36     const char HELP = 'h';
37     const char * const HELP_LONG = "help";
38
39     const char SET_BUCKET = 'b';
40     const char * const SET_BUCKET_LONG = "set-bucket";
41
42     const char DELETE_BUCKET = 'd';
43     const char * const DELETE_BUCKET_LONG = "delete-bucket";
44
45     const char POLICY = 'p';
46     const char * const POLICY_LONG = "policy";
47
48     const char METADATA = 'm';
49     const char * const METADATA_LONG = "metadata";
50
51     const char BUCKET = 'k';
52     const char * const BUCKET_LONG = "bucket";
53
54     const char SET_POLICY = 's';
55     const char * const SET_POLICY_LONG = "set-policy";
56
57     const char CLIENT = 'c';
58     const char * const CLIENT_LONG = "client";
59
60     const char USER = 'u';
61     const char * const USER_LONG = "user";
62
63     const char PRIVILEGE = 'r';
64     const char * const PRIVILEGE_LONG = "privilege";
65
66     const char BULK = 'f';
67     const char * const BULK_LONG = "bulk";
68 }
69
70 namespace CyadCmdlineErrors {
71     const char * const UNKNOWN_ERROR = "Unknown error";
72     const char * const NO_OPTION = "No option specified";
73     const char * const UNKNOWN_OPTION = "Unknown option";
74     const char * const UNKNOWN_OPTION_SET_BUCKET = "Unknown option in --set-bucket";
75     const char * const UNKNOWN_OPTION_DELETE_BUCKET = "Unknown option in --delete-bucket";
76     const char * const UNKNOWN_OPTION_SET_POLICY = "Unknown option in --set-policy";
77     const char * const NO_POLICY = "No --policy specified";
78     const char * const NO_BUCKET = "No bucket specified";
79     const char * const INVALID_POLICY = "Invalid --policy option";
80     const char * const OPTION_MISSING_SET_POLICY = "One or more option missing in --set-policy";
81     const char * const ARGUMENT_MISSING_SET_POLICY = "One or more argument missing in --set-policy";
82 }
83
84 CyadCommandlineParser::CyadCommandlineParser(int argc, char * const *argv)
85     : m_argc(argc), m_argv(argv) {}
86
87 CyadCommandlineParser::~CyadCommandlineParser() {}
88
89 std::shared_ptr<CyadCommand> CyadCommandlineParser::parseMain(void) {
90     namespace Args = CyadCmdlineArgs;
91
92     const struct option long_options[] = {
93         { Args::HELP_LONG,          no_argument,       nullptr, Args::HELP },
94         { Args::SET_BUCKET_LONG,    required_argument, nullptr, Args::SET_BUCKET },
95         { Args::DELETE_BUCKET_LONG, required_argument, nullptr, Args::DELETE_BUCKET },
96         { Args::SET_POLICY_LONG,    no_argument,       nullptr, Args::SET_POLICY },
97         { nullptr, 0, nullptr, 0 }
98     };
99
100     optind = 0; // On entry to `getopt', zero means this is the first call; initialize.
101     int opt;
102     std::stringstream optstr;
103     optstr << ":" << Args::HELP
104            << Args::SET_BUCKET << ":"
105            << Args::DELETE_BUCKET << ":"
106            << Args::SET_POLICY;
107
108     while ((opt = getopt_long(m_argc, m_argv, optstr.str().c_str(), long_options, nullptr)) != -1) {
109         switch (opt) {
110             case Args::HELP:
111                 return std::make_shared<HelpCyadCommand>();
112
113             case Args::SET_BUCKET:
114                 return parseSetBucket(optarg);
115
116             case Args::DELETE_BUCKET:
117                 return parseDeleteBucket(optarg);
118
119             case Args::SET_POLICY:
120                 return parseSetPolicy();
121
122             case '?': // Unknown option
123                 return std::make_shared<ErrorCyadCommand>(CyadCmdlineErrors::UNKNOWN_OPTION);
124
125             case ':': // Missing argument
126                 switch (optopt) {
127                     case Args::SET_BUCKET:
128                     case Args::DELETE_BUCKET:
129                         return std::make_shared<ErrorCyadCommand>(CyadCmdlineErrors::NO_BUCKET);
130                 }
131                 // Shall never happen, but let's just make compiler happy.
132                 return std::make_shared<ErrorCyadCommand>(CyadCmdlineErrors::UNKNOWN_ERROR);
133
134             default:
135                 return std::make_shared<ErrorCyadCommand>(CyadCmdlineErrors::UNKNOWN_OPTION);
136         }
137     }
138
139     return std::make_shared<ErrorCyadCommand>(CyadCmdlineErrors::NO_OPTION);
140 }
141
142 std::shared_ptr<CyadCommand> CyadCommandlineParser::parseSetBucket(const std::string &bucketId) {
143     namespace Args = CyadCmdlineArgs;
144     namespace Errors = CyadCmdlineErrors;
145
146     const struct option long_options[] = {
147         { Args::POLICY_LONG, required_argument, nullptr, Args::POLICY },
148         { Args::METADATA_LONG, required_argument, nullptr, Args::METADATA },
149         { nullptr, 0, nullptr, 0 }
150     };
151
152     std::string policy;
153     std::string metadata;
154
155     int opt;
156     std::stringstream optstr;
157     optstr << ":"
158            << Args::POLICY << ":"
159            << Args::METADATA << ":";
160
161     while ((opt = getopt_long(m_argc, m_argv, optstr.str().c_str(), long_options, nullptr)) != -1) {
162         switch(opt) {
163         case Args::POLICY:
164             policy = optarg;
165             break;
166         case Args::METADATA:
167             metadata = optarg;
168             break;
169         default:
170             return std::make_shared<ErrorCyadCommand>(Errors::UNKNOWN_OPTION_SET_BUCKET);
171         }
172     }
173
174     if (policy.empty())
175         return std::make_shared<ErrorCyadCommand>(Errors::NO_POLICY);
176
177     try {
178         auto policyType = parsePolicyType(policy);
179         return std::make_shared<SetBucketCyadCommand>(bucketId, PolicyResult(policyType, metadata));
180     } catch (const PolicyParsingException &) {
181         return std::make_shared<ErrorCyadCommand>(Errors::INVALID_POLICY);
182     }
183 }
184
185 std::shared_ptr<CyadCommand> CyadCommandlineParser::parseDeleteBucket(const std::string &bucketId) {
186     namespace Errors = CyadCmdlineErrors;
187
188     // Expect no additional options
189     const struct option long_options[] = {
190         { nullptr, 0, nullptr, 0 }
191     };
192
193     if (getopt_long(m_argc, m_argv, ":", long_options, nullptr) == -1) {
194         return std::make_shared<DeleteBucketCyadCommand>(bucketId);
195     } else {
196         return std::make_shared<ErrorCyadCommand>(Errors::UNKNOWN_OPTION_DELETE_BUCKET);
197     }
198 }
199
200 std::shared_ptr<CyadCommand> CyadCommandlineParser::parseSetPolicy(void) {
201     namespace Args = CyadCmdlineArgs;
202     namespace Errors = CyadCmdlineErrors;
203
204     const struct option long_options[] = {
205         { Args::CLIENT_LONG,    required_argument, nullptr, Args::CLIENT },
206         { Args::USER_LONG,      required_argument, nullptr, Args::USER },
207         { Args::PRIVILEGE_LONG, required_argument, nullptr, Args::PRIVILEGE },
208         { Args::BUCKET_LONG,    required_argument, nullptr, Args::BUCKET },
209         { Args::POLICY_LONG,    required_argument, nullptr, Args::POLICY },
210         { Args::METADATA_LONG,  required_argument, nullptr, Args::METADATA },
211         { Args::BULK_LONG,      required_argument, nullptr, Args::BULK },
212         { nullptr, 0, nullptr, 0 }
213     };
214
215     typedef std::map<char, std::string> OptionsValues;
216     OptionsValues values = { { Args::CLIENT,    std::string() },
217                              { Args::USER,      std::string() },
218                              { Args::PRIVILEGE, std::string() },
219                              { Args::POLICY,    std::string() } };
220
221     std::string metadata;
222     std::string bucket;
223
224     int opt;
225     std::stringstream optstr;
226     optstr << ":"
227            << Args::CLIENT << ":"
228            << Args::USER << ":"
229            << Args::PRIVILEGE << ":"
230            << Args::BUCKET << ":"
231            << Args::POLICY << ":"
232            << Args::METADATA << ":"
233            << Args::BULK << ":";
234
235     while ((opt = getopt_long(m_argc, m_argv, optstr.str().c_str(), long_options, nullptr)) != -1) {
236         switch(opt) {
237         case Args::CLIENT:
238         case Args::USER:
239         case Args::PRIVILEGE:
240         case Args::POLICY:
241             values[opt] = optarg;
242             break;
243         case Args::BUCKET:
244             bucket = optarg;
245             break;
246         case Args::METADATA:
247             metadata = optarg;
248             break;
249         case Args::BULK:
250             return std::make_shared<SetPolicyBulkCyadCommand>(optarg);
251         case ':': // Missing argument
252             return std::make_shared<ErrorCyadCommand>(Errors::ARGUMENT_MISSING_SET_POLICY);
253         default:
254             return std::make_shared<ErrorCyadCommand>(Errors::UNKNOWN_OPTION_SET_POLICY);
255         }
256     }
257
258     for (const auto &val : values) {
259         if (val.second.empty()) {
260             // TODO: Identify actual option
261             return std::make_shared<ErrorCyadCommand>(Errors::OPTION_MISSING_SET_POLICY);
262         }
263     }
264
265     try {
266         auto policyType = parsePolicyType(values[Args::POLICY]);
267         auto policyResult = PolicyResult(policyType, metadata);
268         return std::make_shared<SetPolicyCyadCommand>(bucket, policyResult,
269                                                       PolicyKey(values[Args::CLIENT],
270                                                                 values[Args::USER],
271                                                                 values[Args::PRIVILEGE]));
272     } catch (const PolicyParsingException &) {
273         return std::make_shared<ErrorCyadCommand>(Errors::INVALID_POLICY);
274     }
275
276     return std::make_shared<ErrorCyadCommand>(Errors::UNKNOWN_ERROR);
277 }
278
279 PolicyType CyadCommandlineParser::parsePolicyType(const std::string &rawPolicy) {
280     if (rawPolicy == "DENY")
281         return CYNARA_ADMIN_DENY;
282     if (rawPolicy == "NONE")
283         return CYNARA_ADMIN_NONE;
284     if (rawPolicy == "BUCKET")
285         return CYNARA_ADMIN_BUCKET;
286     if (rawPolicy == "ALLOW")
287         return CYNARA_ADMIN_ALLOW;
288
289     // If base for std::stoi is set to 0, it detects base by itself from the format of the sequence
290     try {
291         return std::stoi(rawPolicy, nullptr, 0);
292     } catch (const std::logic_error &) {
293         throw PolicyParsingException();
294     }
295 }
296
297 } /* namespace Cynara */