Password now hashed in app.
This commit is contained in:
parent
2b46eb0353
commit
79f682cbb7
|
@ -22,11 +22,13 @@ from phi.async_ldap.model import (
|
||||||
Service,
|
Service,
|
||||||
create_new_,
|
create_new_,
|
||||||
User,
|
User,
|
||||||
PhiUserExists,
|
PhiEntryExists,
|
||||||
PhiUserDoesNotExist,
|
PhiEntryDoesNotExist,
|
||||||
PhiAttributeMissing,
|
PhiAttributeMissing,
|
||||||
|
PhiUnauthorized,
|
||||||
Group,
|
Group,
|
||||||
)
|
)
|
||||||
|
from phi.security import hash_pass
|
||||||
|
|
||||||
|
|
||||||
BASE_DN = "dc=test,dc=abbiamoundominio,dc=org"
|
BASE_DN = "dc=test,dc=abbiamoundominio,dc=org"
|
||||||
|
@ -41,7 +43,17 @@ EXISTING_USER = [
|
||||||
"sn": ["Existing User"],
|
"sn": ["Existing User"],
|
||||||
"mail": ["existing@mail.org"],
|
"mail": ["existing@mail.org"],
|
||||||
"uid": ["existing_user"],
|
"uid": ["existing_user"],
|
||||||
"userPassword": ["{SHA}oLY7P6V+DWAMJ9ix7vbMYGIfA+E="],
|
"userPassword": ["{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g="],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
MISSING_AUTH_USER = [
|
||||||
|
{
|
||||||
|
"dn": f"uid=existing_user,ou=Hackers,{BASE_DN}>",
|
||||||
|
"objectClass": ["inetOrgPerson", "organizationalPerson", "person", "top"],
|
||||||
|
"cn": ["exists"],
|
||||||
|
"sn": ["Existing User"],
|
||||||
|
"mail": ["existing@mail.org"],
|
||||||
|
"uid": ["existing_user"],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -51,6 +63,7 @@ class MockClient:
|
||||||
self._base_dn = base_dn
|
self._base_dn = base_dn
|
||||||
self.args = args
|
self.args = args
|
||||||
self.called_with_args = {}
|
self.called_with_args = {}
|
||||||
|
self.username = f"uid=mock_connection,ou=Services,{base_dn}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def base_dn(self):
|
def base_dn(self):
|
||||||
|
@ -72,6 +85,8 @@ class MockClient:
|
||||||
return EXISTING_USER
|
return EXISTING_USER
|
||||||
elif f"uid=not_existing,ou=Hackers,{BASE_DN}" in args:
|
elif f"uid=not_existing,ou=Hackers,{BASE_DN}" in args:
|
||||||
return []
|
return []
|
||||||
|
elif f"uid=missing_auth_user,ou=Hackers,{BASE_DN}" in args:
|
||||||
|
return MISSING_AUTH_USER
|
||||||
|
|
||||||
conn.search = _search
|
conn.search = _search
|
||||||
|
|
||||||
|
@ -446,15 +461,19 @@ def test_User_singleton(client_fixture):
|
||||||
async def test_User_create_existing(client_fixture):
|
async def test_User_create_existing(client_fixture):
|
||||||
c = User("existing_user", client_fixture)
|
c = User("existing_user", client_fixture)
|
||||||
|
|
||||||
with pytest.raises(PhiUserExists):
|
with pytest.raises(PhiEntryExists) as e:
|
||||||
await c.create("existing@mail.org", sn="exists", cn="Existing User")
|
await c.create(
|
||||||
|
"existing@mail.org", password="password", sn="exists", cn="Existing User"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert c.dn in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_User_create_not_existing(client_fixture):
|
async def test_User_create_not_existing(client_fixture):
|
||||||
c = User("not_existing", client_fixture)
|
c = User("not_existing", client_fixture)
|
||||||
|
|
||||||
await c.create("not@existing.org")
|
await c.create("not@existing.org", "password")
|
||||||
|
|
||||||
assert client_fixture.called_with_args["search"]["args"] == (c.dn, 0)
|
assert client_fixture.called_with_args["search"]["args"] == (c.dn, 0)
|
||||||
assert c._entry["mail"][0] == "not@existing.org"
|
assert c._entry["mail"][0] == "not@existing.org"
|
||||||
|
@ -477,9 +496,10 @@ async def test_User_sync_existing(client_fixture, caplog):
|
||||||
async def test_User_sync_not_existing(client_fixture, caplog):
|
async def test_User_sync_not_existing(client_fixture, caplog):
|
||||||
c = User("not_existing", client_fixture)
|
c = User("not_existing", client_fixture)
|
||||||
|
|
||||||
with pytest.raises(PhiUserDoesNotExist):
|
with pytest.raises(PhiEntryDoesNotExist) as e:
|
||||||
await c.sync()
|
await c.sync()
|
||||||
|
|
||||||
|
assert c.dn in str(e.value)
|
||||||
assert client_fixture.called_with_args["search"]["args"] == (c.dn, 0)
|
assert client_fixture.called_with_args["search"]["args"] == (c.dn, 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ cn: Guido
|
||||||
sn: Necchi
|
sn: Necchi
|
||||||
mail: gnecchi@autistici.org
|
mail: gnecchi@autistici.org
|
||||||
uid: necchi
|
uid: necchi
|
||||||
userPassword: {SHA}IZiNWojlNxrWIkk23XDh0svKY14=
|
userPassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
|
||||||
|
|
||||||
dn: uid=perozzi,ou=Hackers,dc=unit,dc=macaomilano,dc=org
|
dn: uid=perozzi,ou=Hackers,dc=unit,dc=macaomilano,dc=org
|
||||||
objectClass: inetOrgPerson
|
objectClass: inetOrgPerson
|
||||||
|
|
32
setup.py
32
setup.py
|
@ -2,21 +2,25 @@ from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='phi',
|
name="phi",
|
||||||
version='0.0.1',
|
version="0.0.1",
|
||||||
|
description="Post-Human Interface",
|
||||||
description='Post-Human Interface',
|
|
||||||
# license='',
|
# license='',
|
||||||
url='https://git.abbiamoundominio.org/unit/phi',
|
url="https://git.abbiamoundominio.org/unit/phi",
|
||||||
|
author="unit",
|
||||||
author='unit',
|
author_email="unit@paranoici.org",
|
||||||
author_email='unit@paranoici.org',
|
package_dir={"": "src"},
|
||||||
|
|
||||||
package_dir={'': 'src'},
|
|
||||||
packages=find_packages("src"),
|
packages=find_packages("src"),
|
||||||
entry_points={"console_scripts": ["phid=phi.app:cli"]},
|
entry_points={"console_scripts": ["phid=phi.app:cli"]},
|
||||||
|
setup_requires=["pytest-runner"],
|
||||||
setup_requires=['pytest-runner'],
|
install_requires=[
|
||||||
install_requires=['aiohttp==2.3.8', 'click==7.0', 'pyYAML', 'ldap3', 'bonsai==1.1.0'],
|
"aiohttp==2.3.8",
|
||||||
tests_require=['pytest', 'pytest-aiohttp', 'pytest-asyncio', 'async-generator']
|
"click==7.0",
|
||||||
|
"pyYAML",
|
||||||
|
"ldap3",
|
||||||
|
"bonsai==1.1.0",
|
||||||
|
"passlib==1.7.1",
|
||||||
|
"bcrypt==3.1.7",
|
||||||
|
],
|
||||||
|
tests_require=["pytest", "pytest-aiohttp", "pytest-asyncio", "async-generator"],
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import typing as T
|
||||||
from bonsai import LDAPEntry, LDAPModOp, NoSuchObjectError
|
from bonsai import LDAPEntry, LDAPModOp, NoSuchObjectError
|
||||||
|
|
||||||
from phi.logging import get_logger
|
from phi.logging import get_logger
|
||||||
|
from phi.security import hash_pass
|
||||||
|
|
||||||
|
|
||||||
log = get_logger(__name__)
|
log = get_logger(__name__)
|
||||||
|
@ -253,14 +254,17 @@ class User(Hackers):
|
||||||
def name(self, name):
|
def name(self, name):
|
||||||
raise RuntimeError("Name property is not modifiable.")
|
raise RuntimeError("Name property is not modifiable.")
|
||||||
|
|
||||||
async def create(self, mail, sn=None, cn=None):
|
async def create(self, mail, password=None, sn=None, cn=None):
|
||||||
async with self.client.connect(is_async=True) as conn:
|
async with self.client.connect(is_async=True) as conn:
|
||||||
res = await conn.search(self.dn, 0)
|
res = await conn.search(self.dn, 0)
|
||||||
if len(res) > 0:
|
if len(res) > 0:
|
||||||
raise PhiEntryExists(self.dn)
|
raise PhiEntryExists(self.dn)
|
||||||
_sn = sn if sn is not None else self.name
|
_sn = sn if sn is not None else self.name
|
||||||
_cn = cn if cn is not None else self.name
|
_cn = cn if cn is not None else self.name
|
||||||
self._entry = await create_new_(self, uid=self.name, mail=mail, sn=_sn, cn=_cn)
|
hashed = hash_pass(password)
|
||||||
|
self._entry = await create_new_(
|
||||||
|
self, uid=self.name, mail=mail, sn=_sn, cn=_cn, userPassword=hashed
|
||||||
|
)
|
||||||
|
|
||||||
async def sync(self):
|
async def sync(self):
|
||||||
async with self.client.connect(is_async=True) as conn:
|
async with self.client.connect(is_async=True) as conn:
|
||||||
|
|
23
src/phi/security.py
Normal file
23
src/phi/security.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
from passlib.hash import (
|
||||||
|
ldap_sha1,
|
||||||
|
ldap_bcrypt,
|
||||||
|
ldap_sha256_crypt,
|
||||||
|
ldap_sha512_crypt,
|
||||||
|
ldap_pbkdf2_sha256,
|
||||||
|
ldap_pbkdf2_sha512,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
HASH_CALLABLE = {
|
||||||
|
"sha1": ldap_sha1.hash,
|
||||||
|
"bcrypt": ldap_bcrypt.hash,
|
||||||
|
"sha256_crypt": ldap_sha256_crypt.hash,
|
||||||
|
"sha512_crypt": ldap_sha512_crypt.hash,
|
||||||
|
"pbkdf2_sha256": ldap_pbkdf2_sha256.hash,
|
||||||
|
"pbkdf2_sha512": ldap_pbkdf2_sha512.hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def hash_pass(password, method="sha1"):
|
||||||
|
return HASH_CALLABLE[method](password)
|
Loading…
Reference in New Issue
Block a user