mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-22 23:23:30 -06:00
Passkey: add support for discoverable credentials
Apart from server-side credentials passkey should also register discoverable credentials. ipa user-add-passkey --register now supports an additional option, --cred-type server-side|discoverable that is propagated to passkey_child command. Signed-off-by: Florence Blanc-Renaud <flo@redhat.com> Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
This commit is contained in:
parent
56e179748b
commit
6f0da62f5a
@ -60,6 +60,8 @@ During the registration process, it is possible to specify
|
||||
the authentication will force to execute the user verification check even if
|
||||
the passkey settings do not set this flag. If credentials are registered without
|
||||
the flag, the global passkey settings apply.
|
||||
- credential type: `server-side` or `discoverable`
|
||||
Discoverable credentials do not require to first identify the user.
|
||||
|
||||
When the passkey credential is registered, a relaying party (RP) is set to be
|
||||
the IPA domain (e.g. ipa.test). While using a domain-wide relaying party
|
||||
|
@ -34,6 +34,12 @@ class baseuser_add_passkey(MethodOverride):
|
||||
doc=_('COSE type to use for registration'),
|
||||
values=('es256', 'rs256', 'eddsa'),
|
||||
),
|
||||
StrEnum(
|
||||
'credtype?',
|
||||
cli_name="cred_type",
|
||||
doc=_('Credential type'),
|
||||
values=('server-side', 'discoverable'),
|
||||
),
|
||||
)
|
||||
|
||||
def get_args(self):
|
||||
@ -69,6 +75,7 @@ class baseuser_add_passkey(MethodOverride):
|
||||
options.pop('register')
|
||||
cosetype = options.pop('cosetype', None)
|
||||
require_verif = options.pop('require_user_verification', None)
|
||||
credtype = options.pop('credtype', None)
|
||||
cmd = [paths.PASSKEY_CHILD, "--register",
|
||||
"--domain", self.api.env.domain,
|
||||
"--username", args[0]]
|
||||
@ -78,6 +85,9 @@ class baseuser_add_passkey(MethodOverride):
|
||||
if require_verif is not None:
|
||||
cmd.append("--user-verification")
|
||||
cmd.append(str(require_verif).lower())
|
||||
if credtype:
|
||||
cmd.append("--cred-type")
|
||||
cmd.append(credtype)
|
||||
|
||||
logger.debug("Executing command: %s", cmd)
|
||||
subp = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||
|
@ -167,14 +167,15 @@ def validate_passkey(ugettext, key):
|
||||
|
||||
The expected format is passkey:<key id>,<pubkey>
|
||||
"""
|
||||
pattern = re.compile(r'passkey:(?P<id>.*),(?P<pkey>.*)')
|
||||
pattern = re.compile(
|
||||
r'^passkey:(?P<id>[^,]*),(?P<pkey>[^,]*),?(?P<userid>.*)$')
|
||||
result = re.match(pattern, key)
|
||||
if result is None:
|
||||
return '"%s" is not a valid passkey mapping' % key
|
||||
|
||||
# Validate the id part
|
||||
try:
|
||||
base64.b64decode(result.group('id'))
|
||||
base64.b64decode(result.group('id'), validate=True)
|
||||
except Exception:
|
||||
return '"%s" is not a valid passkey mapping, invalid id' % key
|
||||
|
||||
@ -187,6 +188,13 @@ def validate_passkey(ugettext, key):
|
||||
backend=default_backend())
|
||||
except ValueError:
|
||||
return '"%s" is not a valid passkey mapping, invalid key' % key
|
||||
# Validate the (optional) userid
|
||||
try:
|
||||
userid = result.group('userid')
|
||||
if userid:
|
||||
base64.b64decode(userid, validate=True)
|
||||
except Exception:
|
||||
return '"%s" is not a valid passkey mapping, invalid userid' % key
|
||||
return None
|
||||
|
||||
|
||||
|
@ -55,6 +55,12 @@ PASSKEY_KEY = ("passkey:"
|
||||
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgryfr3YR"
|
||||
"M9OVdWHEDrbvcSyT5D0b/8Ks+fMp8MM0BXV/FOo436ZP"
|
||||
"jUqSU+2LOXVGdKkJU1XBiwl+n/X+vGD1vw==")
|
||||
PASSKEY_DISCOVERABLEKEY = (
|
||||
"passkey:"
|
||||
"pP2z07ygq36HkNabd79ki9H6rfYEIVdluSHjY1YykUbVECXJ3ZDZ3n1EZ9G8HhMv,"
|
||||
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpP2z07ygq36HkNabd1H9Knqqghjv"
|
||||
"vhlW0+FcNzOoXP+49tC/Ee2TbjC3x2dIzJEBFi7iDPSc+OCM+WmD1AfPLQ==,"
|
||||
"P6GjSqAo+RoQRJhGFA3lKcvtpKTGETjCdtVIyLX0KcY=")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -64,15 +70,17 @@ def passkeyuser(request):
|
||||
|
||||
|
||||
class TestAddRemovePasskey(XMLRPC_test):
|
||||
def test_add_passkey(self, passkeyuser):
|
||||
@pytest.mark.parametrize("key", [PASSKEY_KEY, PASSKEY_DISCOVERABLEKEY])
|
||||
def test_add_passkey(self, passkeyuser,key):
|
||||
passkeyuser.ensure_exists()
|
||||
passkeyuser.add_passkey(ipapasskey=PASSKEY_KEY)
|
||||
passkeyuser.add_passkey(ipapasskey=key)
|
||||
passkeyuser.ensure_missing()
|
||||
|
||||
def test_remove_passkey(self, passkeyuser):
|
||||
@pytest.mark.parametrize("key", [PASSKEY_KEY, PASSKEY_DISCOVERABLEKEY])
|
||||
def test_remove_passkey(self, passkeyuser, key):
|
||||
passkeyuser.ensure_exists()
|
||||
passkeyuser.add_passkey(ipapasskey=PASSKEY_KEY)
|
||||
passkeyuser.remove_passkey(ipapasskey=PASSKEY_KEY)
|
||||
passkeyuser.add_passkey(ipapasskey=key)
|
||||
passkeyuser.remove_passkey(ipapasskey=key)
|
||||
|
||||
@pytest.mark.parametrize("key", ['wrongval', 'passkey:123', 'passkey,123'])
|
||||
def test_add_passkey_invalid(self, passkeyuser, key):
|
||||
@ -112,6 +120,23 @@ class TestAddRemovePasskey(XMLRPC_test):
|
||||
error=msg.format(key))):
|
||||
cmd(key)
|
||||
|
||||
def test_add_passkey_invaliduserid(self, passkeyuser):
|
||||
passkeyuser.ensure_exists()
|
||||
key = ("passkey:"
|
||||
"E8Zay6UJm6PG/GcQnej2WMyUrWqijejBCqPWFX6THPrxab01Z59bUguti"
|
||||
"pn5MIk8/zMU6RBlp7jSbkNJsZtomw==,"
|
||||
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgryfr3YRM9OVdWHEDrbvc"
|
||||
"SyT5D0b/8Ks+fMp8MM0BXV/FOo436ZPjUqSU+2LOXVGdKkJU1XBiwl+n/X"
|
||||
"+vGD1vw==,"
|
||||
"wrongid")
|
||||
msg = '"{}" is not a valid passkey mapping, invalid userid'
|
||||
cmd = passkeyuser.make_command('user_add_passkey',
|
||||
passkeyuser.name)
|
||||
with raises_exact(errors.ValidationError(
|
||||
name='passkey',
|
||||
error=msg.format(key))):
|
||||
cmd(key)
|
||||
|
||||
|
||||
STAGEPASSKEY_USER = 'stagepasskeyuser'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user