Commit d3bf36bf authored by ale's avatar ale

Merge branch 'auth-api-refactor' into 'master'

Auth api refactor

See merge request !2
parents 877e7c5b f801e018
......@@ -29,13 +29,44 @@ class AuthBase(object):
supports_otp = False
def __init__(self, config):
self.config = config
pass
def authenticate(self, username, password, otp=None):
"""Authenticate a user.
Args:
username: username (utf-8 encoded string).
password: password (utf-8 encoded string).
otp: OTP token, if provided by the user. Note that this
should be a string, not an integer.
Returns:
A boolean result indicating successful authentication.
"""
return False
def match_groups(self, username, groups):
"""Match user groups against requested ones.
Args:
username: username (utf-8 encoded string).
groups: collection (list, set) of requested groups. Group
names are strings.
Returns:
An unsorted collection (set) containing the intersection of
the groups that the user is a member of and the requested
groups.
"""
return set()
def get_user_email(self, username):
"""Return the email address associated with a user.
The SAML module requires this information: some endpoints
(Gitlab, for instance) make use of it to match local records.
Args:
username: username (utf-8 encoded string).
Returns:
The email address associated with the user, or None.
"""
return None
# Copyright (c) 2016 Autistici/Inventati <info@autistici.org>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
import authclient
from sso_server.auth import AuthBase
class Auth(AuthBase):
"""Authclient authentication provider.
This module knows about the following options:
AUTHCLIENT_SERVER_URL
The URI of the authserver.
AUTHCLIENT_CERT
File with the SSL client certificate.
AUTHCLIENT_KEY
File with the SSL client private key.
AUTHCLIENT_SERVICE
Service name for the authclient protocol.
AUTHCLIENT_SHARD
Shard for the authclient protocol (optional).
"""
supports_otp = True
def __init__(self, config):
self._client = authclient.Client(
url=config['AUTHCLIENT_SERVER_URL'],
client_cert=config['AUTHCLIENT_CERT'],
client_key=config['AUTHCLIENT_KEY'],
)
self._service = config['AUTHCLIENT_SERVICE']
self._shard = config.get('AUTHCLIENT_SHARD')
def authenticate(self, username, password, otp=None):
result = self._client.authenticate(
self._service,
username, password, otp,
shard=self._shard)
return result == authclient.OK
......@@ -22,10 +22,13 @@
# OTHER DEALINGS IN THE SOFTWARE.
import ldap
from ldap.dn import escape_dn_chars
import logging
import re
from sso_server.auth import AuthBase
log = logging.getLogger(__name__)
class Auth(AuthBase):
"""LDAP Authentication.
......@@ -33,37 +36,28 @@ class Auth(AuthBase):
The module will attempt to bind as the user with the given credentials.
It can be configured with a number of options:
auth_ldap_uri
AUTH_LDAP_URI
The URI of the LDAP server to connect to
(default: ldap://localhost:389).
auth_ldap_base
AUTH_LDAP_BASE
Base DN to build the user DN. You will need to specify this.
auth_ldap_dn_format
How to build the user part of the DN (default: uid=%s).
AUTH_LDAP_RDN_FORMAT
How to build the RDN (default: uid=%s). The result will be
prepended to AUTH_LDAP_BASE to generate the final DN. The
"%s" token will be replaced by the username.
"""
def __init__(self, config):
try:
self.uri = config.auth_ldap_uri
except AttributeError:
self.uri = 'ldap://localhost:389'
try:
self.base = config.auth_ldap_base
except AttributeError:
self.base = 'o=Anarchy'
try:
self.rdn_fmt = config.auth_ldap_dn_format
except AttributeError:
self.rdn_fmt = 'uid=%s'
self.base = config['AUTH_LDAP_BASE']
self.uri = config.get('AUTH_LDAP_URI', 'ldap://localhost:389')
self.rdn_fmt = config.get('AUTH_LDAP_RDN_FORMAT', 'uid=%s')
def authenticate(self, username, password, otp=None):
if not re.search(r'^[-a-zA-Z0-9.]+$', username):
return False
if not password:
if not username or not password:
return False
rdn = self.rdn_fmt % username
rdn = self.rdn_fmt % escape_dn_chars(username)
dn = '%s,%s' % (rdn, self.base)
l = ldap.ldapobject.LDAPObject(self.uri)
result = False
......@@ -72,10 +66,12 @@ class Auth(AuthBase):
result = True
except ldap.INVALID_CREDENTIALS:
pass
except Exception, e:
logging.error('Exception while authenticating %s: %s', username, e)
except Exception as e:
log.error('LDAP error while authenticating %s: %s', username, e)
l.unbind()
return result
def match_groups(self, username, groups):
return groups
# TODO: Not implemented. There are too many ways to store
# group membership information in LDAP.
return set()
......@@ -42,16 +42,14 @@ def get_user_groups(username):
class Auth(AuthBase):
"""PAM-based authentication.
Uses PAM to authenticate users. The default service is 'sso', but
you can use a different one specifying the 'auth_pam_service' variable
in the configuration file.
Uses PAM to authenticate users. The default service is 'sso', but you can
use a different one specifying the AUTH_PAM_SERVICE variable in the
configuration.
"""
def __init__(self, config):
try:
self.service = config.auth_pam_service
except AttributeError:
self.service = "sso"
self.service = config.get('AUTH_PAM_SERVICE', 'sso')
def authenticate(self, username, password, otp=None):
pam = PAM.pam()
......
......@@ -31,9 +31,6 @@ class Auth(AuthBase):
and password are equal. It is used only for testing purposes.
The user will be granted membership to a set of sample groups.
"""
def __init__(self, config):
self.config = config
def authenticate(self, u, p, otp=None):
if u == 'error':
raise KeyError('blah!')
......
......@@ -39,7 +39,7 @@ class LoginService(object):
SSO_AUTH_MODULE
Authentication plugin. This should be the Python module path to
something which implements the so_server.auth.AuthBase interface.
something which implements the sso_server.auth.AuthBase interface.
ALLOWED_SERVICES
A list of regular expression patterns defining the services
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment