Wordpress - How to prevent access to wp-admin for certain user roles?

Plugin

It's basically just a user capability check, followed by a redirect in an exit call. It then redirects to the site the request came from.

<?php
! defined( 'ABSPATH' ) AND exit;
/* Plugin Name: (#66093) »kaiser« Deny Admin-UI access for certain roles */


function wpse66093_no_admin_access()
{
    // Do not run if the user is logged in and trying to log out
    // This might need one or two more checks.
    // Especially if you have custom login/logout/reset password/etc rules and routes set up.
    if ( 
        ! is_admin()
        || (
            is_user_logged_in()
            && isset( $GLOBALS['pagenow'] ) AND 'wp-login.php' === $GLOBALS['pagenow']
        )
    ) {
        return;
    }

    $redirect = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : home_url( '/' );
    if ( 
        current_user_can( 'CAPABILITY_NAME_HERE' )
        OR current_user_can( 'CAPABILITY_NAME_HERE' )
    )
        exit( wp_redirect( $redirect ) );
}
add_action( 'admin_init', 'wpse66093_no_admin_access', 100 );

Keep in mind that should only work with defaults (see comments in code). Custom login, logout, register, password reset logic might breaks this.

Roles vs. Capabilities: As role names can change and as roles are just groups of capabilities, it's best to check against a capability, not a role name. You can find a list of built in roles and capabilities here. Just look at what the most restrictive access is and search for a matching capability. Then assign it above. That's easier to maintain, in case a role name changes. Yes, you can use a role name as well, which will work in WordPress, but it's a concept that will bring along a hard to track down bug when a role name changes.

Note: Do not think about roles in a hierarchical manner. Think about the accountant whose email address you enter in some SaaS backend to receive the invoice. Most developers will not have access to billing details and neither will the accountant have access to deployment settings or security credentials. They have differently named roles with equally "high" capabilities, but for completely different parts. Keep this example in mind, when you write capability checks or add custom capabilities to a system.


The accepted answer mentions User Role but actually uses the function for User Capability

Here's the solution for User Roles

 function wpse66094_no_admin_access() {
    $redirect = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : home_url( '/' );
    global $current_user;
    $user_roles = $current_user->roles;
    $user_role = array_shift($user_roles);
    if($user_role === 'YOUR_USER_ROLE_HERE'){
        exit( wp_redirect( $redirect ) );
    }
 }

add_action( 'admin_init', 'wpse66094_no_admin_access', 100 );

Based on the answer provided by @kaiser (thank you btw), this is my working code, just in any case someone needs it. It is placed in functions.php file.

The condition used is, if the user can't manage_options or edit_posts.

function wpse66093_no_admin_access() {
    $redirect = home_url( '/' );
    if ( ! ( current_user_can( 'manage_options' ) || current_user_can( 'edit_posts' ) ) )
        exit( wp_redirect( $redirect ) );
}
add_action( 'admin_init', 'wpse66093_no_admin_access', 100 );