web2py – Controle de Acesso – Introdução

O web2py oferece aos seus usuários um eficiente e customizável sistema de controle de acesso baseado no modelo RBAC – Role Base Access Control, que permite restringir o acesso ao sistema à apenas usuários devidamente registrados e autorizados.

O modelo RBAC é uma alternativa ao modelo MAC (Mandatory Access Control) e DAC (Discretionary Access Control), ele trabalha com regras (roles) e permissões (permissions). As roles funcionando como grupos, aos quais iremos associar os usuários e as permissions serão utilizadas para determinar o que os grupos e seus usuários poderão realizar. Ou seja, as permissões não serão associadas diretamente aos usuários, mas serão transmitidas quando os usuários forem associados às roles (grupos ou regras).   No modelo RBAC, as permissões são associadas à determinadas operações, assim como em um modelo de Organização, já as ACLs utilizadas nos sistemas baseados em DAC controlam as permissões à nível dos objetos.

 No web2py a classe que implementa o modelo RBAC é a Auth; para ativar o funcionamento dos mecanismos de autenticação, o web2py precisa definir algumas tabelas (auth_tables), no banco de dados da aplicação, de forma que seja possível gerenciar todas as regras, permissões e usuários.

Auth Tables:

O funcionando do sistema de controle de acesso do web2py, precisa adicionar as seguintes tabelas o banco de dados da aplicação:

  • auth_user : esta tabela conterá os dados básicos dos usuários, tais como: nome, e-mail, senha e status; sendo que este último campo pode assumir os seguintes valores: registration pending (quando o usuário ainda não foi autorizado), accepted ( quando registro foi realizado com sucesso) e blocked (quando usuário está bloqueado e não pode acessar a aplicação). 
  • auth_group: armazena as informações sobre os gupos, ou as regras (roles), em uma estrutura de relacionamento de muitos para muitos (m-to-m); pois um usuário pode pertencer à vários grupos e um grupo pode estar associado à vários usuários. Os grupos poderão ser identificados tanto pelo seu ID, quanto pela sua role (o nome dado ao grupo).
  • auth_membership: esta tabela estabelece o vinculo dos usuários com os grupos, permitindo que a estrutura de relacionamento muitos para muitos seja normalizada.
  • auth_permission: relaciona os grupos (roles) com as permissões; que são identificadas por um nome, que podem ser o de uma tabela do banco; sendo que este campo pode assumir qualquer um dos valores válidos em web2py
  • auth_event: registra as alterações ocorridas nas demais tabelas, e os acessos realizados via CRUD.
  • auth_cas: utilizado pelo CASCentral Authentication Service, ou, simplesmente, serviço de autenticação centralizado.

Estas tabelas, listadas acima, e suas ligações podem ser representadas conforme a

Figura 1: Auth Tables

Na estrutura utilizada no RBAC do web2py, as únicas restrições para a definição dos nomes das regras (roles) e das permissões, são as mesmas válidas pra as demais definições do web2py.

Características do RBAC

Uma vez que um usuário tenha sido adicionado à estrutura do RBAC, o web2py provê uma API que permite determinar se ele está conectado (logged), se ele é membro de um determinado grupo e se este lhe fornece um dado privilégio (permissão). 

Além disto, o web2py possui uma funcionalidade denominada decorators, que pode ser utilizada para determinar quais usuários poderão ter acesso, ou utilizar, determinadas funções. Por exemplo, podemos definir que apenas os usuários “logados” poderão ter acesso à função; ou somente usuários associados à um determinado grupo; ou se o usuário possui uma determinada permissão. Esta funcionalidade evita que determinadas actions sejam utilizadas por quaisquer usuários. Os decorators devem ser inseridos imediatamente antes da definição da função a qual ele deve ser aplicado.

Além das permissões que podemos adicionar a estrutura do RBAC, o web2py vem configurado para reconhecer e utilizar algumas classes de permissão que estão diretamente ligadas aos métodos do CRUD; ou seja as permissões: create, read, update e delete.

Autenticação

Por meio do processo de autenticação, os usuários serão identificados na estrutura do RBAC do web2py. A classe Auth suporta vários métodos de autenticação, sendo que o método default consiste na autenticação local baseada na tabela auth_user; dentre os outros sistemas suportados destacam-se: Google, PAM, LDAP, Facebook, LinkedIn, Dropbox, OpenID e OAuth.

Para utilizarmos a classe Auth precisamos adicionar as linhas, listadas a seguir, em um dos arquivos de modelo (models), de preferência aquele utilizado para a definição do banco de dados e de suas tabelas.

from gluon.tools import Auth
auth=Auth(db)
auth.define_tables ( ) 

Dentro da pasta models da aplicação localize o arquivo db.py, que por padrão começa com linhas:

# -*- coding: utf-8 -*-

# -------------------------------------------------------------------------
# AppConfig configuration made easy. Look inside private/appconfig.ini
# Auth is for authenticaiton and access control
# -------------------------------------------------------------------------
from gluon.contrib.appconfig import AppConfig
from gluon.tools import Auth

Perceba que alinha destacada é aquela que realiza a importação do módulo Auth, que depois será utilizado para instanciar um objeto da classe com o mesmo nome. Descendo mais um pouco, nas linhas do arquivo, encontraremos:

# host names must be a list of allowed host names (glob syntax allowed)
auth = Auth(db, host_names=configuration.get('host.names'))

# -------------------------------------------------------------------------
# create all tables needed by auth, maybe add a list of extra fields
# -------------------------------------------------------------------------
auth.settings.extra_fields['auth_user'] = []
auth.define_tables(username=False, signature=False)

Na primeira linha destacada, estamos instanciando o objeto auth da classe Auth, neste exemplo, estamos utilizando um segundo argumento que estudaremos em outro momento. Na segunda linha destacada, temos o comando utilizado para criar/atualizar as tabelas utilizadas pelo RBAC.

A classe Auth, por padrão, utiliza o endereço de e-mail, dos usuários para autentica-los (username=False), mas podemos alterar este padrão, de forma que possamos utilizar o seu username, ou login name.

auth.define_tables (username=True)

Quando várias aplicações se autenticam, utilizando a mesma base de dados é importante desabilitar o processo de migração. O processo de migração consiste na atualização física do banco de dados, baseada nas definições dos arquivos de modelo, neste exemplo é o arquivo db.py.

auth.define_tables (username=True, migrate=False)

Para tornar os métodos de autenticação, da classe Auth, disponíveis, devemos adicionar algumas linhas de código em uma controle, localizada na subpasta  controllers.

def user ( ) :
    return dict (form=auth()) 

Neste exemplo utilizaremos o arquivo default.py, que já deve conter algo semelhante à:

# ---- Action for login/register/etc (required for auth) -----
def user():
    """
    exposes:
    http://..../[app]/default/user/login
    http://..../[app]/default/user/logout
    http://..../[app]/default/user/register
    http://..../[app]/default/user/profile
    http://..../[app]/default/user/retrieve_password
    http://..../[app]/default/user/change_password
    http://..../[app]/default/user/bulk_register
    use @auth.requires_login()
        @auth.requires_membership('group name')
        @auth.requires_permission('read','table name',record_id)
    to decorate functions that need access control
    also notice there is http://..../[app]/appadmin/manage/auth 
    to allow administrator to manage users
    """
    return dict(form=auth())

Podemos acessar esta função, utilizando uma view modelo denominada user.html, que geralmente é fornecida pelo web2py e que deve estar disponível no seguinte subpasta, da aplicação views/default. Dependendo o valor passado, via  request.args(0), ou seja via argumento,  para o formulário gerado pela função user, a controller pode expor várias actions, as quais podem ser acessadas por meio da URL:

http://[host]/[appname]/default/user/[argumento]

Segue a lista e a finalidade das actions que podem ser expostas pela função:

  • register (default/user/register): permite que novos usuários se registrem. O web2py, nos permite habilitar o uso do CAPTCHA no processo de registro, ele também fornece uma ferramenta que calcula a entropia da senha, que é definida no arquivo web2py.js, localizado na subpasta static/js. Para melhor a qualidade das senhas, podemos utilizar, um outro recurso fornecido pelo web2py, o validador IS_STRONG, que ajuda a prevenir que sejam aceitas senhas fracas. Esta action gera uma tela semelhante à Figura 2: Registro de Usuários
Figura 2: Web2py – Registro de Usuários

 

  • login (default/user/login): permite que os usuários registrados se conectem ao sistema.
Figura 3: web2py – Login
  • logout (default/user/logout): desconecta o usuário atual e retorna a página anteriormente carregada.
  • profile (default/user/profile): permite ao usuário “logado” editar o seu profile (perfil). A tabela com os dados de perfil do usuário não possui uma estrutura fixa, ou seja, ela pode ser customizada de acordo com a necessidade da aplicação. O processo necessário para customização da tabela auth_user será apresentado em outro momento.
Figura 4: web2py – Profile
  • change_password (default/user/change_password) : permite ao usuário alterar sua senha.
Figura 5: web2py – Change Password
  • verify_email (default/user/verify_email) : quando a verificação de e-mail está habilitada, sempre que um usuário se registra, ele recebe um e-mail de verificação, cujo conteúdo possui um link que direciona para esta action.
  • retrieve_username (default/user/retrieve_username) : quando utilizamos o login name, ou username, em vez do endereço de e-mail, no processo de autenticação, podemos usar este método para permitir ao usuário recuperar seu username, basta o usuário confirmar o endereço de fornecido durante o cadastro.
Figura 6: web2py – Retrieve Username
  • request_reset_password (default/user/retrieve_username) : permite aos usuários que esqueceram a sua senha, solicitar um nova. O usuário receberá um e-mail com um link que aponta para o action reset_password.
Figura 7: web2py – Request Reset Password
  • impersonate (default/user/impersonate): permite a um usuário assumir a “personalidade” de outro, mas somente se o usuário conectado possuir os privilégios necessários. Para determinar se o usuário possui os privilégios necessário podemos utilizar o comando:
has_permission (“impersonate”, db.auth_user, user_id)
Figura 8: web2py – Impersonate

 

  • groups (default/user/groups): apresenta a lista de grupos aos quais o usuário, atualmente conectado, está associado. O conteúdo gerado pela action dependerá das configurações de cada aplicação, na Figura 9 temos o conteúdo de uma tela de exemplo.
Figura 9: web2py – Groups

 

  • not_authorized (default/user/not_authorized): quando o usuário tenta realizar algo que ele não está autorizado, esta action é utilizada para apresenta uma mensagem de erro.
  • navbar (default/user/navbar): é uma função helper que gera uma barra (bar) com os links para as actions de login/register/etc.

Por padrão, todas as actions, listadas anteriormente, serão expostas quando utilizados o método apresentado; mas podemos restringir o acesso à apenas algumas delas. Neste caso, precisamos associar cada uma à uma função específica. Podemos, por exemplo, utilizar o código abaixo para expor apenas as actions de login, de registro e de perfil.

def logar ( ) :
    return dict ( form=auth.login ( ) )

def registar ( ) :
    return dict ( form=auth.register ( ) )

def perfil ( ) :
    return dict ( form=auth.profile ( ) )

Utilizando os decorators, podemos restringir o acesso as funções, como por exemplo, aos usuários registrados e logados (autenticados) :

@auth.requires_login ()
def perfil ( ) :
    return dict ( form=auth.profile ( ) )

auth.user  e auth.user_groups

O elemento auth.user contem uma cópia do registro db.auth_user, relacionado ao usuário atualmente conectado, ou None caso não tenha ocorrido o processo de autenticação. Ainda associado ao usuário logado, temos o auth.user_id, que contem o ID do usuário, que originalmente está armazenado no campo db.auth_user.id.

Além das informações básicas sobre o usuário conectado, temos o dicionário auth.user_groups, onde suas chaves (keys) são os IDs dos grupos nos quais o usuário está associado.

Restringindo o processo de registro

O web2py permite que qualquer usuário se registre no RBAC da aplicação, mas também nos permite impedir que o usuário se conecte até que um dos administradores aprove o seu cadastro; para isto, devemos ajustar o valor associado ao elemento auth.settings.registration_requires_approval, geralmente definido no arquivo db.py. Como podemos observar o valor padrão para esta configuração é False:

auth.settings.registration_requires_approval = False

Para ativar o procedimento de aprovação devemos alterar o valor o elemento para True

auth.settings.registration_requires_approval = True

O registro pode ser aprovado via appadmin ou via programação. Os registros pendentes possuem o campo registration_key  configurados com o valor pending, o registro será aprovado quando seu valor for alterado para vazio, ou seja, eu conteúdo for limpo. Para bloquear o acesso de um determinado usuário à aplicação, devemos alterar o valor, deste  campo, para disabled ou blocked

Ainda podemos impedir que um novo usuário possa acessar o formulário de registro, neste caso devemos adicionar a seguinte linha de configuração ao arquivo db.py.

auth.settings.actions_disabled.append('register')

Com isto finalizamos nossa breve introdução sobre o modelo RBAC implementado pelo web2py, nos próximos artigos abordaremos mais sobre um pouco sobre este modelo e algumas de suas funcionalidades.