|
7 | 7 | -mod_title("Ginger Base"). |
8 | 8 | -mod_description("Ginger Base"). |
9 | 9 | -mod_prio(250). |
10 | | --mod_depends([mod_content_groups, mod_acl_user_groups]). |
| 10 | +-mod_depends([mod_content_groups, mod_acl_user_groups, mod_admin_identity]). |
11 | 11 |
|
12 | 12 | -mod_schema(13). |
13 | 13 |
|
@@ -198,6 +198,32 @@ manage_schema(_Version, Context) -> |
198 | 198 | %%observe_acl_is_allowed(#acl_is_allowed{}, _Context) -> |
199 | 199 | %% undefined. |
200 | 200 |
|
| 201 | + |
| 202 | +%% @doc Allow mod_admin_identity managers to impersonate other users |
| 203 | +event(#postback{message={switch_user, [{id, Id}]}}, Context) -> |
| 204 | + IsAdmin = z_acl:is_admin(Context), |
| 205 | + CanSwitch = IsAdmin |
| 206 | + orelse z_acl:is_allowed(use, mod_admin_identity, Context), |
| 207 | + ImpersonationEnabled = is_impersonation_enabled(Context), |
| 208 | + AdminAllowed = IsAdmin |
| 209 | + andalso Id =/= 1, |
| 210 | + RegularAllowed = ImpersonationEnabled |
| 211 | + andalso CanSwitch |
| 212 | + andalso Id =/= 1 |
| 213 | + andalso can_impersonate_user(Id, Context), |
| 214 | + case AdminAllowed orelse RegularAllowed of |
| 215 | + true -> |
| 216 | + {ok, NewContext} = z_auth:switch_user(Id, Context), |
| 217 | + Url = case z_acl:is_allowed(use, mod_admin, NewContext) of |
| 218 | + true -> |
| 219 | + z_dispatcher:url_for(admin, NewContext); |
| 220 | + false -> |
| 221 | + <<"/">> |
| 222 | + end, |
| 223 | + z_render:wire({redirect, [{location, Url}]}, NewContext); |
| 224 | + false -> |
| 225 | + z_render:growl_error(?__("You are not allowed to switch users.", Context), Context) |
| 226 | + end; |
201 | 227 | %% @doc Handle the submit event of a new comment |
202 | 228 | event(#submit{message={newcomment, Args}, form=FormId}, Context) -> |
203 | 229 | ExtraActions = proplists:get_all_values(action, Args), |
@@ -267,6 +293,80 @@ event(#postback{message={map_infobox, _Args}}, Context) -> |
267 | 293 | ), |
268 | 294 | z_render:wire({script, [{script, JS}]}, Context). |
269 | 295 |
|
| 296 | +can_impersonate_user(TargetId, Context) -> |
| 297 | + case z_acl:user(Context) of |
| 298 | + undefined -> |
| 299 | + false; |
| 300 | + TargetId -> |
| 301 | + true; |
| 302 | + SwitcherId when is_integer(SwitcherId) -> |
| 303 | + AllowHorizontal = is_horizontal_impersonation_allowed(Context), |
| 304 | + SudoContext = z_acl:sudo(Context), |
| 305 | + SwitcherGroups = direct_user_groups(SwitcherId, SudoContext), |
| 306 | + TargetGroups = direct_user_groups(TargetId, SudoContext), |
| 307 | + case {SwitcherGroups, TargetGroups} of |
| 308 | + {[], _} -> |
| 309 | + false; |
| 310 | + {_, []} -> |
| 311 | + false; |
| 312 | + _ -> |
| 313 | + lists:all( |
| 314 | + fun(TargetGroup) -> |
| 315 | + group_impersonation_allowed(TargetGroup, SwitcherGroups, SudoContext, AllowHorizontal) |
| 316 | + end, |
| 317 | + TargetGroups) |
| 318 | + end; |
| 319 | + _ -> |
| 320 | + false |
| 321 | + end. |
| 322 | + |
| 323 | +direct_user_groups(UserId, Context) -> |
| 324 | + lists:usort(acl_user_groups_checks:has_user_groups(UserId, Context)). |
| 325 | + |
| 326 | +group_impersonation_allowed(TargetGroup, SwitcherGroups, Context, AllowHorizontal) -> |
| 327 | + TargetPath = user_group_path(TargetGroup, Context), |
| 328 | + TargetPath =/= [] andalso |
| 329 | + lists:any( |
| 330 | + fun(SwitcherGroup) -> |
| 331 | + SwitcherPath = user_group_path(SwitcherGroup, Context), |
| 332 | + path_allows(TargetPath, SwitcherPath, AllowHorizontal) |
| 333 | + end, |
| 334 | + SwitcherGroups). |
| 335 | + |
| 336 | +user_group_path(GroupId, Context) -> |
| 337 | + case mod_acl_user_groups:lookup(GroupId, Context) of |
| 338 | + undefined -> |
| 339 | + [GroupId]; |
| 340 | + Path when is_list(Path) -> |
| 341 | + Path |
| 342 | + end. |
| 343 | + |
| 344 | +path_allows(TargetPath, SwitcherPath, AllowHorizontal) when is_list(TargetPath), is_list(SwitcherPath) -> |
| 345 | + case lists:suffix(TargetPath, SwitcherPath) of |
| 346 | + true when AllowHorizontal -> |
| 347 | + true; |
| 348 | + true -> |
| 349 | + length(SwitcherPath) > length(TargetPath); |
| 350 | + false -> |
| 351 | + false |
| 352 | + end. |
| 353 | + |
| 354 | +is_impersonation_enabled(Context) -> |
| 355 | + case m_config:get_value(mod_ginger_base, activate_impersonation, Context) of |
| 356 | + undefined -> |
| 357 | + false; |
| 358 | + Value -> |
| 359 | + z_utils:is_true(Value) |
| 360 | + end. |
| 361 | + |
| 362 | +is_horizontal_impersonation_allowed(Context) -> |
| 363 | + case m_config:get_value(mod_ginger_base, allow_horizontal_impersonation, Context) of |
| 364 | + undefined -> |
| 365 | + false; |
| 366 | + Value -> |
| 367 | + z_utils:is_true(Value) |
| 368 | + end. |
| 369 | + |
270 | 370 | %% @doc When a resource is persisted in the admin, update granularity for |
271 | 371 | %% granular date fields. |
272 | 372 | observe_admin_rscform(#admin_rscform{}, Post, _Context) -> |
|
0 commit comments