Leosac  0.7.0
OpenSourceAccessControl
UserSecurityContext.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014-2016 Leosac
3 
4  This file is part of Leosac.
5 
6  Leosac is free software: you can redistribute it and/or modify
7  it under the terms of the GNU Affero General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  Leosac is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU Affero General Public License for more details.
15 
16  You should have received a copy of the GNU Affero General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "UserSecurityContext.hpp"
21 #include "core/auth/Group_odb.h"
22 #include "core/auth/IDoor.hpp"
23 #include "core/auth/User_odb.h"
25 #include "tools/db/DBService.hpp"
28 #include "tools/log.hpp"
29 
30 using namespace Leosac;
31 
33  : SecurityContext(dbsrv)
34  , user_id_(id)
35 {
36 }
37 
39  const ActionParam &ap) const
40 {
41  // Simply put: Administrator can do everything, without any permission check.
42  if (is_admin())
43  return true;
44 
45  switch (action)
46  {
47  case Action::IS_ADMIN:
48  return is_admin();
49  case Action::IS_MANAGER:
50  return is_manager();
51 
53  return is_manager();
55  return is_admin();
56  case Action::USER_READ:
57  return can_read_user(ap.user);
59  return can_read_user_detail(ap.user);
61  return can_update_user(ap.user);
64  return is_manager();
66  return is_manager() || is_self(ap.user.user_id);
67 
69  return true;
71  return true;
72  case Action::GROUP_READ:
73  return can_read_group(ap.group);
77  return can_administrate_group(ap.group);
80 
85 
86 
88  return can_read_credential(ap.cred);
89 
93  return is_manager();
94 
96  return can_read_schedule(ap.sched);
100  return is_manager();
102  return true;
103 
104  case Action::DOOR_READ:
105  return can_read_door(ap.door);
106  case Action::DOOR_CREATE:
107  case Action::DOOR_UPDATE:
108  case Action::DOOR_DELETE:
109  return is_manager();
110  case Action::DOOR_SEARCH:
111  return true;
112 
118  return is_manager();
119 
120  // Zone management is for manager.
121  case Action::ZONE_READ:
122  case Action::ZONE_CREATE:
123  case Action::ZONE_UPDATE:
124  case Action::ZONE_DELETE:
125  case Action::ZONE_SEARCH:
126  return is_manager();
127 
131  return is_admin();
132 
133  case Action::LOG_READ:
134  return is_manager();
135 
136  case Action::AUDIT_READ:
137  return is_manager();
139  return is_admin();
141  return is_manager();
142 
144  return is_admin();
145  default:
146  ASSERT_LOG(0, "Not handled.");
147  }
148  return false;
149 }
150 
152  const SecurityContext::GroupActionParam &gap) const
153 {
154  if (is_manager() || gap.group_id == 0) // listing group.
155  return true;
156  Auth::GroupPtr group =
157  dbsrv_->find_group_by_id(gap.group_id, DBService::THROW_IF_NOT_FOUND);
158  return group->member_has(user_id_);
159 }
160 
162  const SecurityContext::GroupActionParam &gap) const
163 {
164  if (is_manager())
165  return true;
166  Auth::GroupRank rank;
167  Auth::GroupPtr group =
168  dbsrv_->find_group_by_id(gap.group_id, DBService::THROW_IF_NOT_FOUND);
169  if (group->member_has(user_id_, &rank))
170  {
171  return rank == Auth::GroupRank::ADMIN;
172  }
173  return false;
174 }
175 
178 {
179  Auth::UserGroupMembershipPtr ugm = dbsrv_->find_membership_by_id(
181 
182  ActionParam ap;
183  ap.group.group_id = ugm->group_id();
184  return ugm->user_id() == user_id_ ||
186 }
187 
189  const SecurityContext::UserActionParam &) const
190 {
191  return true;
192 }
193 
195 {
196  return is_self(uap.user_id) || is_manager();
197 }
198 
200  const SecurityContext::UserActionParam &uap) const
201 {
202  return is_self(uap.user_id) || is_manager();
203 }
204 
207 {
208  if (is_manager())
209  return true;
210  // If we are at least Operator in the group, we can add someone.
211  Auth::GroupPtr group =
212  dbsrv_->find_group_by_id(map.group_id, DBService::THROW_IF_NOT_FOUND);
213  Auth::GroupRank rank;
214  if (group->member_has(user_id_, &rank))
215  {
216  if (rank >= Auth::GroupRank::OPERATOR && map.rank <= rank)
217  {
218  // Cannot invite to a rank superior our own rank.
219  return true;
220  }
221  }
222  return false;
223 }
224 
227 {
228  if (is_manager())
229  return true;
230  db::OptionalTransaction t(dbsrv_->db()->begin());
231  Auth::UserGroupMembershipPtr membership = dbsrv_->find_membership_by_id(
233 
234  auto group = membership->group().load();
235  auto target_user = membership->user().load();
236  t.commit();
237 
238  if (target_user->id() == user_id_)
239  return true; // Can leave any group.
240 
241  Auth::GroupRank my_rank;
242  if (group && group->member_has(user_id_, &my_rank))
243  {
244  if (my_rank >= Auth::GroupRank::OPERATOR && my_rank >= membership->rank())
245  {
246  // Cannot kick someone with a higher rank.
247  return true;
248  }
249  }
250  return false;
251 }
252 
254  const SecurityContext::ScheduleActionParam &cap) const
255 {
256  if (is_manager() || cap.schedule_id == 0)
257  return true;
258 
259  Tools::ISchedulePtr sched =
260  dbsrv_->find_schedule_by_id(cap.schedule_id, DBService::THROW_IF_NOT_FOUND);
261  // We need to find out is assigned to the schedule.
262  // If it is, we can read the schedule's data.
263  // Todo: Maybe have more fine grained permission here, like being able to read
264  // timeframes and door mapped but not everything.
265 
266  for (const auto &mapping : sched->mapping())
267  {
268  if (mapping->has_user_indirect(self()))
269  return true;
270  }
271  return false;
272 }
273 
275  const SecurityContext::DoorActionParam &dap) const
276 {
277  if (is_manager() || dap.door_id == 0)
278  return true;
279 
280  db::MultiplexedTransaction t(dbsrv_->db()->begin());
281  Auth::IDoorPtr door =
282  dbsrv_->find_door_by_id(dap.door_id, DBService::THROW_IF_NOT_FOUND);
283  for (const auto &mapping : door->lazy_mapping())
284  {
285  auto loaded_mapping = mapping.load();
286  if (loaded_mapping->has_user_indirect(self()))
287  return true;
288  }
289  return false;
290 }
291 
294 {
295  if (is_manager() || cap.credential_id == 0)
296  return true;
297 
298  auto cred = dbsrv_->find_credential_by_id(cap.credential_id,
300  return is_self(cred->owner_id());
301 }
302 
303 
305 {
306  auto user = dbsrv_->find_user_by_id(user_id_);
307  if (user)
308  return user->rank() == Auth::UserRank::ADMIN;
309  return false;
310 }
311 
313 {
314  auto user = dbsrv_->find_user_by_id(user_id_);
315  if (user)
316  return user->rank() >= Auth::UserRank::MANAGER;
317  return false;
318 }
319 
321 {
322  return user_id_ == id;
323 }
324 
326 {
327  return user_id_;
328 }
329 
331 {
332  return dbsrv_->find_user_by_id(user_id_, DBService::THROW_IF_NOT_FOUND);
333 }
334 
336  : UserSecurityContext(nullptr, 0)
337 {
338 }
339 
342 {
343  return false;
344 }
Acts like an odb::transaction, with the exception that it will becomes the active transaction at cons...
This is the header file for a generated source file, GitSHA1.cpp.
bool can_read_membership(const MembershipActionParam &map) const
Can access user management aswel as credential management.
Auth::UserPtr self() const
Load the User object that owns this security context.
bool can_update_user(const UserActionParam &uap) const
Read the audit log and access additional information, such as the JSON "before" and "after" field...
bool can_read_credential(const CredentialActionParam &cap) const
bool can_read_user(const UserActionParam &uap) const
Username, as well as a few basic (firstname, lastname) info are public.
An optional transaction is an object that behave like an odb::transaction if there is no currently ac...
std::shared_ptr< IDoor > IDoorPtr
Definition: AuthFwd.hpp:104
unsigned long UserId
Definition: AuthFwd.hpp:34
std::shared_ptr< Group > GroupPtr
Definition: AuthFwd.hpp:37
bool is_self(Auth::UserId id) const
Return true if the owner of the security context is the user whose id is id.
virtual bool check_permission(Action a, const ActionParam &ap) const
Check for the permission to perform action a with parameters ap.
A workaround permission that requires the user to be administrator.
bool can_administrate_group(const GroupActionParam &gap) const
UserSecurityContext(DBServicePtr dbsrv, Auth::UserId id)
std::shared_ptr< DBService > DBServicePtr
Definition: db_fwd.hpp:34
std::shared_ptr< User > UserPtr
Definition: AuthFwd.hpp:31
bool is_manager() const
Helper function that returns true if the user is at least manager.
GroupRank
The rank of an User inside a Group.
Definition: AuthFwd.hpp:49
bool is_admin() const
Helper function that returns true if the user is an administrator.
A SecurityContext is used to query permission while doing an operation.
Requires that the user be at least manager.
std::shared_ptr< UserGroupMembership > UserGroupMembershipPtr
Definition: AuthFwd.hpp:81
#define ASSERT_LOG(cond, msg)
Definition: log.hpp:221
std::shared_ptr< ISchedule > ISchedulePtr
Definition: ToolsFwd.hpp:37
bool can_create_membership(const MembershipActionParam &map) const
bool can_read_schedule(const ScheduleActionParam &sap) const
virtual bool check_permission_impl(Action a, const ActionParam &ap) const override
Reimplement this method to provide permission checking.
Perform to restart the Leosac server.
bool can_read_user_detail(const UserActionParam &uap) const
Can we enable/disable the user or change its validity period ?
bool can_delete_membership(const MembershipActionParam &map) const
Can we leave/kick someone from a group.
bool can_read_door(const DoorActionParam &dap) const
virtual bool check_permission_impl(Action a, const ActionParam &ap) const override
Reimplement this method to provide permission checking.
Overview of users/doors access permission.
A SecurityContext object for users.
Editing rank means being able to become administrator.
bool can_read_group(const GroupActionParam &gap) const