1 # This file is part of Indico.
2 # Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 # Indico is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3 of the
7 # License, or (at your option) any later version.
9 # Indico is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Indico; if not, see <http://www.gnu.org/licenses/>.
17 from __future__
import unicode_literals
22 class BCryptPassword(object):
23 def __init__(self
, hash_
):
24 self
.hash = str(hash_
)
26 def __eq__(self
, value
):
27 if not self
.hash or not value
:
28 # For security reasons we never consider an empty password/hash valid
30 if isinstance(value
, unicode):
31 value
= value
.encode('utf-8')
32 return self
.hash == bcrypt
.hashpw(value
, self
.hash)
34 def __ne__(self
, other
):
35 return not (self
== other
)
37 def __hash__(self
): # pragma: no cover
38 return hash(self
.hash)
41 return '<BCryptPassword({})>'.format(self
.hash)
45 if isinstance(value
, unicode):
46 value
= value
.encode('utf-8')
47 return bcrypt
.hashpw(value
, bcrypt
.gensalt())
50 class PasswordProperty(object):
51 """Defines a hashed password property.
53 When reading this property, it will return an object which will
54 let you use the ``==`` operator to compare the password against
55 a plaintext password. When assigning a value to it, it will be
56 hashed and stored in :attr:`attr` of the containing object.
58 :param attr: The attribute of the containing object where the
59 password hash is stored.
60 :param backend: The password backend that handles hashing/checking
64 def __init__(self
, attr
, backend
=BCryptPassword
):
66 self
.backend
= backend
68 def __get__(self
, instance
, owner
):
69 return self
.backend(getattr(instance
, self
.attr
, None))
71 def __set__(self
, instance
, value
):
73 raise ValueError('Password may not be empty')
74 setattr(instance
, self
.attr
, self
.backend
.hash(value
))
76 def __delete__(self
, instance
):
77 setattr(instance
, self
.attr
, None)