-
Notifications
You must be signed in to change notification settings - Fork 64
/
Copy pathauth.py
130 lines (111 loc) · 4.35 KB
/
auth.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# -*- encoding: UTF-8 -*-
#
# Form based authentication for CherryPy. Requires the
# Session tool to be loaded.
#
import cherrypy
SESSION_KEY = '_cp_username'
def check_credentials(username, password):
"""Verifies credentials for username and password.
Returns None on success or a string describing the error on failure"""
from spwd import getspnam
from crypt import crypt
try:
enc_pwd = getspnam(username)[1]
except KeyError:
raise OSError("user '%s' not found" % username)
else:
if enc_pwd in ['NP', '!', '', None]:
raise OSError("user '%s' has no password set" % username)
elif enc_pwd in ['LK', '*']:
raise OSError('account is locked')
elif enc_pwd == "!!":
raise OSError('password is expired')
if crypt(password, enc_pwd) == enc_pwd:
return True
else:
raise OSError('incorrect password')
def unix_authenticate(username, password):
"""Fallback authentication for BSD"""
from crypt import crypt
from pwd import getpwnam
cryptedpasswd = getpwnam(username)[1]
if cryptedpasswd:
if cryptedpasswd == 'x' or cryptedpasswd == '*':
raise NotImplementedError("Shadow passwords not supported")
return crypt(password, cryptedpasswd) == cryptedpasswd
else:
return False
def check_auth(*args, **kwargs):
"""A tool that looks in config for 'auth.require'. If found and it
is not None, a login is required and the entry is evaluated as a list of
conditions that the user must fulfill"""
conditions = cherrypy.request.config.get('auth.require', None)
if conditions is not None:
username = cherrypy.session.get(SESSION_KEY)
if username:
cherrypy.request.login = username
for condition in conditions:
# A condition is just a callable that returns true or false
if not condition():
raise cherrypy.HTTPRedirect("/auth/login")
else:
raise cherrypy.HTTPRedirect("/auth/login")
cherrypy.tools.auth = cherrypy.Tool('before_handler', check_auth)
def require(*conditions):
"""A decorator that appends conditions to the auth.require config
variable."""
def decorate(f):
if not hasattr(f, '_cp_config'):
f._cp_config = dict()
if 'auth.require' not in f._cp_config:
f._cp_config['auth.require'] = []
f._cp_config['auth.require'].extend(conditions)
return f
return decorate
# Controller to provide login and logout actions
class AuthController(object):
def __init__(self):
self.html_directory = cherrypy.config['misc.html_directory']
def on_login(self, username):
"""Called on successful login"""
def on_logout(self, username):
"""Called on logout"""
def get_loginform(self):
import os
from cherrypy.lib.static import serve_file
try:
return serve_file(os.path.join(self.html_directory,
'login_%s.html' % cherrypy.config['misc.localization']))
except cherrypy.NotFound:
return serve_file(os.path.join(self.html_directory,
'login_en.html'))
@cherrypy.expose
def login(self, username=None, password=None, hide=None, from_page='/'):
if not username or not password:
return self.get_loginform()
validated = False
try:
validated = check_credentials(username, password)
except OSError:
import pam
validated = pam.authenticate(username, password)
except ImportError:
validated = unix_authenticate(username, password)
finally:
if validated:
cherrypy.session.regenerate()
cherrypy.session[SESSION_KEY] = cherrypy.request.login = username
self.on_login(username)
raise cherrypy.HTTPRedirect("/")
else:
return self.get_loginform()
@cherrypy.expose
def logout(self):
sess = cherrypy.session
username = sess.get(SESSION_KEY, None)
sess[SESSION_KEY] = None
if username:
cherrypy.request.login = None
self.on_logout(username)
raise cherrypy.HTTPRedirect("/index")