Implement admin scopes

This commit is contained in:
Kijin Sung 2024-10-14 23:40:58 +09:00
parent ec6ac82ebd
commit b17c58f17f
11 changed files with 163 additions and 12 deletions

View file

@ -97,6 +97,10 @@ $lang->about_mobile_page_count = 'You can set the number of page links to move p
$lang->about_admin_id = 'You can grant someone permission to manage this module. Please enter the user ID or email address of the person you wish to add.';
$lang->about_grant_deatil = 'Registered users mean users who signed-up to the virtual sites (e.g., cafeXE).';
$lang->about_module = 'Rhymix consists of modules except the basic library. [Module Manage] module will show all installed modules and help you to manage them.';
$lang->admin_scope = 'Scope of Admin Powers';
$lang->admin_scopes['moderate:document'] = 'Manage documents';
$lang->admin_scopes['moderate:comment'] = 'Manage comments';
$lang->admin_scopes['config:*'] = 'Change settings';
$lang->extra_vars_is_strict = 'Specified values only';
$lang->extra_vars_options = 'Options';
$lang->about_extra_vars_is_strict = 'In single and multiple choice fields, only allow the values specified below. If you change the allowed values, it may affect previous posts.';

View file

@ -96,6 +96,10 @@ $lang->about_mobile_page_count = '목록 하단, 페이지를 이동하는 링
$lang->about_admin_id = '특정 회원에게 이 모듈의 관리 권한을 부여할 수 있습니다. 권한을 부여할 회원의 아이디 또는 이메일 주소를 입력해 주세요.';
$lang->about_grant_deatil = '가입한 사용자는 cafeXE 등 분양형 가상 사이트에 가입을 한 로그인 사용자를 의미합니다.';
$lang->about_module = 'Rhymix는 기본 라이브러리를 제외한 나머지는 모두 모듈로 구성되어 있습니다. 모듈 관리 모듈은 설치된 모든 모듈을 보여주고 관리를 돕습니다.';
$lang->admin_scope = '관리자 권한 범위';
$lang->admin_scopes['moderate:document'] = '문서 관리';
$lang->admin_scopes['moderate:comment'] = '댓글 관리';
$lang->admin_scopes['config:*'] = '모듈 설정 변경';
$lang->extra_vars_is_strict = '임의입력 금지';
$lang->extra_vars_options = '선택지';
$lang->about_extra_vars_is_strict = '단일/다중 선택에서 미리 주어진 선택지만 입력할 수 있도록 합니다. 선택지를 변경할 경우 기존 게시물에 영향을 줄 수 있습니다.';

View file

@ -0,0 +1,64 @@
<?php
namespace Rhymix\Modules\Module\Models;
#[\AllowDynamicProperties]
class Permission
{
/**
* Default properties.
*
* Note that $is_admin is an alias to $root,
* and $is_site_admin is an alias to $manager.
*/
public $access;
public $root;
public $manager;
public $scopes;
/**
* Alias to $root, kept for backward compatibility only.
*
* @deprecated
*/
public $is_admin;
/**
* Alias to $manager, kept for backward compatibility only.
*
* @deprecated
*/
public $is_site_admin;
/**
* Primary method to determine whether a user is allowed to do something.
*
* @param string $scope
* @return bool
*/
public function can(string $scope): bool
{
if (isset($this->{$scope}) && $scope !== 'scopes')
{
return boolval($this->{$scope});
}
if ($this->manager && $this->scopes && preg_match('/^(\w+):(.+)$/', $scope, $matches))
{
if ($this->scopes === true)
{
return true;
}
if (is_array($this->scopes) && in_array($scope, $this->scopes))
{
return true;
}
if (is_array($this->scopes) && in_array($matches[1] . ':*', $this->scopes))
{
return true;
}
}
return false;
}
}

View file

@ -292,6 +292,11 @@ class ModuleAdminController extends Module
// Register Admin ID
$oModuleController->deleteAdminId($module_srl);
$admin_member = Context::get('admin_member');
$scopes = Context::get('admin_scopes') ?: null;
if(is_string($scopes) && $scopes !== '')
{
$scopes = explode('|@|', $scopes);
}
if($admin_member)
{
$admin_members = explode(',',$admin_member);
@ -299,7 +304,7 @@ class ModuleAdminController extends Module
{
$admin_id = trim($admin_id);
if(!$admin_id) continue;
$oModuleController->insertAdminId($module_srl, $admin_id);
$oModuleController->insertAdminId($module_srl, $admin_id, $scopes);
}
}

View file

@ -148,6 +148,12 @@ class Module extends ModuleObject
{
return true;
}
// check scope column on module_admins table
if (!$oDB->isColumnExists('module_admins', 'scopes'))
{
return true;
}
}
/**
@ -311,6 +317,12 @@ class Module extends ModuleObject
return $output;
}
}
// check scope column on module_admins table
if (!$oDB->isColumnExists('module_admins', 'scopes'))
{
$oDB->addColumn('module_admins', 'scopes', 'text', null, null, false, 'member_srl');
}
}
/**

View file

@ -806,7 +806,7 @@ class ModuleController extends Module
/**
* @brief Specify the admin ID to a module
*/
function insertAdminId($module_srl, $admin_id)
function insertAdminId($module_srl, $admin_id, $scopes = null)
{
if (strpos($admin_id, '@') !== false)
{
@ -824,6 +824,14 @@ class ModuleController extends Module
$args = new stdClass();
$args->module_srl = intval($module_srl);
$args->member_srl = $member_info->member_srl;
if (is_array($scopes))
{
$args->scopes = json_encode(array_values($scopes));
}
else
{
$args->scopes = new Rhymix\Framework\Parsers\DBQuery\NullValue;
}
$output = executeQuery('module.insertAdminId', $args);
Rhymix\Framework\Cache::delete("site_and_module:module_admins:" . intval($module_srl));

View file

@ -1853,7 +1853,9 @@ class ModuleModel extends Module
}
/**
* @brief Check if a member is a module administrator
* Check if a member is a module administrator
*
* @return array|bool
*/
public static function isModuleAdmin($member_info, $module_srl = null)
{
@ -1882,14 +1884,22 @@ class ModuleModel extends Module
$module_admins = array();
foreach ($output->data as $module_admin)
{
$module_admins[$module_admin->member_srl] = true;
$module_admins[$module_admin->member_srl] = $module_admin->scopes ? json_decode($module_admin->scopes) : true;
}
if ($output->toBool())
{
Rhymix\Framework\Cache::set("site_and_module:module_admins:$module_srl", $module_admins, 0, true);
}
}
return isset($module_admins[$member_info->member_srl]);
if (isset($module_admins[$member_info->member_srl]))
{
return $module_admins[$member_info->member_srl];
}
else
{
return false;
}
}
/**
@ -1900,8 +1910,14 @@ class ModuleModel extends Module
$obj = new stdClass();
$obj->module_srl = $module_srl;
$output = executeQueryArray('module.getAdminID', $obj);
if(!$output->toBool() || !$output->data) return;
if (!$output->toBool() || !$output->data)
{
return;
}
foreach ($output->data as $row)
{
$row->scopes = !empty($row->scopes) ? json_decode($row->scopes) : null;
}
return $output->data;
}
@ -2129,7 +2145,12 @@ class ModuleModel extends Module
}
/**
* @brief Return privileges(granted) information by using module info, xml info and member info
* Get privileges(granted) information by using module info, xml info and member info
*
* @param object $module_info
* @param object $member_info
* @param ?object $xml_info
* @return Rhymix\Modules\Module\Models\Permission
*/
public static function getGrant($module_info, $member_info, $xml_info = null)
{
@ -2148,8 +2169,6 @@ class ModuleModel extends Module
}
}
$grant = new stdClass;
// Get information of module.xml
if(!$xml_info)
{
@ -2172,6 +2191,7 @@ class ModuleModel extends Module
$privilege_list = array_unique($privilege_list, SORT_STRING);
// Grant first
$grant = new Rhymix\Modules\Module\Models\Permission;
foreach($privilege_list as $val)
{
// If an administrator, grant all
@ -2180,7 +2200,7 @@ class ModuleModel extends Module
$grant->{$val} = true;
}
// If a module manager, grant all (except 'root', 'is_admin')
else if($is_module_admin === true && $val !== 'root' && $val !== 'is_admin')
elseif ($is_module_admin && $val !== 'root' && $val !== 'is_admin')
{
$grant->{$val} = true;
}
@ -2196,6 +2216,20 @@ class ModuleModel extends Module
}
}
// If module admin, add scopes
if ($member_info && $member_info->is_admin == 'Y')
{
$grant->scopes = true;
}
elseif ($is_module_admin)
{
$grant->scopes = $is_module_admin;
}
else
{
$grant->scopes = [];
}
// If access were not granted, check more
if(!$grant->access)
{

View file

@ -2,6 +2,9 @@
<tables>
<table name="module_admins" />
</tables>
<columns>
<column name="*" />
</columns>
<conditions>
<condition operation="equal" column="module_srl" var="module_srl" filter="number" />
<condition operation="equal" column="member_srl" var="member_srl" pipe="and" />

View file

@ -5,6 +5,7 @@
<columns>
<column name="module_srl" var="module_srl" notnull="notnull" />
<column name="member_srl" var="member_srl" notnull="notnull" />
<column name="scopes" var="scopes" />
<column name="regdate" default="curdate()" />
</columns>
</query>

View file

@ -1,5 +1,6 @@
<table name="module_admins">
<column name="module_srl" type="number" size="11" notnull="notnull" unique="unique_module_admin" />
<column name="member_srl" type="number" size="11" notnull="notnull" unique="unique_module_admin" />
<column name="scopes" type="text" />
<column name="regdate" type="date" index="idx_regdate" />
</table>

View file

@ -8,7 +8,7 @@
<form action="./" method="post" onsubmit="return procFilter(this, insert_grant)" id="fo_obj">
<input type="hidden" name="module_srl" value="{$module_srl}" />
<input type="hidden" name="admin_member" value="<!--@foreach($admin_member as $key => $val)--><!--@if($member_config->identifier == 'email_address')-->{$val->email_address},<!--@else-->{$val->user_id},<!--@end--><!--@end-->" />
<div class="section x_form-horizontal">
<h1>{$lang->module_admin}</h1>
<div class="x_control-group">
@ -34,6 +34,21 @@
<p id="adminListHelp" class="x_help-block">{$lang->about_admin_id}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label">
{$lang->admin_scope}
</label>
<div class="x_controls">
{@ $default_scopes = array_keys($lang->admin_scopes->getArrayCopy())}
{@ $admin_scopes = $admin_member ? (array_first($admin_member)->scopes ?? $default_scopes) : $default_scopes}
<!--@foreach($lang->admin_scopes as $key => $val)-->
<label class="x_inline">
<input type="checkbox" name="admin_scopes[]" value="{$key}" checked="checked"|cond="in_array($key, $admin_scopes)" />
{$val}
</label>
<!--@endforeach-->
</div>
</div>
</div>
<div class="section">