Chapter 9: Controle de Acesso
Controle de acesso
O web2py inclui um mecanismo RBAC (Role Based Access Control) poderoso e personalizável.
Aqui está uma definição da Wikipedia:
"RBAC (Role-Based Access Control) é uma abordagem para restringir o acesso do sistema a usuários autorizados. É uma abordagem alternativa mais nova ao controle de acesso obrigatório (MAC) e ao controle de acesso discricionário (DAC). Às vezes, o RBAC é chamado de segurança.
O RBAC é uma tecnologia de controle de acesso flexível e neutra em termos de políticas, suficientemente poderosa para simular o DAC e o MAC. Por outro lado, o MAC pode simular o RBAC se o gráfico de funções estiver restrito a uma árvore e não a um conjunto parcialmente ordenado.
Antes do desenvolvimento do RBAC, o MAC e o DAC eram considerados os únicos modelos conhecidos para controle de acesso: se um modelo não era MAC, era considerado um modelo DAC e vice-versa. Pesquisas no final da década de 1990 demonstraram que o RBAC não se enquadra em nenhuma das categorias.
Dentro de uma organização, as funções são criadas para várias funções de trabalho. As permissões para executar determinadas operações são atribuídas a funções específicas. Os membros da equipe (ou outros usuários do sistema) são atribuídos a funções específicas e, por meio dessas atribuições de função, adquirem as permissões para executar determinadas funções do sistema. Ao contrário do controle de acesso baseado em contexto (CBAC), o RBAC não examina o contexto da mensagem (como a fonte de uma conexão).
Como os usuários não recebem permissões diretamente, mas apenas os adquirem por meio de sua função (ou funções), o gerenciamento de direitos individuais do usuário se torna uma questão de simplesmente atribuir funções apropriadas ao usuário; Isso simplifica operações comuns, como adicionar um usuário ou alterar o departamento de um usuário.
O RBAC difere das listas de controle de acesso (ACLs) usadas nos sistemas tradicionais de controle de acesso discricionário, pois atribui permissões a operações específicas com significado na organização, e não a objetos de dados de baixo nível. Por exemplo, uma lista de controle de acesso pode ser usada para conceder ou negar acesso de gravação a um arquivo de sistema específico, mas não ditaria como esse arquivo poderia ser alterado. "
A classe web2py que implementa o RBAC é chamada Auth.
Auth precisa (e define) as seguintes tabelas:
auth_user
armazena o nome do usuário, endereço de e-mail, senha e status (registro pendente, aceito, bloqueado)auth_group
armazena grupos ou funções para usuários em uma estrutura muitos para muitos. Por padrão, cada usuário está em seu próprio grupo, mas um usuário pode estar em vários grupos e cada grupo pode conter vários usuários. Um grupo é identificado por uma função e uma descrição.auth_membership
vincula usuários e grupos em uma estrutura de muitos para muitos.auth_permission
vincula grupos e permissões. Uma permissão é identificada por um nome e, opcionalmente, por uma tabela e um registro. Por exemplo, membros de um determinado grupo podem ter permissões de "atualização" em um registro específico de uma tabela específica.auth_event
registra mudanças nas outras tabelas e acesso bem-sucedido via CRUD a objetos controlados pelo RBAC.auth_cas
é usado para o Serviço de Autenticação Central (CAS). Cada aplicativo web2py é um provedor CAS e pode, opcionalmente, ser um consumidor CAS.
O esquema é reproduzido graficamente na imagem abaixo:
Em princípio, não há restrição quanto aos nomes das funções e aos nomes das permissões; o desenvolvedor pode criá-los para corrigir as funções e permissões na organização. Depois de criados, o web2py fornece uma API para verificar se um usuário está logado, se um usuário é membro de um determinado grupo e/ou se o usuário é membro de qualquer grupo que tenha uma determinada permissão obrigatória.
O web2py também fornece decorators para restringir o acesso a qualquer função com base em login, associação e permissões.
O web2py também entende algumas permissões específicas, ou seja, aquelas que têm um nome que corresponde aos métodos CRUD (criar, ler, atualizar, excluir) e podem aplicá-las automaticamente sem a necessidade de usar decorators.
Neste capítulo, vamos discutir diferentes partes do RBAC, uma por uma.
Autenticação
Para usar o RBAC, os usuários precisam ser identificados. Isso significa que eles precisam se registrar (ou estar registrados) e fazer o login.
Auth fornece vários métodos de login. O padrão consiste em identificar usuários com base na tabela local auth_user
. Como alternativa, ele pode fazer login de usuários contra sistemas de autenticação de terceiros e provedores de login único, como Google, PAM, LDAP, Facebook, LinkedIn, Dropbox, OpenID, OAuth, etc.
Para começar a usar Auth
, você precisa de pelo menos este código em um arquivo de modelo, que também é fornecido com o aplicativo web2py "welcome" e assume um db
objeto de conexão:
from gluon.tools import Auth
auth = Auth(db)
auth.define_tables(username=False, signature=False)
Por padrão, o web2py usa e-mail para login. Se, em vez disso, você quiser fazer login usando o nome de usuário definido auth.define_tables(username=True)
Configuração signature=True
Adiciona carimbo de data e usuário e data para tabelas de autenticação, para rastrear modificações.
A autenticação tem um opcional secure=True
argumento, que forçará as páginas autenticadas a passarem pelo HTTPS.
Por padrão, o Auth protege logins contra falsificações de solicitação entre sites (CSRF). Isso é realmente fornecido pela proteção CSRF padrão do web2py sempre que os formulários são gerados em uma sessão. No entanto, em algumas circunstâncias, a sobrecarga de criar uma sessão para login, solicitação de senha e tentativas de redefinição pode ser indesejável. Ataques DOS são teoricamente possíveis. A proteção contra CSRF pode ser desativada para formulários Auth (a partir da versão 2.6):
Auth = Auth(..., csrf_prevention = False)
o
password
campo dodb.auth_user
tabela padrão para umCRYPT
validador, que precisa de umhmac_key
. Em aplicativos Web2py herdados, você pode ver um argumento extra passado para o construtor Auth:hmac_key = Auth.get_or_create_key()
. Esta última é uma função que lê a chave HMAC de um arquivo "private/auth.key" dentro da pasta do aplicativo. Se o arquivo não existir, ele cria umhmac_key
. Se vários aplicativos compartilharem o mesmo banco de dados de autenticação, verifique se eles também usam o mesmohmac_key
. Isso não é mais necessário para novos aplicativos, pois as senhas são salgadas com um sal aleatório individual.
Se vários aplicativos compartilharem o mesmo banco de dados de autenticação, desative as migrações: auth.define_tables(migrate=False)
.
Para expor Auth, você também precisa da seguinte função em um controlador (por exemplo, em "default.py"):
def user(): return dict(form=auth())
O objeto
auth
e ouser
ação já estão definidas no aplicação scaffolding.
O web2py também inclui uma visualização de amostra "welcome/views/default/user.html" para renderizar adequadamente essa função que se parece com isso:
{{extend 'layout.html'}}
<h2>{{=T( request.args(0).replace('_', ' ').capitalize() )}}</h2>
<div id="web2py_user_form">
{{=form}}
{{if request.args(0)=='login':}}
{{if not 'register' in auth.settings.actions_disabled:}}
<br/><a href="{{=URL(args='register')}}">register</a>
{{pass}}
{{if not 'request_reset_password' in auth.settings.actions_disabled:}}
<br/>
<a href="{{=URL(args='request_reset_password')}}">lost password</a>
{{pass}}
{{pass}}
</div>
Observe que esta função simplesmente exibe form
e, portanto, pode ser personalizado usando a sintaxe normal do formulário personalizado. A única ressalva é que a forma exibida por form=auth()
depende de request.args(0)
; portanto, se você substituir o padrão auth()
formulário de login com um formulário de login personalizado, você pode precisar if
declaração como esta na visão:
{{if request.args(0)=='login':}}...custom login form...{{pass}}
O controlador acima expõe várias ações:
http://.../[app]/default/user/register
http://.../[app]/default/user/login
http://.../[app]/default/user/logout
http://.../[app]/default/user/profile
http://.../[app]/default/user/change_password
http://.../[app]/default/user/verify_email
http://.../[app]/default/user/retrieve_username
http://.../[app]/default/user/request_reset_password
http://.../[app]/default/user/reset_password
http://.../[app]/default/user/impersonate
http://.../[app]/default/user/groups
http://.../[app]/default/user/not_authorized
- registo permite que os usuários se registrem. Está integrado ao CAPTCHA, embora esteja desativado por padrão. Isso também é integrado a uma calculadora de entropia do lado do cliente definida em "web2py.js". A calculadora indica a força da nova senha. Você pode usar o
IS_STRONG
validador para evitar que o web2py aceite senhas fracas. - entrar permite que os usuários registrados se conectem (se o registro for verificado ou não exigir verificação, se tiver sido aprovado ou não exigir aprovação e se não tiver sido bloqueado).
- sair faz o que você esperaria, mas também, como os outros métodos, registra o evento e pode ser usado para acionar algum evento.
- perfil permite que os usuários editem seu perfil, ou seja, o conteúdo da tabela
auth_user
. Observe que esta tabela não possui uma estrutura fixa e pode ser personalizada. - mudar senha permite que os usuários alterem suas senhas de maneira segura.
- verificar email. Se a verificação de e-mail estiver ativada, os visitantes, após o registro, receberão um e-mail com um link para verificar as informações de e-mail. O link aponta para esta ação.
- retrieve_username. Por padrão, Auth usa e-mail e senha para login, mas pode, opcionalmente, usar nome de usuário em vez de e-mail. Neste último caso, se um usuário esquecer seu nome de usuário, o
retrieve_username
O método permite que o usuário digite o endereço de e-mail e recupere o nome de usuário por e-mail. - request_reset_password. Permite que usuários que esqueceram sua senha solicitem uma nova senha. Eles receberão um email de confirmação apontando para reset_password.
- personificar permite que um usuário "represente" outro usuário. Isso é importante para depuração e para fins de suporte.
request.args[0]
é o id do usuário para ser representado. Isso só é permitido se o usuário logadohas_permission('impersonate', db.auth_user, user_id)
. Você pode usarauth.is_impersonating()
para verificar se o usuário atual está se passando por outra pessoa. - grupos lista os grupos dos quais o usuário logado atual é um membro.
- não autorizado exibe uma mensagem de erro quando o visitante tentou fazer algo que ele não está autorizado a fazer
- navbar é um ajudante que gera uma barra com login/register/etc. links.
Logout, profile, change_password, impersonate e groups requerem login.
Por padrão, todos eles são expostos, mas é possível restringir o acesso a apenas algumas dessas ações.
Todos os métodos acima podem ser estendidos ou substituídos por subclassificação Auth.
Todos os métodos acima podem ser usados em ações separadas. Por exemplo:
def mylogin(): return dict(form=auth.login())
def myregister(): return dict(form=auth.register())
def myprofile(): return dict(form=auth.profile())
...
Para restringir o acesso a funções apenas a visitantes conectados, decore a função como no exemplo a seguir
@auth.requires_login()
def hello():
return dict(message='hello %(first_name)s' % auth.user)
Qualquer função pode ser decorada, não apenas ações expostas. Claro que isso ainda é apenas um exemplo muito simples de controle de acesso. Exemplos mais complexos serão discutidos posteriormente.
auth.user_groups
.
auth.user
contém uma cópia dodb.auth_user
registros para o usuário logado ouNone
de outra forma. Há também umauth.user_id
que é o mesmo queauth.user.id
(isto é, o id do registrador atual no usuário) ouNone
. Similarmente,auth.user_groups
contém um dicionário onde cada chave é o id de um grupo de com o atual usuário logado é membro de, o valor é a função de grupo correspondente.
o auth.requires_login()
decorador, bem como o outro auth.requires_*
decorators tomar um opcional otherwise
argumento. Pode ser definido para uma string onde redirecionar o usuário, se os arquivos de registro ou para um objeto que pode ser chamado. É chamado se o registro falhar.
Restrições ao registro
Se você quiser permitir que os visitantes se registrem, mas não façam login até que o registro seja aprovado pelo administrador:
auth.settings.registration_requires_approval = True
Você pode aprovar um registro por meio da interface appadmin. Olhe para a tabela auth_user
. Registros pendentes têm um registration_key
campo definido como "pendente". Um registro é aprovado quando este campo está em branco.
Através da interface appadmin, você também pode bloquear o login do usuário. Localize o usuário na tabela auth_user
e definir o registration_key
para "bloqueado". Usuários "bloqueados" não têm permissão para efetuar login. Observe que isso impedirá que um visitante faça o login, mas não forçará um visitante que já esteja logado a efetuar logout. A palavra "disabled" pode ser usada em vez de "bloqueada" se preferir, com exatamente o mesmo comportamento.
Você também pode bloquear o acesso à página "registrar" completamente com esta declaração:
auth.settings.actions_disabled.append('register')
Se você quiser permitir que as pessoas se registrem e as registrem automaticamente após o registro, mas ainda assim desejarem enviar um email para verificação para que não possam fazer login novamente após o logout, a menos que tenham concluído as instruções no email, você poderá realizá-lo da seguinte maneira:
auth.settings.registration_requires_verification = True
auth.settings.login_after_registration = True
Outros métodos de Auth pode ser restringido da mesma maneira.
Integração com OpenID, Facebook, etc. Janrain OpenID Facebook LinkedIn Google MySpace Flickr
Você pode usar o Controle de Acesso à Base de Função web2py e autenticar com outros serviços como OpenID, Facebook, LinkedIn, Google, Dropbox, MySpace, Flickr, etc. A maneira mais fácil é usar o Janrain Engage (antigo RPX) (Janrain.com).
O Dropbox é discutido como um caso especial no Capítulo 14, uma vez que permite mais do que apenas o login, ele também fornece serviços de armazenamento para os usuários logados.
O Janrain Engage é um serviço que fornece autenticação de middleware. Você pode se registrar no Janrain.com, registrar um domínio (o nome do seu aplicativo) e um conjunto de URLs que você usará, e eles lhe fornecerão uma chave de API.
Agora edite o modelo do seu aplicativo web2py e coloque as seguintes linhas em algum lugar após a definição do auth
objeto:
from gluon.contrib.login_methods.rpx_account import RPXAccount
auth.settings.actions_disabled=['register', 'change_password', 'request_reset_password']
auth.settings.login_form = RPXAccount(request,
api_key='...',
domain='...',
url = "http://your-external-address/%s/default/user/login" % request.application)
A primeira linha importa o novo método de login, a segunda linha desabilita o registro local e a terceira linha solicita que o web2py use o método de login do RPX. Você deve inserir seu próprio api_key
fornecida pelo Janrain.com, o domínio que você escolhe no registro e o domínio externo url
da sua página de login. Para obter o login, em janrain.com, vá para [Deployment] [Application Settings]. No lado direito há o "Application Info", o api_key é chamado de "API Key (Secret)".
O domínio é o "Domínio da Aplicação" sem liderar "https: //" e sem o título ".rpxnow.com /" Por exemplo: se você registrou um site como "secure.mywebsite.org", Janrain o transforma no domínio de aplicativo "https://secure-mywebsite.rpxnow.com".
Quando um novo usuário faz o login pela primeira vez, o web2py cria um novo db.auth_user
registro associado ao usuário. Ele usará o registration_id
campo para armazenar um ID exclusivo para o usuário. A maioria dos métodos de autenticação também fornecerá um nome de usuário, email, first_name e last_name, mas isso não é garantido. Quais campos são fornecidos depende do método de login selecionado pelo usuário. Se o mesmo usuário fizer login duas vezes usando mecanismos de autenticação diferentes (por exemplo, uma vez com o OpenID e uma vez com o Facebook), o Janrain pode não reconhecer o mesmo usuário e emitir diferentes registration_id
.
Você pode personalizar o mapeamento entre os dados fornecidos pelo Janrain e os dados armazenados em db.auth_user
. Aqui está um exemplo para o Facebook:
auth.settings.login_form.mappings.Facebook = lambda profile: dict(registration_id = profile["identifier"],
username = profile["preferredUsername"],
email = profile["email"],
first_name = profile["name"]["givenName"],
last_name = profile["name"]["familyName"])
As chaves no dicionário são campos em db.auth_user
e os valores são entradas de dados no objeto de perfil fornecido por Janrain. Veja a documentação on-line do Janrain para obter detalhes sobre o último.
O Janrain também manterá estatísticas sobre o login de seus usuários.
Este formulário de login é totalmente integrado ao controle de acesso baseado na função web2py e você ainda pode criar grupos, tornar usuários membros de grupos, atribuir permissões, bloquear usuários, etc.
O serviço básico gratuito do Janrain permite que até 2500 usuários registrados exclusivos façam login anualmente. Acomodar mais usuários exige um upgrade para uma das camadas de serviço pagas.
Se você preferir não usar o Janrain e quiser usar um método de login diferente (LDAP, PAM, Google, OpenID, OAuth/Facebook, LinkedIn, etc.), poderá fazê-lo. A API para fazer isso é descrita mais adiante no capítulo.
CAPTCHA e reCAPTCHA
Isto é o que você precisa fazer para usar o reCAPTCHA:
- Registre-se com reCAPTCHA [recaptcha] V2 e obter um par (PUBLIC_KEY, PRIVATE_KEY) para sua conta. Estas são apenas duas cordas.
- Anexe o seguinte código ao seu modelo após o
auth
objeto é definido:from gluon.tools import Recaptcha2 auth.settings.captcha = Recaptcha2(request, 'PUBLIC_KEY', 'PRIVATE_KEY')
O reCAPTCHA pode não funcionar se você acessar o site como 'localhost' ou '127.0.0.1', porque ele está registrado para funcionar somente com sites visíveis publicamente.
o Recaptcha
construtor leva alguns argumentos opcionais:
Recaptcha(..., error_message='invalid', label='Verify:', options='')
Existe um argumento experimental, ajax=True
, que usa a API ajax para recaptcha. Ele pode ser usado com qualquer recaptcha, mas foi especificamente adicionado para permitir que campos de recatcha funcionem em formulários LOAD (veja Capítulo 12 para mais informações sobre LOAD, que permite web2py para componentes 'plugin' de uma página com ajax). É experimental porque pode ser substituído pela detecção automática de quando o ajax é necessário.
options
pode ser uma cadeia de configuração, e. options="theme:'white', lang:'fr'"
Mais detalhes: reCAPTCHA [recaptchagoogle] e customizing .
Se você não quiser usar o reCAPTCHA, examine a definição do Recaptcha2
class em "gluon/tools.py", já que é fácil usar outros sistemas CAPTCHA.
Notar que Recaptcha
é apenas um ajudante que se estende DIV
. Ele gera um campo fictício que valida usando o reCaptcha
serviço e, portanto, pode ser usado em qualquer forma, incluindo FORMs definidos utilizados:
form = FORM(INPUT(...), Recaptcha(...), INPUT(_type='submit'))
Você pode usá-lo em todos os tipos de SQLFORM por injeção:
form = SQLFORM(...) or SQLFORM.factory(...)
form.element('table').insert(-1, TR('', Recaptcha(...), ''))
Personalizando Auth
A chamada para
auth.define_tables()
define as tabelas Auth que ainda não foram definidas. Isso significa que, se você quiser, você pode definir o seu própria tabelaauth_user
.
Existem várias maneiras de personalizar a autenticação. A maneira mais simples é adicionar campos extras:
## after auth = Auth(db)
auth.settings.extra_fields['auth_user']= [
Field('address'),
Field('city'),
Field('zip'),
Field('phone')]
## before auth.define_tables(username=True)
Você pode declarar campos extras não apenas para a tabela "auth_user", mas também para outras tabelas "auth_". Usando extra_fields
é a maneira recomendada, pois não vai quebrar nenhum mecanismo interno.
Outra maneira de fazer isso, embora não seja realmente recomendado, consiste em definir suas próprias tabelas de autenticação. Se uma tabela é declarada antes auth.define_tables()
é usado em vez do padrão. Aqui está como fazer isso:
## after auth = Auth(db)
db.define_table(
auth.settings.table_user_name,
Field('first_name', length=128, default=''),
Field('last_name', length=128, default=''),
Field('email', length=128, default='', unique=True), # required
Field('password', 'password', length=512, # required
readable=False, label='Password'),
Field('address'),
Field('city'),
Field('zip'),
Field('phone'),
Field('registration_key', length=512, # required
writable=False, readable=False, default=''),
Field('reset_password_key', length=512, # required
writable=False, readable=False, default=''),
Field('registration_id', length=512, # required
writable=False, readable=False, default=''))
## do not forget validators
custom_auth_table = db[auth.settings.table_user_name] # get the custom_auth_table
custom_auth_table.first_name.requires = IS_NOT_EMPTY(error_message=auth.messages.is_empty)
custom_auth_table.last_name.requires = IS_NOT_EMPTY(error_message=auth.messages.is_empty)
custom_auth_table.password.requires = [IS_STRONG(), CRYPT()]
custom_auth_table.email.requires = [
IS_EMAIL(error_message=auth.messages.invalid_email),
IS_NOT_IN_DB(db, custom_auth_table.email)]
auth.settings.table_user = custom_auth_table # tell auth to use custom_auth_table
## before auth.define_tables()
Você pode adicionar qualquer campo que desejar e pode alterar os validadores, mas não pode removê-los os campos marcados como "obrigatórios" neste exemplo.
É importante criar campos "password", "registration_key", "reset_password_key" e "registration_id" readable=False
e writable=False
, desde que um visitante não deve ser autorizado a mexer com eles.
Se você adicionar um campo chamado "username", ele será usado no lugar de "email" para login. Se fizer isso, você precisará adicionar um validador também:
auth_table.username.requires = IS_NOT_IN_DB(db, auth_table.username)
Observe que o Auth armazena em cache o usuário conectado na sessão e é isso que você recebe auth.user
, portanto, você precisa limpar as sessões para que as alterações dos campos extras sejam refletidas nela.
Renomeando tabelas Auth
[renaming_auth_tables]
Os nomes reais das tabelas Auth
são armazenadas em
auth.settings.table_user_name = 'auth_user'
auth.settings.table_group_name = 'auth_group'
auth.settings.table_membership_name = 'auth_membership'
auth.settings.table_permission_name = 'auth_permission'
auth.settings.table_event_name = 'auth_event'
Os nomes da tabela podem ser alterados reatribuindo as variáveis acima após o auth
objeto é definido e antes que as tabelas Auth sejam definidas. Por exemplo:
auth = Auth(db)
auth.settings.table_user_name = 'person'
#...
auth.define_tables()
As tabelas reais também podem ser referenciadas, independentemente de seus nomes reais, por
auth.settings.table_user
auth.settings.table_group
auth.settings.table_membership
auth.settings.table_permission
auth.settings.table_event
Nota: auth.signature é definido quando o Auth é inicializado, ou seja, antes de você definir os nomes das tabelas customizadas. Para evitar isso, faça:
auth = Auth(db, signature=False)
Nesse caso, auth.signature será definido quando você chamar auth.define_tables (), ponto em que os nomes das tabelas customizadas já estarão configurados.
Outros métodos de login e formulários de login
Auth fornece vários métodos de login e ganchos para criar novos métodos de login. Cada método de login suportado corresponde a um arquivo na pasta
gluon/contrib/login_methods/
Consulte a documentação nos próprios arquivos para cada método de login, mas aqui estão alguns exemplos.
Primeiro de tudo, precisamos fazer uma distinção entre dois tipos de métodos de login alternativos:
- métodos de login que usam um formulário de login web2py (embora as credenciais sejam verificadas fora do web2py). Um exemplo é o LDAP.
- métodos de login que exigem um formulário de login único externo (um exemplo é o Google e o Facebook).
No último caso, o web2py nunca obtém as credenciais de login, apenas um token de login emitido pelo provedor de serviços. O token é armazenado em db.auth_user.registration_id
.
Vamos considerar exemplos do primeiro caso:
Basic
Digamos que você tenha um serviço de autenticação, por exemplo, no URL
https://basic.example.com
que aceita autenticação básica de acesso. Isso significa que o servidor aceita solicitações HTTP com um cabeçalho do formulário:
GET /index.html HTTP/1.0
Host: basic.example.com
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
onde a última string é a codificação base64 do nome de usuário da string: password. O serviço responde 200 OK se o usuário estiver autorizado e 400, 401, 402, 403 ou 404.
Você deseja inserir o nome de usuário e senha usando o padrão Auth
formulário de login e verifique as credenciais contra tal serviço. Tudo o que você precisa fazer é adicionar o seguinte código ao seu aplicativo
from gluon.contrib.login_methods.basic_auth import basic_auth
auth.settings.login_methods.append(
basic_auth('https://basic.example.com'))
Notar que auth.settings.login_methods
é uma lista de métodos de autenticação que são executados sequencialmente. Por padrão, é definido como
auth.settings.login_methods = [auth]
Quando um método alternativo é anexado, por exemplo basic_auth
, Auth primeiro tenta entrar no visitante com base no conteúdo de auth_user
e quando isso falhar, ele tenta o próximo método na lista. Se um método conseguir efetuar login no visitante e se auth.settings.login_methods[0]==auth
, Auth
executa as seguintes ações:
- se o usuário não existir em
auth_user
, um novo usuário é criado e o nome de usuário/e-mail e senhas são armazenados. - se o usuário existir em
auth_user
mas a nova senha aceita não corresponde à senha armazenada antiga, a senha antiga é substituída pela nova (observe que as senhas são sempre armazenadas em hash, a menos que especificado de outra forma).
Se você não deseja armazenar a nova senha em auth_user
, então é suficiente alterar a ordem dos métodos de login ou remover auth
da lista. Por exemplo:
from gluon.contrib.login_methods.basic_auth import basic_auth
auth.settings.login_methods = [basic_auth('https://basic.example.com')]
O mesmo se aplica a qualquer outro método de login descrito aqui.
SMTP e Gmail SMTP Gmail
Você pode verificar as credenciais de login usando um servidor SMTP remoto, por exemplo, o Gmail; ou seja, você faz o login do usuário se o e-mail e a senha que eles fornecerem forem credenciais válidas para acessar o servidor SMTP do Gmail ( smtp.gmail.com:587
). Tudo o que é necessário é o seguinte código:
from gluon.contrib.login_methods.email_auth import email_auth
auth.settings.login_methods.append(
email_auth("smtp.gmail.com:587", "@gmail.com"))
O primeiro argumento de email_auth
é o endereço: porta do servidor SMTP. O segundo argumento é o domínio de email.
Isso funciona com qualquer servidor SMTP que exija autenticação TLS.
PAM
Autenticação usando Pluggable Authentication Modules (PAM) funciona como nos casos anteriores. Permite que o web2py autentique usuários usando as contas do sistema operacional:
from gluon.contrib.login_methods.pam_auth import pam_auth
auth.settings.login_methods.append(pam_auth())
LDAP
Autenticação usando o LDAP funciona muito como nos casos anteriores.
Para usar o login LDAP com o MS Active Directory:
from gluon.contrib.login_methods.ldap_auth import ldap_auth
auth.settings.login_methods.append(ldap_auth(mode='ad',
server='my.domain.controller',
base_dn='ou=Users,dc=domain,dc=com'))
Para usar o login LDAP com o Lotus Notes e Domino:
auth.settings.login_methods.append(ldap_auth(mode='domino',
server='my.domino.server'))
Para usar o login LDAP com o OpenLDAP (com UID):
auth.settings.login_methods.append(ldap_auth(server='my.ldap.server',
base_dn='ou=Users,dc=domain,dc=com'))
Para usar o login LDAP com o OpenLDAP (com CN):
auth.settings.login_methods.append(ldap_auth(mode='cn',
server='my.ldap.server', base_dn='ou=Users,dc=domain,dc=com'))
Existem parâmetros adicionais para deixar web2py
- ler dados adicionais, como o nome de usuário do LDAP
- implementar controle de grupo
- restringir o acesso de login.
Veja a documentação de ldap_auth
dentro web2py/gluon/contrib/login_methods/ldap_auth.py
.
Google App Engine
A autenticação usando o Google quando executado no Google App Engine exige que você ignore o formulário de login da web2py, seja redirecionado para a página de login do Google e receba sucesso. Como o comportamento é diferente dos exemplos anteriores, a API é um pouco diferente.
from gluon.contrib.login_methods.gae_google_login import GaeGoogleAccount
auth.settings.login_form = GaeGoogleAccount()
OpenID
Já discutimos anteriormente a integração com o Janrain (que tem suporte ao OpenID) e essa é a maneira mais fácil de usar o OpenID. No entanto, às vezes, você não quer depender de um serviço de terceiros e deseja acessar o provedor OpenID diretamente do consumidor (seu aplicativo).
Aqui está um exemplo:
from gluon.contrib.login_methods.openid_auth import OpenIDAuth
auth.settings.login_form = OpenIDAuth(auth)
OpenIDAuth
requer que o módulo python-openid seja instalado separadamente. Sob o capô, este método de login define a seguinte tabela:
db.define_table('alt_logins',
Field('username', length=512, default=''),
Field('type', length =128, default='openid', readable=False),
Field('user', self.table_user, readable=False))
que armazena os nomes de usuários openid para cada usuário. Se você quiser exibir os openids para o usuário logado atual:
{{=auth.settings.login_form.list_user_openids()}}
OAuth2.0 OAuth Facebook Google Twitter
Já discutimos anteriormente a integração com o Janrain, mas às vezes você não quer confiar em um serviço de terceiros e deseja acessar um provedor OAuth2.0 diretamente; por exemplo, Facebook, Linkedin, Twitter, Google, todos eles fornecem um serviço de autenticação OAuth2.0. O web2py lida com o fluxo do OAuth2.0 de forma transparente para que um usuário possa ser verificado em relação a qualquer OAuth2.0 configurado provedor durante o login. Além da autenticação, um provedor OAuth2.0 pode conceder a qualquer acesso de aplicativo web2py aos recursos do usuário com acesso restrito, uma API proprietária. Google, Twitter, Facebook e assim por diante, todos têm APIs que podem ser facilmente acessado por um aplicativo web2py.
Deve ser sublinhado que o OAuth2.0 é limitado apenas à autenticação e autorização (por exemplo, o CAS tem mais funcionalidades), isso significa que cada provedor OAuth2.0 tem uma maneira diferente de receber um ID exclusivo de seu banco de dados do usuário por meio de uma de suas APIs. Métodos específicos são bem explicados na respectiva documentação do provedor, eles geralmente consistem em uma chamada REST simples. É por isso que para cada provedor OAuth2.0 existe a necessidade de escrever algumas linhas de código.
Antes de escrever qualquer instrução no modelo de aplicativo, é necessário um primeiro passo para qualquer provedor: registrar um novo aplicativo; isso geralmente é feito no site do provedor e é explicado na documentação do provedor.
Há algumas coisas que precisam ser conhecidas quando houver a necessidade de adicionar um novo provedor OAuth2.0 ao seu aplicação: 1. o URI de autorização; 2. o URI de solicitação de token; 3. o token de identificação do aplicativo e o segredo recebido no registro do novo aplicativo; 4. as permissões que o provedor deve conceder ao aplicativo web2py, ou seja, o "escopo" (consulte a documentação do provedor); 5. a chamada da API para receber um UID do usuário autenticado, conforme explicado na documentação do provedor. Os pontos 1 a 4 são usados para inicializar o ponto de extremidade de autorização usado pelo web2py para se comunicar com o provedor OAuth2.0. O ID exclusivo é recuperado pelo web2py com uma chamada para o método get_user () quando necessário durante o fluxo de login; é aqui a chamada da API do ponto 5 é necessária.
Estas são as modificações essenciais que precisam ser feitas em seu modelo: uma. importar classe OAuthAccount; b. definir uma implementação OAuthClass derivada; c. substituir o método __init __ () dessa classe; d. sobrescreva o método get_user () dessa classe. e. instanciar a classe com os dados dos pontos 1-4 da lista acima;
Depois que a classe é instanciada e o usuário é autenticado, o aplicativo web2py pode acessar a API do provedor a qualquer momento usando o token de acesso OAuth2.0 chamando o método accessToken () dessa classe.
O que segue é um exemplo do que pode ser usado com o Facebook. Este é um exemplo básico usando a API do Facebook Graph, Lembre-se de que, ao escrever um método get_user () adequado, muitas coisas diferentes podem ser feitas. O exemplo mostra como o O token de acesso OAuth2.0 pode ser usado ao chamar a API remota do provedor.
Primeiro de tudo você deve instalar o Facebook Python SDK .
Em segundo lugar, você precisa do seguinte código em seu modelo:
## Define oauth application id and secret.
FB_CLIENT_ID='xxx'
FB_CLIENT_SECRET="yyyy"
## import required modules
try:
import json
except ImportError:
from gluon.contrib import simplejson as json
from facebook import GraphAPI, GraphAPIError
from gluon.contrib.login_methods.oauth20_account import OAuthAccount
## extend the OAUthAccount class
class FaceBookAccount(OAuthAccount):
"""OAuth impl for FaceBook"""
AUTH_URL="https://graph.facebook.com/oauth/authorize"
TOKEN_URL="https://graph.facebook.com/oauth/access_token"
def __init__(self):
OAuthAccount.__init__(self, None, FB_CLIENT_ID, FB_CLIENT_SECRET,
self.AUTH_URL, self.TOKEN_URL,
scope='email,user_about_me,user_activities, user_birthday, user_education_history, user_groups, user_hometown, user_interests, user_likes, user_location, user_relationships, user_relationship_details, user_religion_politics, user_subscriptions, user_work_history, user_photos, user_status, user_videos, publish_actions, friends_hometown, friends_location,friends_photos',
state="auth_provider=facebook",
display='popup')
self.graph = None
def get_user(self):
'''Returns the user using the Graph API.
'''
if not self.accessToken():
return None
if not self.graph:
self.graph = GraphAPI((self.accessToken()))
user = None
try:
user = self.graph.get_object("me")
except GraphAPIError, e:
session.token = None
self.graph = None
if user:
if not user.has_key('username'):
username = user['id']
else:
username = user['username']
if not user.has_key('email'):
email = '%s.fakemail' %(user['id'])
else:
email = user['email']
return dict(first_name = user['first_name'],
last_name = user['last_name'],
username = username,
email = '%s' %(email) )
## use the above class to build a new login form
auth.settings.login_form=FaceBookAccount()
Já discutimos anteriormente a integração com o Janrain (que tem suporte no LinkedIn) e essa é a maneira mais fácil de usar o OAuth. No entanto, em algum momento você não quer depender de um serviço de terceiros ou pode acessar o LinkedIn diretamente para obter mais informações do que o Janrain.
Aqui está um exemplo:
from gluon.contrib.login_methods.linkedin_account import LinkedInAccount
auth.settings.login_form=LinkedInAccount(request,KEY,SECRET,RETURN_URL)
LinkedInAccount
requer o módulo "python-linkedin" instalado separadamente.
X509
Você também pode fazer o login passando para a página um certificado x509 e sua credencial será extraída do certificado. Isto exige M2Crypto
instalado a partir de
http://chandlerproject.org/bin/view/Projects/MeTooCrypto
Depois de instalar o M2Cryption, você pode fazer:
from gluon.contrib.login_methods.x509_auth import X509Account
auth.settings.actions_disabled=['register', 'change_password', 'request_reset_password']
auth.settings.login_form = X509Account()
Agora você pode autenticar no web2py passando seu certificado x509. Como fazer isso depende do navegador, mas provavelmente é mais provável que você use certificados para serviços da web. Neste caso você pode usar por exemplo cURL
para experimentar sua autenticação:
curl -d "firstName=John&lastName=Smith" -G -v --key private.key --cert server.crt https://example/app/default/user/profile
Isso funciona fora da caixa com o Rocket (o servidor web interno web2py), mas você pode precisar de algum trabalho extra de configuração no lado do servidor web, se você estiver usando um servidor web diferente. Em particular, você precisa informar ao seu servidor da Web onde os certificados estão localizados no host local e que ele precisa verificar os certificados provenientes dos clientes. Como fazer isso é dependente do servidor web e, portanto, omitido aqui.
Múltiplos formulários de login
Alguns métodos de login modificam o login_form, outros não. Quando eles fazem isso, eles podem não ser capazes de coexistir. No entanto, alguns coexistem fornecendo vários formulários de login na mesma página. O web2py fornece uma maneira de fazer isso. Aqui está um exemplo de mistura de login normal (auth) e login RPX (janrain.com):
from gluon.contrib.login_methods.extended_login_form import ExtendedLoginForm
other_form = RPXAccount(request, api_key='...', domain='...', url='...')
auth.settings.login_form = ExtendedLoginForm(auth, other_form, signals=['token'])
Se os sinais estiverem definidos e um parâmetro na solicitação corresponder a qualquer sinal, ele retornará a chamada de other_form.login_form
em vez de. other_form
pode lidar com algumas situações particulares, por exemplo, várias etapas do login do OpenID dentro other_form.login_form
.
Caso contrário, ele renderizará o formulário de login normal junto com o other_form
.
Versão de registro
Você pode usar o Auth para ativar a versão completa de registros:
auth.enable_record_versioning(db,
archive_db=None,
archive_names='%(tablename)s_archive',
current_record='current_record'):
Isso diz ao web2py para criar uma tabela de arquivos para cada uma das tabelas db
e armazene uma cópia de cada registro quando modificado. A cópia antiga é armazenada. A nova cópia não é.
Os últimos três parâmetros são opcionais:
archive_db
permite especificar outro banco de dados no qual as tabelas de archive devem ser armazenadas. Configurando paraNone
é o mesmo que defini-lo paradb
.archive_names
fornece um padrão para nomear cada tabela de arquivo.current_record
especificou o nome do campo de referência a ser utilizado na tabela de arquivos para se referir ao registro original, não modificado. Notar quearchive_db!=db
então o campo de referência é apenas um campo inteiro, uma vez que referências cruzadas de banco de dados não são possíveis.
Apenas tabelas com modified_by
e modified_on
campos (conforme criado por exemplo, por auth.signature) será arquivado.
Quando você enable_record_versioning
, se os registros tiverem um is_active
campo (também criado por auth.signature), registros nunca serão excluídos, mas serão marcados com is_active=False
. De fato, enable_record_versioning
adiciona um common_filter
para cada tabela versionada que filtra registros com is_active=False
então eles essencialmente se tornam invisíveis.
Se vocês enable_record_versioning
, você não deve usar auth.archive
ou crud.archive
senão você terminará com registros duplicados. Essas funções explicitamente enable_record_versioning
faz automaticamente e eles serão reprovados.
Mail
e Auth
Você pode ler mais sobre a API web2py para e-mails e configuração de e-mail em Capítulo 8 . Aqui nós limitamos a discussão à interação entre Mail
e Auth
.
Definir um mailer com
from gluon.tools import Mail
mail = Mail()
mail.settings.server = 'smtp.example.com:25'
mail.settings.sender = 'you@example.com'
mail.settings.login = 'username:password'
ou simplesmente use o mailer fornecido por auth
:
mail = auth.settings.mailer
mail.settings.server = 'smtp.example.com:25'
mail.settings.sender = 'you@example.com'
mail.settings.login = 'username:password'
Você precisa substituir o mail.settings pelos parâmetros apropriados para o seu servidor SMTP. Conjunto mail.settings.login = None
se o servidor SMTP não exigir autenticação. Se você não quiser usar o TLS, defina mail.settings.tls = False
Dentro Auth
Por padrão, a verificação de e-mail está desativada. Para ativar o email, anexe as seguintes linhas no modelo onde auth
é definido:
auth.settings.registration_requires_verification = True
auth.settings.registration_requires_approval = False
auth.settings.reset_password_requires_verification = True
auth.messages.verify_email = 'Click on the link %(link)s to verify your email'
auth.messages.reset_password = 'Click on the link %(link)s to reset your password'
Nos dois auth.messages
acima, talvez seja necessário substituir a parte da URL da string pelo URL completo adequado da ação. Isso é necessário porque o web2py pode ser instalado atrás de um proxy e não pode determinar suas próprias URLs públicas com absoluta certeza. Os exemplos acima (que são os valores padrão) devem, no entanto, funcionar na maioria dos casos.
Verificação em duas etapas
A verificação em duas etapas (ou autenticação de dois fatores) é uma maneira de melhorar segurança de autenticação. A configuração adiciona uma etapa extra no processo de login. Na primeira etapa, os usuários recebem o formulário padrão de nome de usuário/senha. Se eles passar com sucesso este desafio, enviando o nome de usuário e senha corretos, e a autenticação de dois fatores é ativada para o usuário, o servidor apresentará um segundo formulário antes de registrá-los.
Essa funcionalidade pode ser ativada por usuário:
Este caso é um bom exemplo para aplicativos em que os usuários podem ativar/desativar a autenticação de dois fatores por eles mesmos.
Este formulário solicitará aos usuários um código de seis dígitos que foi enviado por email para suas contas (o servidor envia um email para o código se o nome de usuário e a senha estiverem corretos). Por padrão, o usuário irá 3 tentativas para introduzir o código. Se o código estiver incorreto após 3 tentativas, a segunda etapa de verificação será considerada como tendo falhado e o usuário deverá concluir o primeiro desafio (nome de usuário/senha) novamente.
- Crie um grupo (também conhecido como perfil) para a verificação em duas etapas. Neste exemplo, ele será chamado
auth2step
e a descrição pode serTwo-step verification
. - Dê uma participação de usuário a essa função.
- Adicione a seguinte configuração no modelo em que você criou e configurou seu objeto auth (provavelmente no modelo db.py):
auth.settings.two_factor_authentication_group = "auth2step"
- Não se esqueça de configurar o servidor de email em db.py
Essa funcionalidade pode ser ativada para todo o aplicativo:
Este formulário solicitará aos usuários um código de seis dígitos que foi enviado por email para suas contas (o servidor envia um email para o código se o nome de usuário e a senha estiverem corretos). Por padrão, o usuário irá 3 tentativas para introduzir o código. Se o código estiver incorreto após 3 tentativas, a segunda etapa de verificação será considerada como tendo falhado e o usuário deverá concluir o primeiro desafio (nome de usuário/senha) novamente.
auth.settings.auth_two_factor_enabled = True
Este caso afetará todo o usuário no aplicativo. Por exemplo, se o IP do escritório for 93.56.854.54 e você não quiser a autenticação de dois fatores do IP do escritório. Nos seus modelos:
if request.env.remote_addr != '93.56.854.54':
auth.settings.auth_two_factor_enabled = True
Outras opções que podem ser aplicadas sobre os exemplos anteriores:
#Exemplo 1: Se você quiser enviar o código por SMS em vez de e-mail. Em seus modelos, escreva:
def _sendsms(user, auth_two_factor):
#write the process to send the auth_two_factor code by SMS
return auth_two_factor
auth.settings.auth_two_factor_enabled = True
auth.messages.two_factor_comment = "Your code have been sent by SMS"
auth.settings.two_factor_methods = [lambda user, auth_two_factor: _sendsms(user, auth_two_factor)]
Para def _sendsms(...)
recebe dois valores: user e auth_two_factor:
- usuário: é uma linha com todos os seus parâmetros. Você pode acessá-los: user.email, user.first_name etc.
- auth_two_factor: string que contém o código de autenticação.
Note que no caso de você querer enviar um SMS, você precisará adicionar um campo extra, por exemplo phone
na sua tabela de usuários. Neste caso, você pode acessar o campo de telefone como user.phone
. Mais informações sobre como enviar um SMS com o web2py Emails-and-SMS
#Exemplo 2: Se você quiser enviar o código por SMS e criar ou criar um código próprio:
def _sendsms(user, auth_two_factor):
auth_two_factor = #write your own algorithm to generate the code.
#write the process to send the auth_two_factor code by SMS
return auth_two_factor
auth.settings.two_factor_methods = [lambda user, auth_two_factor: _sendsms(user, auth_two_factor)]
#Exemplo 3: o código é gerado por um cliente externo. Por exemplo, o Mobile OTP Client:
O MOTP (senha única móvel) permite que você faça o login com uma senha única (OTP) gerada em um cliente motp, os clientes motp estão disponíveis para praticamente todas as plataformas. Para saber mais sobre a visita OTP wiki-One-time-password para saber mais visite
Observe que, para essa forma de autenticação de dois fatores, telefone e servidor (onde o aplicativo web2py é hospedado) precisam ser sincronizados (no horário). Eles podem estar em um fuso horário diferente. Isso ocorre porque o OTP usa o carimbo de data/hora do Unix. Ele rastreia o tempo como um total de segundos em execução.
Alguns parâmetros extras para configuração:
Defina suas tentativas personalizadas de login:
auth.setting.auth_two_factor_tries_left = 3
Mensagem para retornar caso o código esteja incorreto:
auth.messages.invalid_two_factor_code = 'Incorrect code. {0} more attempt(s) remaining.'
Para personalizar o modelo de email:
auth.messages.retrieve_two_factor_code='Your temporary login code is {0}'
auth.messages.retrieve_two_factor_code_subject='Your temporary login code is {0}'
Para personalizar o formulário de dois fatores:
auth.messages.label_two_factor = 'Authentication code'
auth.messages.two_factor_comment = 'The code was emailed to you and is required for login.'
Autorização
Depois que um novo usuário é registrado, um novo grupo é criado para conter o usuário. O papel do novo usuário é convencionalmente "user_ [id]" onde [id] é o id do usuário recém-criado. A criação do grupo pode ser desativada com
auth.settings.create_user_groups = None
apesar de não sugerirmos fazê-lo. Notar que create_user_groups
não é um booleano (embora possa ser False
) mas o padrão é:
auth.settings.create_user_groups="user_%(id)s"
Ele armazena um modelo para o nome do grupo criado para o usuário id
.
Os usuários têm associação em grupos. Cada grupo é identificado por um nome/função. Grupos têm permissões. Os usuários têm permissões por causa dos grupos aos quais eles pertencem. Por padrão, cada usuário é membro de seu próprio grupo.
Você também pode fazer
auth.settings.everybody_group_id = 5
para tornar qualquer novo usuário automaticamente membro do grupo número 5. Aqui 5 é usado como um exemplo e assumimos que o grupo já foi criado.
Você pode criar grupos, dar adesão e permissões via appadmin ou programaticamente usando os seguintes métodos:
auth.add_group('role', 'description')
retorna o id do grupo recém-criado.
auth.del_group(group_id)
exclui o grupo com group_id
.
auth.del_group(auth.id_group('user_7'))
elimina o grupo com o papel "user_7", ou seja, o grupo exclusivamente associado ao número de usuário 7.
auth.user_group(user_id)
retorna o id do grupo unicamente associado ao usuário identificado por user_id
.
auth.add_membership(group_id, user_id)
dá user_id
filiação ao grupo group_id
. Se o user_id
não é especificado, então web2py assume o usuário logado atual.
auth.del_membership(group_id, user_id)
revoga user_id
filiação ao grupo group_id
. Se o user_id
não é especificado, então web2py assume o usuário logado atual.
auth.has_membership(group_id, user_id, role)
verifica se user_id
tem participação no grupo group_id
ou o grupo com a função especificada. Somente group_id
ou role
deve ser passado para a função, não ambos. Se o user_id
não é especificado, então web2py assume o usuário logado atual.
NOTA: Para evitar a consulta do banco de dados em cada carregamento de página que usa auth.has_membership, alguém pode usar cached = True. Se o cache estiver configurado para True has_membership (), marque group_id ou role apenas na variável auth.user_groups, que é preenchida corretamente apenas no momento do login. Isso significa que, se uma associação de usuário for alterada durante uma determinada sessão, o usuário terá que fazer logoff e efetuar login novamente para que auth.user_groups sejam recriados corretamente e reflitam a modificação da associação do usuário. Há uma exceção neste logoff e no processo de login, que é o caso de o usuário alterar sua própria participação, neste caso o auth.user_groups pode ser atualizado corretamente para o usuário conectado porque o web2py tem acesso à variável user_groups da sessão. Para fazer uso desta exceção, alguém tem que colocar uma instrução "auth.update_groups ()" em seu código de aplicativo para forçar a atualização de auth.user_groups. Como menção, isso só funcionará se o próprio usuário que alterá-lo não for membro se outro usuário, digamos um administrador, alterar a associação de outra pessoa.
auth.add_permission(group_id, 'name', 'object', record_id)
dá permissão "nome" (definido pelo usuário) no objeto "objeto" (também definido pelo usuário) para membros do grupo group_id
. Se "objeto" é um nome de tabela, a permissão pode se referir a toda a tabela, definindo record_id
para um valor de zero, ou a permissão pode se referir a um registro específico especificando um record_id
valor maior que zero. Ao conceder permissões em tabelas, é comum usar um nome de permissão no conjunto ('criar', 'ler', 'atualizar', 'excluir', 'selecionar'), pois essas permissões são compreendidas e podem ser aplicadas pelo CRUD APIs.
E se group_id
é zero, o web2py usa o grupo exclusivamente associado ao atual usuário logado.
Você também pode usar auth.id_group(role="...")
para obter o id de um grupo dado seu nome.
auth.del_permission(group_id, 'name', 'object', record_id)
revoga a permissão.
auth.has_permission('name', 'object', record_id, user_id)
verifica se o usuário identificado por user_id
tem associação em um grupo com a permissão solicitada.
rows = db(auth.accessible_query('read', db.mytable, user_id)) .select(db.mytable.ALL)
retorna todas as linhas da tabela "mytable" que o usuário user_id
tem permissão de "leitura". Se o user_id
não é especificado, então web2py assume o usuário logado atual. o accessible_query(...)
pode ser combinado com outras consultas para tornar as mais complexas. accessible_query(...)
é o único Auth método para exigir um JOIN, por isso não funciona no Google App Engine.
Assumindo as seguintes definições:
>>> from gluon.tools import Auth
>>> auth = Auth(db)
>>> auth.define_tables()
>>> secrets = db.define_table('secret_document', Field('body'))
>>> james_bond = db.auth_user.insert(first_name='James',
last_name='Bond')
Aqui está um exemplo:
>>> doc_id = db.secret_document.insert(body = 'top secret')
>>> agents = auth.add_group(role = 'Secret Agent')
>>> auth.add_membership(agents, james_bond)
>>> auth.add_permission(agents, 'read', secrets)
>>> print auth.has_permission('read', secrets, doc_id, james_bond)
True
>>> print auth.has_permission('update', secrets, doc_id, james_bond)
False
Decorators
A maneira mais comum de verificar a permissão não é por chamadas explícitas para os métodos acima, mas pela decoração de funções para que as permissões sejam verificadas em relação ao visitante conectado. aqui estão alguns exemplos:
def function_one():
return 'this is a public function'
@auth.requires_login()
def function_two():
return 'this requires login'
@auth.requires_membership('agents')
def function_three():
return 'you are a secret agent'
@auth.requires_permission('read', secrets)
def function_four():
return 'you can read secret documents'
@auth.requires_permission('delete', 'any file')
def function_five():
import os
for file in os.listdir('./'):
os.unlink(file)
return 'all files deleted'
@auth.requires(auth.user_id==1 or request.client=='127.0.0.1', requires_login=True)
def function_six():
return 'you can read secret documents'
@auth.requires_permission('add', 'number')
def add(a, b):
return a + b
def function_seven():
return add(3, 4)
O argumento de condição de @auth.requires(condition)
pode ser uma chamada e, a menos que a condição seja simples, é melhor passar uma condição de chamada que uma condição, pois ela será mais rápida, pois a condição será avaliada apenas se necessário. Por exemplo
@auth.requires(lambda: check_condition())
def action():
....
@auth.requires
também aceita um argumento opcional requires_login
qual padrão é True
. Se definido como Falso, não será necessário efetuar login antes de avaliar a condição como verdadeiro/falso. A condição pode ser um valor booleano ou uma função que é avaliada como booleana.
Observe que o acesso a todas as funções além da primeira é restrito com base nas permissões que o visitante pode ou não ter.
Se o visitante não estiver logado, a permissão não poderá ser verificada; o visitante é redirecionado para a página de login e, em seguida, volta para a página que requer permissões.
Combinando requisitos
Ocasionalmente, é necessário combinar requisitos. Isso pode ser feito através de um decorator genérico requires
que leva um único argumento, uma condição verdadeira ou falsa. Por exemplo, para dar acesso aos agentes, mas apenas na terça-feira:
@auth.requires(auth.has_membership(group_id='agents') and request.now.weekday()==1)
def function_seven():
return 'Hello agent, it must be Tuesday!'
ou equivalente:
@auth.requires(auth.has_membership(role='Secret Agent') and request.now.weekday()==1)
def function_seven():
return 'Hello agent, it must be Tuesday!'
Autorização e CRUD
Usar decorators e/ou verificações explícitas fornece uma maneira de implementar o controle de acesso.
Outra maneira de implementar o controle de acesso é sempre usar o CRUD (em oposição a SQLFORM
) para acessar o banco de dados e solicitar que o CRUD imponha controle de acesso a tabelas e registros de banco de dados. Isso é feito ligando Auth
e CRUD com a seguinte declaração:
crud.settings.auth = auth
Isso impedirá que o visitante acesse qualquer uma das funções CRUD, a menos que o visitante esteja logado e tenha acesso explícito. Por exemplo, para permitir que um visitante poste comentários, mas apenas atualize seus próprios comentários (supondo que crud, auth e db.comment estão definidos):
def give_create_permission(form):
group_id = auth.id_group('user_%s' % auth.user.id)
auth.add_permission(group_id, 'read', db.comment)
auth.add_permission(group_id, 'create', db.comment)
auth.add_permission(group_id, 'select', db.comment)
def give_update_permission(form):
comment_id = form.vars.id
group_id = auth.id_group('user_%s' % auth.user.id)
auth.add_permission(group_id, 'update', db.comment, comment_id)
auth.add_permission(group_id, 'delete', db.comment, comment_id)
auth.settings.register_onaccept = give_create_permission
crud.settings.auth = auth
def post_comment():
form = crud.create(db.comment, onaccept=give_update_permission)
comments = db(db.comment).select()
return dict(form=form, comments=comments)
def update_comment():
form = crud.update(db.comment, request.args(0))
return dict(form=form)
Você também pode selecionar registros específicos (aqueles aos quais você tem acesso de 'leitura'):
def post_comment():
form = crud.create(db.comment, onaccept=give_update_permission)
query = auth.accessible_query('read', db.comment, auth.user.id)
comments = db(query).select(db.comment.ALL)
return dict(form=form, comments=comments)
Os nomes das permissões impostas por:
crud.settings.auth = auth
são "read", "create", "update", "delete", "select", "impersonate".
Autorização e downloads
O uso de decorators e o uso de crud.settings.auth
não imponha autorização em arquivos baixados pela função de download normal
def download(): return response.download(request, db)
Se alguém desejar, deve declarar explicitamente quais campos "upload" contêm arquivos que precisam de controle de acesso no momento do download. Por exemplo:
db.define_table('dog',
Field('small_image', 'upload'),
Field('large_image', 'upload'))
db.dog.large_image.authorize = lambda record: auth.is_logged_in() and auth.has_permission('read', db.dog, record.id, auth.user.id)
O atributo authorize
do campo de upload pode ser Nenhum (o padrão) ou uma função que decide se o usuário está logado e tem permissão para 'ler' o registro atual. Neste exemplo, não há restrição ao download de imagens vinculadas pelo campo "small_image", mas exigimos o controle de acesso em imagens vinculadas pelo campo "large_image".
Controle de acesso e autenticação básica
Ocasionalmente, pode ser necessário expor ações que tenham decorators que exigem controle de acesso como serviços; ou seja, para chamá-los de um programa ou script e ainda poder usar a autenticação para verificar a autorização.
Auth permite login via autenticação básica:
auth.settings.allow_basic_login = True
Com este conjunto, uma ação como
@auth.requires_login()
def give_me_time():
import time
return time.ctime()
pode ser chamado, por exemplo, de um comando shell:
wget --user=[username] --password=[password] --auth-no-challenge
http://.../[app]/[controller]/give_me_time
Também é possível fazer o login chamando auth.basic()
em vez de usar um decorator @auth
:
def give_me_time():
import time
auth.basic()
if auth.user:
return time.ctime()
else:
return 'Not authorized'
O login básico geralmente é a única opção para serviços (descrita no próximo capítulo), mas está desabilitado por padrão.
Gerenciamento de Aplicativos via usuários privilegiados (Experimental)
Normalmente, as funções de administrador, como a definição de usuários e grupos, são gerenciadas pelo administrador do servidor. No entanto, você pode querer que um grupo de usuários privilegiados tenha direitos de administrador para um aplicativo específico. Isso é possível com versões depois de web2py v2.5.1 (A atualização de um aplicativo existente requer o novo controlador appadmin e a nova visualização appadmin.html, copiados do aplicativo de boas-vindas. Além disso, os aplicativos criados antes do web2py v2.6 precisam do novo arquivo javascript em welcome/static/js/web2py.js)
O conceito permite diferentes configurações de gerenciamento, cada uma permitindo que um grupo de usuários edite um determinado conjunto de tabelas neste aplicativo.
Exemplo: Primeiro, crie um grupo (também conhecido como perfil) para seus usuários privilegiados. Neste exemplo, será chamado admin. Dê uma participação de usuário a essa função. Segundo, pense em um nome para descrever essa configuração de gerenciamento, como db_admin.
Adicione a seguinte configuração no modelo em que você criou e configurou seu objeto auth (provavelmente no modelo db):
auth.settings.manager_actions = dict(db_admin=dict(role='admin', heading='Manage Database', tables = db.tables))
Um item de menu tem o URL como abaixo, passando o nome da configuração de gerenciamento como um arg:
URL('appadmin', 'manage', args=['db_admin'])
Este URL aparece como/appadmin/manage/auth.
Uso avançado
Esse mecanismo permite várias configurações de gerenciamento; Cada configuração de gerenciamento adicional é apenas outra chave definida em auth.settings.manager_actions.
Por exemplo, talvez você queira que um grupo de usuários (como 'Super') tenha acesso a todas as tabelas em uma configuração de gerenciamento chamada "db_admin" e outro grupo (como 'Gerenciador de conteúdo') para ter acesso administrativo a tabelas relacionadas para conteúdo em uma configuração de gerenciamento chamada "content_admin".
Isso pode ser configurado assim:
auth.settings.manager_actions = dict(
db_admin=dict(role='Super', heading='Manage Database', tables=db.tables),
content_admin=dict(role='Content Manager', tables=[content_db.articles, content_db.recipes, content_db.comments])
content_mgr_group_v2 = dict(role='Content Manager v2', db=content_db,
tables=['articles', 'recipes', 'comments'],
smartgrid_args=dict(
DEFAULT=dict(maxtextlength=50, paginate=30),
comments=dict(maxtextlength=100, editable=False)
)
)
(A chave do cabeçalho é opcional. Se ausente, um padrão inteligente será usado)
Você poderia então fazer dois novos itens de menu com estas URLs:
URL('appadmin', 'manage', args=['db_admin'])
URL('appadmin', 'manage', args=['content_admin'])
A configuração de gerenciamento chamada "content_mgr_group_v2" mostra algumas possibilidades mais avançadas. A chave smartgrid_args é passada para a smartgrid usada para editar ou visualizar as tabelas. Além da chave especial DEFAULT, os nomes das tabelas são passados como chaves (como a tabela chamada "comentários"). A sintaxe neste exemplo nomeia as tabelas como uma lista de strings, usando a chave db = content_db para especificar o banco de dados.
Autenticação manual
Algumas vezes você quer implementar sua própria lógica e fazer login de usuário "manual". Isso também pode ser feito chamando a função:
user = auth.login_bare(username, password)
login_bare
retorna o usuário se o usuário existir e a senha for válida, caso contrário, retornará False. username
é o email se a tabela "auth_user" não tiver um campo "username".
Configurações de autenticação e mensagens
Aqui está uma lista de todos os parâmetros que podem ser personalizados para Auth
O seguinte deve apontar para um gluon.tools.Mail
objeto para permitir auth
para enviar e-mails:
auth.settings.mailer = None
Leia mais sobre como configurar o correio aqui: Mail and Auth
O seguinte deve ser o nome do controlador que definiu o user
açao:
auth.settings.controller = 'default'
O seguinte foi uma configuração muito importante em versões mais antigas do web2py:
auth.settings.hmac_key = None
Onde foi definido como algo como "sha512: a-pass-phrase" e passado para o validador CRYPT para o campo "senha" do auth_user
tabela, fornecendo o algoritmo e a-pass-frase usado para hash as senhas. No entanto, o web2py longers precisa dessa configuração porque manipula isso automaticamente.
Por padrão, a autenticação também requer um comprimento mínimo de senha de 4. Isso pode ser alterado:
auth.settings.password_min_length = 4
Para desativar uma ação, anexe seu nome a esta lista:
auth.settings.actions_disabled = []
Por exemplo:
auth.settings.actions_disabled.append('register')
irá desativar o registro.
Se você deseja receber um email para verificar o registro, defina True
:
auth.settings.registration_requires_verification = False
Para fazer o login automaticamente das pessoas após o registro, mesmo que elas não tenham concluído o processo de verificação de e-mail, defina o seguinte para True
:
auth.settings.login_after_registration = False
Se novos registrantes precisarem aguardar aprovação antes de poderem fazer login, defina True
:
auth.settings.registration_requires_approval = False
A aprovação consiste em definir registration_key==''
via appadmin ou programaticamente.
Se você não quiser um novo grupo para cada novo usuário, defina o seguinte para False
:
auth.settings.create_user_groups = True
As configurações a seguir determinam métodos de login alternativos e formulários de login, conforme discutido anteriormente:
auth.settings.login_methods = [auth]
auth.settings.login_form = auth
Você quer permitir login básico?
auth.settings.allows_basic_login = False
O seguinte é o URL do login
açao:
auth.settings.login_url = URL('user', args='login')
Se o usuário tentou acessar a página de registro, mas já está logado, ele será redirecionado para este URL:
auth.settings.logged_url = URL('user', args='profile')
Isso deve apontar para o URL da ação de download, caso o perfil contenha imagens:
auth.settings.download_url = URL('download')
Eles devem apontar para o URL para o qual você deseja redirecionar seus usuários depois dos vários possíveis auth
ações (caso não haja referenciador):
Nota: Se o seu aplicativo for baseado no aplicativo padrão Scaffold Welcome, use o auth.navbar. Para que as configurações abaixo entrem em vigor, você precisa editar o layout.html e definir o argumento referrer_actions = None. auth.navbar (mode = 'dropdown', referrer_actions = None)
Também é possível manter referrer_actions para alguns eventos de autenticação. Por exemplo
auth.navbar(referrer_actions=['login', 'profile'])
Se o comportamento padrão for deixado inalterado, o auth.navbar usa o parâmetro de URL _next e o usa para enviar o usuário de volta à página de referência. No entanto, se o comportamento de auto-referência padrão da barra de navegação for alterado, as configurações abaixo terão efeito.
auth.settings.login_next = URL('index')
auth.settings.logout_next = URL('index')
auth.settings.profile_next = URL('index')
auth.settings.register_next = URL('user', args='login')
auth.settings.retrieve_username_next = URL('index')
auth.settings.retrieve_password_next = URL('index')
auth.settings.change_password_next = URL('index')
auth.settings.request_reset_password_next = URL('user', args='login')
auth.settings.reset_password_next = URL('user', args='login')
auth.settings.verify_email_next = URL('user', args='login')
Se o visitante não estiver logado e chamar uma função que requer autenticação, o usuário é redirecionado para auth.settings.login_url
qual padrão é URL('default', 'user/login')
. Pode-se substituir esse comportamento redefinindo:
auth.settings.on_failed_authentication = lambda url: redirect(url)
Esta é a função chamada para o redirecionamento. O argumento url
`passado para esta função é o url da página de login.
Se o visitante não tiver permissão para acessar uma determinada função, o visitante será redirecionado para a URL definida por
auth.settings.on_failed_authorization = URL('user', args='on_failed_authorization')
Você pode alterar essa variável e redirecionar o usuário para outro lugar.
Frequentemente on_failed_authorization
é um URL, mas pode ser uma função que retorna o URL e será chamada em falha na autorização.
Estas são listas de callbacks que devem ser executadas após a validação do formulário para cada uma das ações correspondentes antes de qualquer IO do banco de dados:
auth.settings.login_onvalidation = []
auth.settings.register_onvalidation = []
auth.settings.profile_onvalidation = []
auth.settings.retrieve_password_onvalidation = []
auth.settings.reset_password_onvalidation = []
Cada retorno de chamada deve ser uma função que leva o form
objeto e pode modificar os atributos do objeto de formulário antes do IO do banco de dados ser executado.
Estas são listas de retornos de chamada que devem ser executados após o IO do banco de dados e antes do redirecionamento:
auth.settings.login_onaccept = []
auth.settings.register_onaccept = []
auth.settings.profile_onaccept = []
auth.settings.verify_email_onaccept = []
Aqui está um exemplo:
auth.settings.register_onaccept.append(lambda form: mail.send(to='you@example.com', subject='new user',
message='new user email is %s'%form.vars.email))
Você pode ativar o captcha para qualquer um dos auth
ações:
auth.settings.captcha = None
auth.settings.login_captcha = None
auth.settings.register_captcha = None
auth.settings.retrieve_username_captcha = None
auth.settings.retrieve_password_captcha = None
Se o .captcha
configurações aponta para um gluon.tools.Recaptcha
, todas as formas para as quais a opção correspondente (como .login_captcha
) está configurado para None
terá um captcha, enquanto aqueles para os quais a opção correspondente é definida como False
não vou. Se, em vez disso, .captcha
está configurado para None
, apenas aqueles que têm uma opção correspondente definida como gluon.tools.Recaptcha
objeto terá captcha e os outros não.
Este é o tempo de expiração da sessão de login:
auth.settings.expiration = 3600 # seconds
Você pode alterar o nome do campo de senha (no Firebird, por exemplo, "senha" é uma palavra-chave e não pode ser usada para nomear um campo):
auth.settings.password_field = 'password'
Normalmente, o formulário de login tenta validar um email. Isso pode ser desativado, alterando esta configuração:
auth.settings.login_email_validate = True
Deseja mostrar o ID do registro na página do perfil de edição?
auth.settings.showid = False
Para formulários personalizados, convém desativar a notificação automática de erros nos formulários:
auth.settings.hideerror = False
Também para formulários personalizados, você pode alterar o estilo:
auth.settings.formstyle = 'table3cols'
(pode ser "bootstrap3_inline", "table3cols", "table2cols", "divs" e "ul"; para todas as opções, consulte gluon/sqlhtml.py)
E você pode definir o separador para formulários gerados por autenticação:
auth.settings.label_separator = ':'
Por padrão, o formulário de login oferece a opção de estender o login por meio da opção "lembrar-me". O tempo de expiração pode ser alterado ou a opção desativada por meio dessas configurações:
auth.settings.long_expiration = 3600*24*30 # one month
auth.settings.remember_me_form = True
Você também pode personalizar as seguintes mensagens cujo uso e contexto devem ser óbvios:
auth.messages.submit_button = 'Submit'
auth.messages.verify_password = 'Verify Password'
auth.messages.delete_label = 'Check to delete:'
auth.messages.function_disabled = 'Function disabled'
auth.messages.access_denied = 'Insufficient privileges'
auth.messages.registration_verifying = 'Registration needs verification'
auth.messages.registration_pending = 'Registration is pending approval'
auth.messages.login_disabled = 'Login disabled by administrator'
auth.messages.logged_in = 'Logged in'
auth.messages.email_sent = 'Email sent'
auth.messages.unable_to_send_email = 'Unable to send email'
auth.messages.email_verified = 'Email verified'
auth.messages.logged_out = 'Logged out'
auth.messages.registration_successful = 'Registration successful'
auth.messages.invalid_email = 'Invalid email'
auth.messages.unable_send_email = 'Unable to send email'
auth.messages.invalid_login = 'Invalid login'
auth.messages.invalid_user = 'Invalid user'
auth.messages.is_empty = "Cannot be empty"
auth.messages.mismatched_password = "Password fields don't match"
auth.messages.verify_email = ...
auth.messages.verify_email_subject = 'Password verify'
auth.messages.username_sent = 'Your username was emailed to you'
auth.messages.new_password_sent = 'A new password was emailed to you'
auth.messages.password_changed = 'Password changed'
auth.messages.retrieve_username = 'Your username is: %(username)s'
auth.messages.retrieve_username_subject = 'Username retrieve'
auth.messages.retrieve_password = 'Your password is: %(password)s'
auth.messages.retrieve_password_subject = 'Password retrieve'
auth.messages.reset_password = ...
auth.messages.reset_password_subject = 'Password reset'
auth.messages.invalid_reset_password = 'Invalid reset password'
auth.messages.profile_updated = 'Profile updated'
auth.messages.new_password = 'New password'
auth.messages.old_password = 'Old password'
auth.messages.group_description = 'Group uniquely assigned to user %(id)s'
auth.messages.register_log = 'User %(id)s Registered'
auth.messages.login_log = 'User %(id)s Logged-in'
auth.messages.logout_log = 'User %(id)s Logged-out'
auth.messages.profile_log = 'User %(id)s Profile updated'
auth.messages.verify_email_log = 'User %(id)s Verification email sent'
auth.messages.retrieve_username_log = 'User %(id)s Username retrieved'
auth.messages.retrieve_password_log = 'User %(id)s Password retrieved'
auth.messages.reset_password_log = 'User %(id)s Password reset'
auth.messages.change_password_log = 'User %(id)s Password changed'
auth.messages.add_group_log = 'Group %(group_id)s created'
auth.messages.del_group_log = 'Group %(group_id)s deleted'
auth.messages.add_membership_log = None
auth.messages.del_membership_log = None
auth.messages.has_membership_log = None
auth.messages.add_permission_log = None
auth.messages.del_permission_log = None
auth.messages.has_permission_log = None
auth.messages.label_first_name = 'First name'
auth.messages.label_last_name = 'Last name'
auth.messages.label_username = 'Username'
auth.messages.label_email = 'E-mail'
auth.messages.label_password = 'Password'
auth.messages.label_registration_key = 'Registration key'
auth.messages.label_reset_password_key = 'Reset Password key'
auth.messages.label_registration_id = 'Registration identifier'
auth.messages.label_role = 'Role'
auth.messages.label_description = 'Description'
auth.messages.label_user_id = 'User ID'
auth.messages.label_group_id = 'Group ID'
auth.messages.label_name = 'Name'
auth.messages.label_table_name = 'Table name'
auth.messages.label_record_id = 'Record ID'
auth.messages.label_time_stamp = 'Timestamp'
auth.messages.label_client_ip = 'Client IP'
auth.messages.label_origin = 'Origin'
auth.messages.label_remember_me = "Remember me (for 30 days)"
add|del|has
os registros de associação permitem o uso de "% (user_id) s" e "% (group_id) s". add|del|has
os logs de permissão permitem o uso de "% (user_id) s", "% (nome) s", "% (table_name) s" e "% (record_id) s". Serviço de Autenticação Central CAS authentication
O web2py fornece suporte para autenticação de terceiros e logon único. Aqui discutimos o Serviço de Autenticação Central (CAS), que é um padrão da indústria, e o cliente e o servidor são integrados ao web2py.
O CAS é um protocolo aberto para autenticação distribuída e funciona da seguinte maneira: Quando um visitante chega ao nosso site, nosso aplicativo verifica a sessão se o usuário já está autenticado (por exemplo, por meio de um session.token
objeto). Se o usuário não for autenticado, o controlador redirecionará o visitante do dispositivo CAS, onde o usuário poderá efetuar login, registrar e gerenciar suas credenciais (nome, email e senha). Se o usuário se registra, ele recebe um e-mail e o registro não é concluído até que ele responda ao e-mail. Uma vez que o usuário tenha registrado e logado com sucesso, o dispositivo CAS redireciona o usuário para o nosso aplicativo junto com uma chave. Nosso aplicativo usa a chave para obter as credenciais do usuário por meio de uma solicitação HTTP em segundo plano para o servidor CAS.
Usando esse mecanismo, vários aplicativos podem usar uma conexão única por meio de um único servidor CAS. O servidor que fornece autenticação é chamado de provedor de serviços. Aplicativos que buscam autenticar visitantes são chamados de consumidores de serviços.
O CAS é semelhante ao OpenID, com uma diferença principal. No caso do OpenID, o visitante escolhe o provedor de serviços. No caso do CAS, nosso aplicativo faz essa escolha, tornando o CAS mais seguro.
A execução de um provedor Web2py do CAS é tão fácil quanto copiar o aplicativo de andaimes. Na verdade, qualquer aplicativo web2py que expõe a ação
## in provider app
def user(): return dict(form=auth())
é um provedor CAS 2.0 e seus serviços podem ser acessados na URL
http://.../provider/default/user/cas/login
http://.../provider/default/user/cas/validate
http://.../provider/default/user/cas/logout
(assumimos que o aplicativo seja chamado de "provedor").
Você pode acessar este serviço a partir de qualquer outro aplicativo da Web (o consumidor) simplesmente delegando a autenticação ao provedor:
## in consumer app
auth = Auth(db, cas_provider='http://127.0.0.1:8000/provider/default/user/cas')
Quando você visitar o URL de login do aplicativo do consumidor, ele será redirecionado para o aplicativo do provedor, que executará a autenticação e redirecionará de volta para o consumidor. Todos os processos de registro, logout, alteração de senha, recuperação de senha devem ser preenchidos no aplicativo do provedor. Uma entrada sobre o usuário que efetuou login será criada no lado do consumidor para que você adicione campos extras e tenha um perfil local. Graças ao CAS 2.0 todos os campos que são legíveis no provedor e têm um campo correspondente no campo auth_user
tabela do consumidor será copiada automaticamente.
Auth(..., cas_provider='...')
trabalha com provedores de terceiros e suporta CAS 1.0 e 2.0. A versão é detectada automaticamente. Por padrão, ele cria as URLs do provedor a partir de uma base (o cas_provider
URL acima) anexando
/login
/validate
/logout
Estes podem ser alterados no consumidor e no provedor
## in consumer or provider app (must match)
auth.settings.cas_actions['login']='login'
auth.settings.cas_actions['validate']='validate'
auth.settings.cas_actions['logout']='logout'
Se você deseja se conectar a um provedor CAS web2py de um domínio diferente, deve ativá-los anexando à lista de domínios permitidos:
## in provider app
auth.settings.cas_domains.append('example.com')
Usando o web2py para autorizar aplicativos que não são web2py
Isso é possível, mas depende do servidor da web. aqui nós assumimos dois aplicativos rodando sob o mesmo servidor web: Apache com mod_wsgi
. Um dos aplicativos é o web2py com um aplicativo que provê controle de acesso via Auth. O outro pode ser um script CGI, um programa PHP ou qualquer outra coisa. Queremos instruir o servidor da Web para solicitar permissão ao aplicativo anterior quando um cliente solicitar acesso a ele.
Primeiro de tudo precisamos modificar o aplicativo web2py e adicionar o seguinte controlador:
def check_access():
return 'true' if auth.is_logged_in() else 'false'
que retorna true
se o usuário estiver logado e false
de outra forma. Agora execute um processo web2py em segundo plano:
nohup python web2py.py -a '' -p 8002
A porta 8002 é uma obrigação e não há necessidade de ativar o administrador, portanto, nenhuma senha de administrador.
Então precisamos editar o arquivo de configuração do Apache (por exemplo "/ etc/apache2/sites-available/default") e instruir o apache para que quando o programa não-web2py for chamado, ele chame o programa acima check
ação em vez disso e somente se ele retorna true
deve prosseguir e responder ao pedido, caso contrário, deve negar o acesso.
Como o web2py e o aplicativo não-web2py são executados no mesmo domínio, se o usuário fizer login no aplicativo web2py, o cookie da sessão web2py será transmitido para o Apache mesmo quando o outro aplicativo for solicitado e permitirá a verificação de credenciais.
Para conseguir isso, precisamos de um script, "web2py/scripts/access.wsgi", que possa executar esse truque. O script é enviado com o web2py. Tudo o que precisamos fazer é dizer ao apache para chamar esse script, a URL do aplicativo que precisa de controle de acesso e a localização do script:
<VirtualHost *:80>
WSGIDaemonProcess web2py user=www-data group=www-data
WSGIProcessGroup web2py
WSGIScriptAlias / /home/www-data/web2py/wsgihandler.py
AliasMatch ^myapp/path/needing/authentication/myfile /path/to/myfile
<Directory /path/to/>
WSGIAccessScript /path/to/web2py/scripts/access.wsgi
</Directory>
</VirtualHost>
Aqui "^ myapp/path/need/authentication/myfile" é a expressão regular que deve corresponder à solicitação recebida e "/ path/to /" é o local absoluto da pasta web2py.
O script "access.wsgi" contém a seguinte linha:
URL_CHECK_ACCESS = 'http://127.0.0.1:8002/%(app)s/default/check_access'
que aponta para o aplicativo web2py que solicitamos, mas você pode editá-lo para apontar para um aplicativo específico, sendo executado em uma porta diferente de 8002.
Você também pode alterar o check_access()
ação e tornar sua lógica mais complexa. Esta ação pode recuperar a URL originalmente solicitada usando a variável de ambiente
request.env.request_uri
e você pode implementar regras mais complexas:
def check_access():
if not auth.is_logged_in():
return 'false'
elif not user_has_access(request.env.request_uri):
return 'false'
else:
return 'true'