added mfaData to admin

This commit is contained in:
Adolfo Gómez García 2022-07-04 21:29:41 +02:00
parent 8783db925f
commit 1c65722d24
9 changed files with 73 additions and 27 deletions

View File

@ -80,21 +80,21 @@ class Users(DetailHandler):
custom_methods = ['servicesPools', 'userServices']
@staticmethod
def uuid_to_id(iterator):
for v in iterator:
v['id'] = v['uuid']
del v['uuid']
yield v
def getItems(self, parent: Authenticator, item: typing.Optional[str]):
# processes item to change uuid key for id
def uuid_to_id(iterable: typing.Iterable[typing.MutableMapping[str, typing.Any]]):
for v in iterable:
v['id'] = v['uuid']
del v['uuid']
yield v
logger.debug(item)
# Extract authenticator
try:
if item is None:
values = list(
Users.uuid_to_id(
parent.users.all().values(
uuid_to_id(
(i for i in parent.users.all().values(
'uuid',
'name',
'real_name',
@ -104,7 +104,8 @@ class Users(DetailHandler):
'is_admin',
'last_access',
'parent',
)
'mfaData',
))
)
)
for res in values:
@ -127,6 +128,7 @@ class Users(DetailHandler):
'is_admin',
'last_access',
'parent',
'mfaData',
),
)
res['id'] = u.uuid
@ -153,7 +155,7 @@ class Users(DetailHandler):
except Exception:
return _('Current users')
def getFields(self, parent):
def getFields(self, parent: Authenticator):
return [
{
'name': {
@ -198,13 +200,17 @@ class Users(DetailHandler):
'staff_member',
'is_admin',
]
if self._params.get('name', '') == '':
if self._params.get('name', '').strip() == '':
raise RequestError(_('Username cannot be empty'))
if 'password' in self._params:
valid_fields.append('password')
self._params['password'] = cryptoManager().hash(self._params['password'])
if 'mfaData' in self._params:
valid_fields.append('mfaData')
self._params['mfaData'] = self._params['mfaData'].strip()
fields = self.readFieldsFromParams(valid_fields)
if not self._user.is_admin:
del fields['staff_member']
@ -224,9 +230,8 @@ class Users(DetailHandler):
user.__dict__.update(fields)
logger.debug('User parent: %s', user.parent)
if auth.isExternalSource is False and (
user.parent is None or user.parent == ''
):
# If internal auth, threat it "special"
if auth.isExternalSource is False and not user.parent:
groups = self.readFieldsFromParams(['groups'])['groups']
logger.debug('Groups: %s', groups)
logger.debug('Got Groups %s', parent.groups.filter(uuid__in=groups))
@ -414,7 +419,7 @@ class Groups(DetailHandler):
except Exception:
raise self.invalidRequestException()
def saveItem(self, parent: Authenticator, item) -> None:
def saveItem(self, parent: Authenticator, item: typing.Optional[str]) -> None:
group = None # Avoid warning on reference before assignment
try:
is_meta = self._params['type'] == 'meta'
@ -429,7 +434,7 @@ class Groups(DetailHandler):
fields = self.readFieldsFromParams(valid_fields)
is_pattern = fields.get('name', '').find('pat:') == 0
auth = parent.getInstance()
if item is None: # Create new
if not item: # Create new
if not is_meta and not is_pattern:
auth.createGroup(
fields
@ -482,7 +487,9 @@ class Groups(DetailHandler):
except Exception:
raise self.invalidItemException()
def servicesPools(self, parent: Authenticator, item: str) -> typing.List[typing.Mapping[str, typing.Any]]:
def servicesPools(
self, parent: Authenticator, item: str
) -> typing.List[typing.Mapping[str, typing.Any]]:
uuid = processUuid(item)
group = parent.groups.get(uuid=processUuid(uuid))
res: typing.List[typing.Mapping[str, typing.Any]] = []
@ -503,7 +510,9 @@ class Groups(DetailHandler):
return res
def users(self, parent: Authenticator, item: str) -> typing.List[typing.Mapping[str, typing.Any]]:
def users(
self, parent: Authenticator, item: str
) -> typing.List[typing.Mapping[str, typing.Any]]:
uuid = processUuid(item)
group = parent.groups.get(uuid=processUuid(uuid))

View File

@ -99,7 +99,7 @@ class InternalDBAuth(auths.Authenticator):
if self.reverseDns.isTrue():
try:
return str(
dns.resolver.query(dns.reversename.from_address(ip), 'PTR')[0]
dns.resolver.query(dns.reversename.from_address(ip).to_text(), 'PTR')[0]
)
except Exception:
pass

View File

@ -178,6 +178,9 @@ class StorageAsDict(MutableMapping):
def get(self, key: str, default: typing.Any = None) -> typing.Any:
return self[key] or default
def delete(self, key: str) -> None:
self.__delitem__(key)
# Custom utility methods
@property
def group(self) -> str:

View File

@ -1,4 +1,4 @@
# Generated by Django 3.2.10 on 2022-06-23 19:34
# Generated by Django 3.2.10 on 2022-07-04 21:20
from django.db import migrations, models
import django.db.models.deletion
@ -11,6 +11,11 @@ class Migration(migrations.Migration):
]
operations = [
migrations.AddField(
model_name='user',
name='mfaData',
field=models.CharField(default='', max_length=128),
),
migrations.CreateModel(
name='MFA',
fields=[

View File

@ -37,6 +37,7 @@ from django.db import models
from django.db.models import signals, Q, Count
from uds.core.util import log
from uds.core.util import storage
from .authenticator import Authenticator
from .util import UnsavedForeignKey
@ -67,6 +68,9 @@ class User(UUIDModel):
state = models.CharField(max_length=1, db_index=True)
password = models.CharField(
max_length=128, default=''
) # Only used on "internal" sources or sources that "needs password"
mfaData = models.CharField(
max_length=128, default=''
) # Only used on "internal" sources
staff_member = models.BooleanField(
default=False
@ -202,6 +206,26 @@ class User(UUIDModel):
# This group matches
yield g
# Get custom data
def getCustomData(self, key: str) -> typing.Optional[str]:
"""
Returns the custom data for this user for the provided key.
Usually custom data will be associated with transports, but can be custom data registered by ANY module.
Args:
key: key of the custom data to get
Returns:
The custom data for the key specified as a string (can be empty if key is not found).
If the key exists, the custom data will always contain something, but may be the values are the default ones.
"""
with storage.StorageAccess('manager' + self.manager.uuid) as store:
return store[self.uuid + '_' + key]
def __str__(self):
return 'User {} (id:{}) from auth {}'.format(
self.name, self.id, self.manager.name
@ -217,11 +241,15 @@ class User(UUIDModel):
:note: If destroy raises an exception, the deletion is not taken.
"""
toDelete = kwargs['instance']
toDelete: User = kwargs['instance']
# first, we invoke removeUser. If this raises an exception, user will not
# be removed
toDelete.getManager().removeUser(toDelete.name)
# Remove related stored values
with storage.StorageAccess('manager' + toDelete.manager.uuid) as store:
for key in store.keys():
store.delete(key)
# now removes all "child" of this user, if it has children
User.objects.filter(parent=toDelete.id).delete()

View File

@ -98,7 +98,7 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
state_date = models.DateTimeField(db_index=True)
creation_date = models.DateTimeField(db_index=True)
data = models.TextField(default='')
user: 'models.ForeignKey[UserService, User]' = models.ForeignKey(
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='userServices',
@ -394,7 +394,7 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
self.os_state = state
self.save(update_fields=['os_state', 'state_date'])
def assignToUser(self, user: User) -> None:
def assignToUser(self, user: typing.Optional[User]) -> None:
"""
Assigns this user deployed service to an user.
@ -403,7 +403,7 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
"""
self.cache_level = 0
self.state_date = getSqlDatetime()
self.user = user # type: ignore
self.user = user
self.save(update_fields=['cache_level', 'state_date', 'user'])
def setInUse(self, inUse: bool) -> None:

File diff suppressed because one or more lines are too long

View File

@ -487,6 +487,7 @@ gettext("Role");
gettext("Admin");
gettext("Staff member");
gettext("User");
gettext("MFA");
gettext("Groups");
gettext("Cancel");
gettext("Ok");

View File

@ -99,7 +99,7 @@
</svg>
</div>
</uds-root>
<script src="/uds/res/admin/runtime.js?stamp=1656004218" defer></script><script src="/uds/res/admin/polyfills-es5.js?stamp=1656004218" nomodule defer></script><script src="/uds/res/admin/polyfills.js?stamp=1656004218" defer></script><script src="/uds/res/admin/main.js?stamp=1656004218" defer></script>
<script src="/uds/res/admin/runtime.js?stamp=1656962877" defer></script><script src="/uds/res/admin/polyfills-es5.js?stamp=1656962877" nomodule defer></script><script src="/uds/res/admin/polyfills.js?stamp=1656962877" defer></script><script src="/uds/res/admin/main.js?stamp=1656962877" defer></script>
</body></html>