Atualizado em 19/06/2026
Controle de acesso é um daqueles temas que parecem triviais até o dia em que um contributor consegue publicar sozinho, ou um cliente reclama que não enxerga um menu que deveria. O WordPress resolve isso com um modelo elegante de papéis (roles) e capabilities — mas ele só funciona como blindagem se você usá-lo do jeito certo. A regra de ouro, que vamos repetir até ficar gravada: no código, você verifica a capability, nunca o nome do papel.
O modelo: papéis são conjuntos de capabilities
A distinção central é esta:
- Capability é a permissão granular — a unidade real de autorização. Exemplos:
edit_posts,publish_posts,manage_options,delete_others_posts. - Papel (role) é apenas um rótulo que agrupa um conjunto de capabilities. "Editor" não é uma permissão; é um nome para uma coleção delas.
O WordPress vem com cinco papéis padrão, em ordem decrescente de poder:
| Papel | Para que serve (capabilities-chave) |
|---|---|
administrator | Controle total do site, incluindo manage_options, instalar plugins/temas e (em single site) edit_users. |
editor | Gerencia e publica o conteúdo de todos: edit_others_posts, publish_pages, manage_categories. |
author | Escreve e publica os próprios posts: publish_posts, edit_published_posts, upload_files. |
contributor | Escreve, mas não publica: edit_posts, sem publish_posts nem upload_files. |
subscriber | Apenas gerencia o próprio perfil: read. |
Em multisite há um nível acima. O super admin tem a capability manage_network e governa toda a rede de sites. Várias capabilities "perigosas" (como editar arquivos de plugins) também respeitam constantes como DISALLOW_FILE_EDIT e o contexto da instalação.
A regra de ouro: cheque a capability, não o papel
A função central é current_user_can(). Resista à tentação de escrever lógica baseada no nome do papel — papéis mudam, são renomeados, plugins adicionam os seus (o WooCommerce cria shop_manager e customer), e o cliente pode customizar tudo com um plugin de gerenciamento de papéis. Capabilities são estáveis e expressam exatamente a intenção.
<?php
// ERRADO: amarra a lógica a um nome de papel
$user = wp_get_current_user();
if ( in_array( 'editor', (array) $user->roles, true ) ) {
// ... frágil: quebra se o papel for renomeado ou customizado
}
// CERTO: verifica a permissão granular
if ( current_user_can( 'edit_others_posts' ) ) {
// ... robusto, independe do nome do papel
}
Esse é o ponto onde permissões se cruzam com a segurança no código: toda ação sensível — salvar dados, expor um endpoint, mostrar um botão administrativo — deve passar por um current_user_can().
current_user_can() com e sem objeto
A função tem duas formas. Sem ID, ela checa uma capability primitiva. Com um ID, ela checa uma capability de meta contra um objeto específico — e aí o WordPress decide a resposta levando em conta o autor do post, por exemplo.
<?php
// Capability primitiva (genérica): "pode editar posts em geral?"
if ( current_user_can( 'edit_posts' ) ) {
// mostra o link "Novo post"
}
// Capability de meta (sobre um objeto): "pode editar ESTE post?"
$post_id = 123;
if ( current_user_can( 'edit_post', $post_id ) ) {
// permite editar o post 123 especificamente
}
// Para um usuário arbitrário (não o atual), use user_can():
$user_id = 45;
if ( user_can( $user_id, 'publish_posts' ) ) {
// o usuário 45 pode publicar
}
Não confunda edit_post (singular) com edit_posts (plural). O singular só faz sentido com um ID e é uma meta capability; o plural é a capability primitiva que de fato existe nos papéis. Chamar current_user_can( 'edit_post' ) sem o ID não verifica o que você imagina.
Capabilities de meta e map_meta_cap
Aqui está a parte que confunde quase todo mundo. Capabilities como edit_post, delete_post e read_post são de meta: nenhum papel as possui diretamente. Quando você chama current_user_can( 'edit_post', $post_id ), o WordPress executa map_meta_cap(), que traduz essa meta capability em uma ou mais capabilities primitivas, conforme o contexto:
- Se o usuário é o autor do post,
edit_postmapeia paraedit_posts. - Se o post é de outra pessoa, mapeia para
edit_others_posts. - Se o post já está publicado, também exige
edit_published_posts. - Se o post é privado e de outro autor, entra
edit_private_posts.
É por isso que um author consegue editar o próprio rascunho mas não o post publicado de um colega: as capabilities primitivas que ele tem não cobrem todas as que o map_meta_cap() exige naquele caso. Esse mecanismo é o coração da lógica de permissões do WordPress — e a razão de você sempre passar o ID do objeto ao checar ações sobre um item específico.
Gerenciando papéis e capabilities via código
As funções de gerenciamento — add_role(), remove_role(), get_role() e os métodos $role->add_cap()/remove_cap() — têm uma característica crucial: elas persistem no banco de dados, na opção wp_user_roles. Logo, não são chamadas que você dispara em todo carregamento; são operações de setup, que rodam uma única vez.
O lugar certo é o hook de ativação do plugin:
<?php
// No arquivo principal do plugin.
register_activation_hook( __FILE__, 'meu_plugin_ativar' );
function meu_plugin_ativar() {
// Cria um papel novo UMA vez. Os caps são um array cap => bool.
add_role(
'gerente_loja',
'Gerente da Loja',
array(
'read' => true,
'edit_posts' => true,
'edit_published_posts' => true,
'publish_posts' => true,
'upload_files' => true,
// capability própria do plugin:
'gerenciar_relatorios' => true,
)
);
}
// Limpeza simétrica na desativação:
register_deactivation_hook( __FILE__, 'meu_plugin_desativar' );
function meu_plugin_desativar() {
remove_role( 'gerente_loja' );
}
Repare na capability gerenciar_relatorios: capabilities customizadas são apenas strings que você inventa e depois verifica com current_user_can( 'gerenciar_relatorios' ) nas telas do seu plugin. É assim que você cria áreas de acesso próprias sem depender das permissões genéricas do WordPress.
Para adicionar uma capability a um papel já existente, busque o papel com get_role() e use add_cap() — também na ativação:
<?php
function meu_plugin_ativar_caps() {
$editor = get_role( 'editor' ); // objeto WP_Role ou null
if ( $editor ) {
$editor->add_cap( 'gerenciar_relatorios' );
// remover seria: $editor->remove_cap( 'gerenciar_relatorios' );
}
}
register_activation_hook( __FILE__, 'meu_plugin_ativar_caps' );
Por que isso importa tanto. Como as alterações ficam gravadas, chamar add_cap() em todo request além de inútil dificulta a depuração: você pode remover a linha do código e a capability continuar lá, "fantasma", no banco. Em desenvolvimento, isso gera confusão — desconfie sempre do estado persistido e, ao mexer em papéis, reative o plugin (ou rode um comando de WP-CLI) para reaplicar do zero.
Capabilities customizadas para Custom Post Types
Por padrão, um Custom Post Type usa as mesmas capabilities de post — ou seja, quem pode editar posts pode editar o seu CPT. Quase sempre você quer controle separado. Para isso, defina capability_type e ative map_meta_cap no registro:
<?php
add_action( 'init', 'registrar_cpt_livro' );
function registrar_cpt_livro() {
register_post_type( 'livro', array(
'label' => 'Livros',
'public' => true,
'show_in_rest' => true,
// Gera capabilities no plural: edit_livros, publish_livros, etc.
'capability_type' => 'livro',
// Faz o WordPress mapear edit_post -> edit_livros, e assim por diante:
'map_meta_cap' => true,
'supports' => array( 'title', 'editor', 'author' ),
) );
}
Com capability_type => 'livro', o WordPress passa a exigir capabilities como edit_livros, edit_others_livros, publish_livros, delete_livros e read_private_livros. Como essas capabilities ainda não existem em papel nenhum, lembre-se de concedê-las (na ativação do plugin) aos papéis que devem administrar livros — caso contrário nem o administrador verá o CPT no menu, a não ser que você também passe 'capabilities' apontando para caps que ele já possui.
Dica de plural. O capability_type aceita um array array( 'livro', 'livros' ) quando o plural não é só o singular + "s". Sem isso, o WordPress simplesmente concatena "s", o que pode gerar capabilities estranhas para nomes em português.
Permissões na REST API e em formulários
Capabilities são a camada de autorização em dois lugares onde falhas são comuns. Na segurança do código, vale memorizar a divisão de papéis:
- REST API: toda rota registrada com
register_rest_route()precisa de umpermission_callbackque devolva o resultado de umcurrent_user_can(). Sem ele (ou com um__return_true), a rota fica aberta a qualquer um — uma das brechas mais frequentes em plugins. - Formulários do admin: a capability responde "este usuário pode?"; o nonce responde "esta requisição foi intencional?". As duas verificações são complementares e ambas obrigatórias — uma não substitui a outra.
Armadilhas comuns
| Sintoma | Causa provável | Correção |
|---|---|---|
| Capability "fantasma" não some ao remover a linha | add_cap() persistiu no banco | Rode na ativação; reverta na desativação e reative o plugin |
| Removi o papel, usuário ainda acessa | Caps atribuídas direto ao usuário ou herdadas | Ajuste as capabilities e reatribua o usuário a outro papel |
| CPT não aparece nem para o admin | capability_type sem conceder as caps | Conceda edit_livros etc. aos papéis na ativação |
current_user_can('edit_post') não confere o post | Faltou passar o $post_id | Use current_user_can( 'edit_post', $post_id ) |
| Rota REST acessível por qualquer um | permission_callback ausente ou __return_true | Cheque current_user_can() no callback |
Boas práticas
- Cheque capability, nunca o papel. Repetimos de propósito: é o erro estrutural mais comum em controle de acesso no WordPress.
- Menor privilégio. Conceda só o que a tarefa exige. Não dê
manage_optionspara quem só precisa publicar um CPT. - Setup na ativação, limpeza na desativação. Trate papéis e caps como migração de banco — operações idempotentes que rodam uma vez.
- Passe o objeto ao checar ações sobre itens.
edit_post/delete_postsem ID raramente dizem o que você quer. - Nomeie capabilities customizadas com prefixo.
meuplugin_gerenciar_xevita colisão com o core e com outros plugins. - Documente o mapa de permissões. Quem pode o quê é decisão de produto; deixe explícito no README do plugin.
Permissões bem-feitas são invisíveis quando funcionam e desastrosas quando falham. Combine este modelo com as práticas de segurança no código e com CPTs bem registrados, e explore mais temas no guia de desenvolvimento. Se a capability passar, deixe agir; se não passar, negue de forma clara — sem nunca confiar no nome do papel.
Perguntas frequentes
Devo checar o papel (role) ou a capability no código?
Sempre a capability. Use current_user_can( 'edit_posts' ), nunca algo como "se o usuário é editor". Papéis são apenas nomes para conjuntos de capabilities e podem ser alterados por plugins (o WooCommerce adiciona shop_manager, por exemplo) ou customizados pelo cliente. Checar a permissão granular mantém o código correto mesmo quando os papéis mudam, e segue o princípio do menor privilégio.
Por que não devo chamar add_role() ou add_cap() a cada carregamento?
Porque essas funções gravam no banco de dados (na opção wp_user_roles). Chamá-las em todo request é desperdício e pode mascarar bugs: a alteração "gruda" mesmo depois de você remover a chamada. O padrão correto é rodar add_role()/add_cap() uma única vez, no hook de ativação do plugin (register_activation_hook), e reverter na desativação.
Removi um papel, mas o usuário ainda tem a permissão. Por quê?
Remover um papel com remove_role() não toca nos usuários que já estavam nele nem em capabilities atribuídas individualmente. Capabilities também podem ser concedidas diretamente a um usuário, fora de qualquer papel. Por isso, para revogar acesso de forma confiável, ajuste as capabilities relevantes e considere reatribuir os usuários a outro papel.
Qual a diferença entre edit_posts e edit_post?
edit_posts (plural) é uma capability primitiva, que fica gravada nos papéis. edit_post (singular) é uma capability de meta: ela não é atribuída diretamente, e sim traduzida pelo map_meta_cap() em capabilities primitivas conforme o contexto — por exemplo, em edit_posts ou edit_others_posts, dependendo de quem é o autor do post. Por isso você sempre passa o ID: current_user_can( 'edit_post', $post_id ).
Como dar permissões próprias a um Custom Post Type?
No register_post_type(), defina capability_type (ex.: 'livro') e map_meta_cap => true. O WordPress passa a esperar capabilities como edit_livros, publish_livros, edit_others_livros etc. Você então concede essas capabilities aos papéis desejados (geralmente na ativação do plugin). Sem isso, o CPT herda as capabilities de post e qualquer autor pode mexer nele.
Capabilities têm a ver com a segurança da REST API e de formulários?
Diretamente. Toda rota da REST API deveria ter um permission_callback que chama current_user_can(); sem ele, a rota fica pública. E em formulários do admin a capability anda de mãos dadas com o nonce: o nonce confirma a intenção da requisição, a capability confirma a autorização do usuário. Faltando qualquer um dos dois, há brecha. Veja o artigo de segurança no código para o detalhe.