Create Block Theme 플러그인 Manage Theme Fonts 작동 안하는 문제 수정하기

Create Block Theme 플러그인 메뉴

Twenty Twenty-Three 테마를 쓰다보면 같이 쓰면 유용한 플러그인이 있는데 그게 Create Block Theme 플러그인입니다.

근데 이 플러그인에서 폰트 관리 메뉴가 분명 초반엔 잘 작동하는걸 확인했는데 어느 순간부터 작동하지 않는걸 깨닫게 됬습니다.

Add local fonts to your theme 페이지

이 화면에서 “Upload local fonts to your theme” 버튼을 눌러도 아무 변화가 없다는것이지요.

처음엔 플러그인 버그인줄 알았는데 테스트 서버에선 정상 작동하길래 원인이 뭔가 이것저것 뒤적이다가 플러그인 소스분석(?) 삽질의 끝에 원인을 찾아냈습니다.

워드프레스 사용 초기에 여기저기서 봤던 워드프레스 보안 권고 사항에서 wp-config.php에

define('DISALLOW_FILE_EDIT', true);

이 항목을 넣으라고 하는것을 여러곳에서 봤었고 제가 대시보드에서 직접 소스 코드 수정할 일은 없기 때문에 위 항목을 넣고 썼었습니다. 근데 저 항목이 플러그인 코드에 걸려서 작동이 안된것 이였습니다.

단순히 저 항목만 보면 아무 상관이 없을거 같은데 왜 그런지는 아래에 적어봅니다.

function save_local_fonts_to_theme () {
    if (
        current_user_can( 'edit_themes' ) &&
        ! empty( $_POST['nonce'] ) &&
        wp_verify_nonce( $_POST['nonce'], 'create_block_theme' ) &&
        ! empty( $_FILES['font-file'] ) &&
        ! empty( $_POST['font-name'] ) &&
        ! empty( $_POST['font-style'] ) &&
        ! empty( $_POST['font-weight'] )
    ) {

create-block-themeadminclass-manage-fonts.php 파일을 열면 save_local_fonts_to_theme 함수가 있는데 여기 맨 처음 current_user_can( ‘edit_themes’ ) 함수에서 false가 나옵니다.

저는 관리자계정인데 (ID가 1이라 그런지 권한 변경 메뉴도 안뜸) edit_themes 권한이 없다는게 말이 안됩니다.

그래서 저 current_user_can 함수가 어떻게 굴러가는지 확인해봤습니다.

function current_user_can( $capability, ...$args ) {
    return user_can( wp_get_current_user(), $capability, ...$args );
}

wp_get_current_user 함수는 현재 유저 객체를 리턴하며 제 상황에선 아래 이미지와 같습니다.

워드프레스 유저 객체 정보

제 계정 권한에는 edit_themes가 분명히 있음에도 불구하고 current_user_can 함수는 false를 리턴합니다. 그래서 왜 그런지 current_user_can함수에서 사용하는 user_can 이라는 함수를 확인해 봤습니다.

function user_can( $user, $capability, ...$args ) {
    if ( ! is_object( $user ) ) {
        $user = get_userdata( $user );
    }

    if ( empty( $user ) ) {
        // User is logged out, create anonymous user object.
        $user = new WP_User( 0 );
        $user->init( new stdClass );
    }

    return $user->has_cap( $capability, ...$args );
}

user_can 함수도 별 내용은 없고 $user->has_cap 함수 결과에 의존합니다. 역시 내부를 확인합니다.

public function has_cap( $cap, ...$args ) {
    if ( is_numeric( $cap ) ) {
        _deprecated_argument( __FUNCTION__, '2.0.0', __( 'Usage of user levels is deprecated. Use capabilities instead.' ) );
        $cap = $this->translate_level_to_cap( $cap );
    }

    $caps = map_meta_cap( $cap, $this->ID, ...$args );

    // Multisite super admin has all caps by definition, Unless specifically denied.
    if ( is_multisite() && is_super_admin( $this->ID ) ) {
        if ( in_array( 'do_not_allow', $caps, true ) ) {
            return false;
        }
        return true;
    }

    // Maintain BC for the argument passed to the "user_has_cap" filter.
    $args = array_merge( array( $cap, $this->ID ), $args );

    /**
     * Dynamically filter a user's capabilities.
     *
     * @since 2.0.0
     * @since 3.7.0 Added the `$user` parameter.
     *
     * @param bool[]   $allcaps Array of key/value pairs where keys represent a capability name
     *                          and boolean values represent whether the user has that capability.
     * @param string[] $caps    Required primitive capabilities for the requested capability.
     * @param array    $args {
     *     Arguments that accompany the requested capability check.
     *
     *     @type string    $0 Requested capability.
     *     @type int       $1 Concerned user ID.
     *     @type mixed  ...$2 Optional second and further parameters, typically object ID.
     * }
     * @param WP_User  $user    The user object.
     */
    $capabilities = apply_filters( 'user_has_cap', $this->allcaps, $caps, $args, $this );

    // Everyone is allowed to exist.
    $capabilities['exist'] = true;

    // Nobody is allowed to do things they are not allowed to do.
    unset( $capabilities['do_not_allow'] );

    // Must have ALL requested caps.
    foreach ( (array) $caps as $cap ) {
        if ( empty( $capabilities[ $cap ] ) ) {
            return false;
        }
    }

    return true;
}

간단하게 유저 권한 목록만 비교해서 하면 될걸 굳이 이렇게 구현한것은 코어가 제공하는 유저 등급별 권한은 정해져 있지만 사이트 관리자나 개발자가 코어는 수정하지 않고 후킹으로 권한 제제를 걸기 좋으라고 만든게 아닐까 생각을 해보고 다시 코드로 넘어가자면…

map_meta_cap 함수를 통해 현재 제 계정으로는 ‘edit_themes’ 권한을 허용하지 않는걸로 나옵니다. 그 이유 또한 해당 함수 돌아가는 과정을 보면 알 수 있는데요.

function map_meta_cap( $cap, $user_id, ...$args ) {
    $caps = array();

    switch ( $cap ) {
        case 'edit_themes':
            // Disallow the file editors.
            if ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT ) {
                $caps[] = 'do_not_allow';
            } elseif ( ! wp_is_file_mod_allowed( 'capability_edit_themes' ) ) {
                $caps[] = 'do_not_allow';
            } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
                $caps[] = 'do_not_allow';
            } else {
                $caps[] = $cap;
            }
            break;
    }
}

하일라이트 한 부분을 보면 DISALLOW_FILE_EDIT이 정의되어 있으면 do_not_allow가 나와서 맨처음 current_user_can 함수가 false 처리된것입니다.

암튼 처음엔 이거 때문에 뭔가 디비 값이 꼬인건지 아님 서버가 꼬인건지 해서 서버 밀고 다시 깔까도 고민했었는데 해결되서 다행이군요.

번역기 돌려서 플러그인 개발자에게 다른 방식으로 처리하거나 저거에 대한 알림이 나오게 해달라고 요청을 해봐야 겠습니다.


2023년 2월 3일 추가

플러그인 개발자분이 올린글 보고 버그로 깃헙에 이슈 등록했습니다. 이제 패치되겠죠.


Comments

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다