밑에 lang.xml 파일에 불필요한 값을 정리하면 속도가 빨라진다는 이야기 떄문에 XE 사용팁 카테고리에 글 개진 해봅니다.

 

https://xe1.xpressengine.com/tip/22789771

https://xetown.com/lakepark/13459

https://xetown.com/lakepark/12794

 

예전에 NuriCMS를 한창 진행할때 제가 제일 처음 진행했던것이 module.xml 구조 정리였습니다. 당시에 기억으로는 XE에 비해 nuriCMS가 좀 더 빠른것 같다는 이야기를 하신분들이 계셨었는데 그냥 단순히 @misol 님의 가상서버의 성능과 튜닝 덕택이겠거니 하고 생각했던게 위 링크의 lang.xml 정리로 빨라진다는게 이용자들로 하여금 체감되었기 떄문에 지금에서 생각하면 module.xml 구조 정리가 성능에 일목했었던 것 같습니다.

 

아래 재정비를 위한 고증(?)과 결과를 링크와 링크 못볼 수도 있으니 여기다가도 남기겠습니다.

 

재정비 결과를 말씀드리면 files/cache/module_info안의 파일에 생성되는 용량은 대략 절반으로 줍니다.

 

기존 member.ko.1.8.6.php <- 28KB(498줄)

개선 member.ko.1.8.6.php <- 15KB(278줄)

 

cache에 생성되는 값들만 잘 추적해서 개선하면 성능효과 볼 수 있고, 또 중규모 이상의 커뮤니티를 생각하시는 분들은 예전에 제가 공홈에 작성했던 XE DB튜닝하기를 참고하시면 더욱 성능에 도움이 되실 겁니다.

 

module.xml 구조 재정비 의견 (Google docs)


module.xml 구조 재정비 의견

들어가기전에

https://github.com/NuriCms/core/issues/10

module.xml에 의미적으로 정의해야하는 값들의 재정비와 관리자권한 등 처리구조에 대해 리마인드 차원에서 정리해봅니다. 아래에 각 모듈별로 module.xml을 풀어놓고 재정비의 의견을 받기 위함이기도 합니다.

 

아래는 module.xml의 정보와 사용자권한이 어떻게 처리되는지 프로세스를 대략적으로 정리해봤습니다.

+index.php

   +Modulehandler.class.php

       procModule()

           …

           최초로 module.xml를 받아옴

           $xml_info = $oModuleModel->getModuleActionXml($this->module);

           ...

           처음으로 admin을 선정한다. (act에 admin이란 단어만 있으면 admin으로 인식)

           $kind = strpos(strtolower($this->act), 'admin') !== FALSE ? 'admin' : '';

           if(!$kind && $this->module == 'admin')

           {

               $kind = 'admin';

           }

           ...

           $oModule->setModuleInfo($this->module_info, $xml_info);

               +ModuleObject.class.php

                   setModuleInfo($module_info, $xml_info)

                       …

                       첫 권한 가져옴

                       $grant = $oModuleModel->getGrant($module_info, $logged_info, $xml_info);

                           +module.model.php

                               module_srl이 없을때는 로그인 대상자의 권한여부, 있을 경우는 로그인 대상자와 <grants>의 값을 비교

                               getGrant($module_info, $member_info, $xml_info = '')

                       …

                       권한자가 아닐 경우 현재 접근되고 있는 act의 필요권한을 조사

                       if(!$grant->manager)

                       {

                           <permissions>에 선언되어있는지 확인

                           $permission_target = $xml_info->permission->{$this->act};

                           permission이 선언되어있지 않고 act이름에 ‘Admin’이 포함되어있다면 manager로 지정함

                           if(!$permission_target && substr_count($this->act, 'Admin'))

                           {

                               $permission_target = 'manager';

                           }

                           // Check permissions

                           switch($permission_target)

                           {

                               case 'root' :

                               권한자가 아닌 접근자가 manager 권한을 읽을경우 로그인 화면으로 포워딩

                               case 'manager' :

                                   $this->stop('msg_is_not_administrator');

                                   return;

                               case 'member' :

                                   if(!$is_logged)

                                   {

                                       $this->stop('msg_not_permitted_act');

                                       return;

                                   }

                                   break;

                           }

                       }

                       ...

                       if(method_exists($this, 'init'))

              {

                          각 class의 init() 선언

                  $this->init();

              }


 

특별한 조건이 아니라면 *.admin.view.php, *.admin.controller.php에서 권한검사를 할필요가 없습니다.

 

위 ①, ②에 보여지는 것 처럼 관리자용 class는 모듈 수행전에 검사를 하고 있습니다. 그러니 특수한 용도가 아니라면 is_admin과 같은 검사 로직은 필요가 없습니다.


 

module.xml에서 권한을 정의할 수 있는 방법들

 

module.xml예를 작성했습니다.

<module>

   <grants>

       <grant name="list" default="guest">

           <title xml:lang="ko">목록</title>

       </grant>

   </grants>

   <permissions>

       <permission action="getApiGroups" target="manager" />

   </permissions>

   <actions>

   <action name="dispMemberInfo" type="view" standalone="true" />

   ...

   <action name="dispMemberAdminList" type="view" standalone="true" index="true" admin_index="true" menu_name="userList" menu_index="true"/>

   <action name="dispMemberAdminConfig" type="view" menu_name="userSetting" menu_index="true" />

   ...

   <action name="getMemberMenu" type="model" standalone="true" />

   <action name="getMemberAdminColorset" type="model" standalone="true" />

   <action name="getMemberAdminInsertJoinForm" type="model" />

   <action name="getMemberAdminIPCheck" type="model" standalone="true" />

   <action name="getApiGroups" type="model" standalone="true" />

   ...

   <action name="procMemberLogin" type="controller" standalone="true" ruleset="@login" />

   <action name="procMemberInsert" type="controller" standalone="true" ruleset="@insertMember" />

   <action name="procMemberModifyEmailAddress" type="controller" ruleset="modifyEmailAddress" />

   ...

   <action name="procMemberAdminInsert" type="controller" standalone="true" ruleset="insertAdminMember" />

   <action name="procMemberAdminInsertDefaultConfig" type="controller" ruleset="insertDefaultConfig" grant=”manager” />

   <action name="procMemberAdminInsertGroup" type="controller" standalone="true" ruleset="insertGroup" />

   <action name="procMemberAdminUpdateGroup" type="controller" standalone="true" ruleset="updateGroup" />

 

module.xml내에서는 <grants>, <permissions>, 그리고 <action>에도 grant라는 값으로 권한 검사에 판단할 수 있는 값을 처리하고 있습니다.

 

<action>에서도 grant라는 값을 정의할 수 있습니다.

+module.model.php

   function getModuleActionXml($module)

   {

   

   $type = $action->attrs->type;

   $grant = $action->attrs->grant?$action->attrs->grant:'guest';

   $standalone = $action->attrs->standalone=='true'?'true':'false';

   $ruleset = $action->attrs->ruleset?$action->attrs->ruleset:'';

   $method = $action->attrs->method?$action->attrs->method:'';

   $index = $action->attrs->index;

   $admin_index = $action->attrs->admin_index;

   $setup_index = $action->attrs->setup_index;

   $simple_setup_index = $action->attrs->simple_setup_index;

   $menu_index = $action->attrs->menu_index;

   ….


 

정리하자면 module.xml만 잘 활용한 설계를 하면 모듈내에서의 is_admin 사용을 줄일 수 있다는 겁니다.


 

필요없는 정보들

 

module.xml에서 <action>에 ①standalone, ①grant는 필요없는 정보입니다.

  • standalone은 그 사용역할이 XE개발자매뉴얼에도 deprecated 되었다고 명시하고 있습니다.

  • grant는 그 값이 어디에도 선언된게 없으며 어디서도 관련처리를 하는곳이 없습니다. 위 getModuleActionXml에서만 파싱을 하고 있으며, 값이 없으면 무조건 guest로 선언하도록 되어있습니다. 관리자 권한을 선언한 action이 grant에서는 guest로 선언한다는건 모순이며, 파싱에서 무조건적으로 선언하고 있기 때문에 여러가지로 자원낭비가 됩니다.

 

고로, 해당 기능은 전부 삭제해야합니다.


 

checkCSRF()를 init()에 선언

 

최근 CSRF 보안으로 긴급패치를 하였으나 로직상으로는 init()에서 파일전체를 보호하는게 맞습니다. ③을통해 모듈의 각 class에 init()가 있다면 수행하고 있기 때문에 이곳에서 class내에 함수들을 총괄 보호해야합니다. 단, 모든 곳에 checkCSRF()를 선언해버리면 관리자페이지 - 설정 - 고급의 default_url의 값이 공란일 경우 모든 접근이 checkCSRF()로 거부되어버리는 문제점이 있으니 install 모듈에 한해서는 설정을 하지말아야합니다.

 

이 문제점에 대해서 모순이 발생되고 있기 때문에 점진적으로는 코어가 멀티도메인을 입력받아 default_url의 공란 발생을 막아야합니다.


 

정리

 

module.xml과 관련된 사항은 아래와 같은 수정작업을 해야합니다.

 

  • action의 의미상 *.admin.controller.php, *.admin.model.php, *.admin.view.php는 <permissions>을 통해여 권한처리를 해야합니다.

  • 필요없는 standalone, grant 기능은 제거해야합니다.

  • checkCSRF()를 최소한 *.admin.controller.php, *.controller.php의 init()에 선언해야합니다.
    단, install 모듈은 procInstallAdminSaveTimeZone()이외의 것만 선언하든지, 선언하지 말아야 합니다.




 

추가정리

 

정리 내용을 진행하는 과정에서의 문제점을 재정리합니다.

 

  • permission은 root, manager, member, (or guest)로 정의할 수 있습니다.
    (ModuleObject.class.php setModuleInfo에 정의되어있습니다.)

  • root권한은 현재 전혀 활용이없기 때문에 이 기능을 manager의 기능(관리자검사)과 CSRF검사에 사용해야합니다.

  • manager는 종전의 기능대로 ‘admin’을 포함하고 있지 않은 일반 action을 관리자권한 검사하는데에 이용합니다.
    (그러므로 action명에 'admin'이 포함되면서 manager권한으로 permission이 지정된 action은 제거합니다.)

  • action명에 'admin'이 포함된 action과 root, manager권한으로 permission이 지정된 action의 함수에 불필요한 권한검사나 checkCSRF를 제거합니다.

 

결과정리

 

아래와 같이 수행되도록 개선했습니다.

 

function setModuleInfo($module_info, $xml_info)

{

   ...

   // get permission types(guest, member, manager, root) of the currently requested action

   $permission_target = $xml_info->permission->{$this->act};

   // check CSRF If a root permission in module.xml

   if($permission_target == 'root' && !checkCSRF())

   {

       return $this->stop('msg_invalid_request');

   }

 

   // checks permission and action if you don't have an admin privilege

   if(!$grant->manager)

   {

       // check manager if a permission in module.xml otherwise action if no permission

       if(!$permission_target && substr_count($this->act, 'Admin'))

       {

       $permission_target = 'manager';

       }

       // Check permissions

       switch($permission_target)

       {

           case 'root' :

               $this->stop('msg_is_not_administrator');

               return;

           case 'manager' :

               $this->stop('msg_is_not_administrator');

               return;

           case 'member' :

               if(!$is_logged)

               {

                   $this->stop('msg_not_permitted_act');

                   return;

               }

           break;

   }

}


 

Permission 정의

권한

내용

root

action을 수행하기전에 관리자 권한검사와 CSRF검사를 수행합니다.

manager

action을 수행하기전에 관리자 권한검사를 수행합니다.

member

action을 수행하기전에 로그인되어있는지 검사를 수행합니다.

guest

별도로 정의되지 않았습니다.(setModuleInfo()에 주석문 상에서의 설명뿐이 없음)

그외

action명에 'admin’이 대소문자 구분없이 포함되어있다면 manager권한으로 동작합니다.


manager권한의 쓰임새는 action명에 admin이 포함되지 않은 action이 관리자 검사수행을 필요로할때 쓰이게 됩니다.


 

TAG •
  • profile
    와우~
  • ?
    추천!! ㅎㅎ
    누리CMS 이야기 나올때 마다 죄송한 마음입니다..ㅎㅎㅠ
  • ? profile
    ㅋㅋㅋㅋㅋㅋㅋ
  • profile
    일반 사용자가 이걸 어떻게 적용할 수 있나요?
  • profile profile
    없습니다. ㅠㅠ

    그러고보니 위에 설명은 여러가지 적어놓은거라 팁으로 읽어보면 이걸 뭐 어찌하라는거지 모를 수가 있군요.

    나중에 코어에서 grant=guest 버리는거 따로 올릴게요 ㅠ
  • profile profile
    ㅎㅎ 제가 아직 프로그램 쪽은 잘 몰라서요 ㅠ;
  • profile
    모듈핸들러 상태가 그야말로 누더기이긴 하죠. 실제 사용되지도 않는 기능들이 덕지덕지 붙어있고... ㅠㅠ

    checkCSRF()는 기본URL을 설정하지 않았더라도 같은 도메인내의 요청은 허용하는 등의 방식으로 개선할 수 있을 것 같은데, 언제 또 생각나면 제가 PR 넣어 볼게요.