Skip to content
Permalink
Browse files

Merge pull request #1966 from privacyidea/1375/limit_tokentypes

Add tokentype specifc limits
  • Loading branch information
cornelinux committed Dec 19, 2019
2 parents 562c8b1 + 0bdf8f2 commit 5c265bba65258a24abde5c918ea4396ba41fca79
@@ -41,6 +41,10 @@ type: int

Limit the maximum number of tokens per user in this realm.

There are also token type specific policies to limit the
number of tokens of a specific token type, that a user is
allowed to have assigned.

.. note:: If you do not set this action, a user may have
unlimited tokens assigned.

@@ -55,6 +59,10 @@ type: int

Limit the maximum number of active tokens per user.

There are also token type specific policies to limit the
number of tokens of a specific token type, that a user is
allowed to have assigned.

.. note:: Inactive tokens will not be taken into account.
If the token already exists, it can be recreated if the token
is already active.
@@ -63,15 +63,13 @@
The functions of this module are tested in tests/test_api_lib_policy.py
"""
import logging

log = logging.getLogger(__name__)
from privacyidea.lib.error import PolicyError, RegistrationError, TokenAdminError
from flask import g, current_app
from privacyidea.lib.policy import SCOPE, ACTION, PolicyClass
from privacyidea.lib.policy import Match
from privacyidea.lib.user import (get_user_from_param, get_default_realm,
split_user, User)
from privacyidea.lib.token import (get_tokens, get_realms_of_token)
from privacyidea.lib.token import (get_tokens, get_realms_of_token, get_token_type)
from privacyidea.lib.utils import (get_client_ip,
parse_timedelta, is_true, check_pin_policy, get_module_class)
from privacyidea.lib.crypto import generate_password
@@ -88,6 +86,8 @@
from privacyidea.lib.tokens.u2f import x509name_to_string
from privacyidea.lib.tokens.pushtoken import PUSH_ACTION

log = logging.getLogger(__name__)

optional = True
required = False

@@ -629,11 +629,40 @@ def check_max_token_user(request=None, action=None):
:return: True otherwise raises an Exception
"""
ERROR = "The number of tokens for this user is limited!"
ERROR_TYPE = "The number of tokens of type {0!s} for this user is limited!"
ERROR_ACTIVE = "The number of active tokens for this user is limited!"
ERROR_ACTIVE_TYPE = "The number of active tokens of type {0!s} for this user is limited!"
params = request.all_data
user_object = get_user_from_param(params)
serial = getParam(params, "serial")
if user_object.login:
serial = getParam(params, "serial")
tokentype = getParam(params, "type")
if not tokentype:
if serial:
# If we have a serial but no tokentype, we can get the tokentype from
# the token, if it exists
tokentype = get_token_type(serial) or "hotp"
else:
tokentype = "hotp"

# check maximum number of type specific tokens of user
limit_list = Match.user(g, scope=SCOPE.ENROLL,
action="{0!s}_{1!s}".format(tokentype.lower(), ACTION.MAXTOKENUSER),
user_object=user_object).action_values(unique=False, write_to_audit_log=False)
if limit_list:
# we need to check how many tokens of this specific type the user already has assigned!
tokenobject_list = get_tokens(user=user_object, tokentype=tokentype)
if serial and serial in [tok.token.serial for tok in tokenobject_list]:
# If a serial is provided and this token already exists, the
# token can be regenerated
pass
else:
already_assigned_tokens = len(tokenobject_list)
max_value = max([int(x) for x in limit_list])
if already_assigned_tokens >= max_value:
g.audit_object.add_policy(limit_list.get(str(max_value)))
raise PolicyError(ERROR_TYPE.format(tokentype))

# check maximum tokens of user
limit_list = Match.user(g, scope=SCOPE.ENROLL, action=ACTION.MAXTOKENUSER,
user_object=user_object).action_values(unique=False, write_to_audit_log=False)
@@ -651,21 +680,36 @@ def check_max_token_user(request=None, action=None):
g.audit_object.add_policy(limit_list.get(str(max_value)))
raise PolicyError(ERROR)

# check maximum active tokens of user
limit_list = Match.user(g, scope=SCOPE.ENROLL,
action="{0!s}_{1!s}".format(tokentype, ACTION.MAXACTIVETOKENUSER),
user_object=user_object).action_values(unique=False, write_to_audit_log=False)
if limit_list:
# we need to check how many active tokens the user already has assigned!
tokenobject_list = get_tokens(user=user_object, active=True, tokentype=tokentype)
if serial and serial in [tok.token.serial for tok in tokenobject_list]:
# If a serial is provided and this token already exists, the
# token can be regenerated
pass
else:
already_assigned_tokens = len(tokenobject_list)
max_value = max([int(x) for x in limit_list])
if already_assigned_tokens >= max_value:
g.audit_object.add_policy(limit_list.get(str(max_value)))
raise PolicyError(ERROR_ACTIVE_TYPE.format(tokentype))

# check maximum active tokens of user
limit_list = Match.user(g, scope=SCOPE.ENROLL, action=ACTION.MAXACTIVETOKENUSER,
user_object=user_object).action_values(unique=False, write_to_audit_log=False)
if limit_list:
# we need to check how many active tokens the user already has assigned!
tokenobject_list = get_tokens(user=user_object, active=True)
_token_allowed = False
if serial:
for tok in tokenobject_list:
if tok.token.serial == serial:
# If a serial is provided and this token already exists (and is active), the
# token can be regenerated. If the token would be inactive, regenerating this
# token would reactivate it and thus the user would have more tokens!
_token_allowed = True
if not _token_allowed:
if serial and serial in [tok.token.serial for tok in tokenobject_list]:
# If a serial is provided and this token already exists (and is active), the
# token can be regenerated. If the token would be inactive, regenerating this
# token would reactivate it and thus the user would have more tokens!
pass
else:
already_assigned_tokens = len(tokenobject_list)
max_value = max([int(x) for x in limit_list])
if already_assigned_tokens >= max_value:
@@ -42,6 +42,7 @@
from OpenSSL import crypto
from privacyidea.lib.decorators import check_token_locked
from privacyidea.lib import _
from privacyidea.lib.policy import SCOPE, ACTION, GROUP

optional = True
required = False
@@ -156,9 +157,21 @@ def get_class_info(key=None, ret='all'):
'user': ['enroll'],
# This tokentype is enrollable in the UI for...
'ui_enroll': ["admin", "user"],
'policy': {},
'policy': {
SCOPE.ENROLL: {
ACTION.MAXTOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of certificates assigned."),
'group': GROUP.TOKEN
},
ACTION.MAXACTIVETOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of active certificates assigned."),
'group': GROUP.TOKEN
}
}
}
}

if key:
ret = res.get(key, {})
else:
@@ -42,6 +42,7 @@
from privacyidea.lib.decorators import check_token_locked
from privacyidea.lib.utils import to_bytes, to_unicode
from privacyidea.lib import _
from privacyidea.lib.policy import SCOPE, ACTION, GROUP
optional = True
required = False

@@ -119,6 +120,19 @@ def get_class_info(key=None, ret='all'):
'title': 'Daplug Event Token',
'description': _("event based OTP token using "
"the HOTP algorithm"),
'policy': {
SCOPE.ENROLL: {
ACTION.MAXTOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of daplug tokens assigned."),
'group': GROUP.TOKEN
},
ACTION.MAXACTIVETOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of active daplug tokens assigned."),
'group': GROUP.TOKEN
}
}}
}

if key:
@@ -72,7 +72,7 @@
from privacyidea.lib.config import get_from_config
from privacyidea.api.lib.utils import getParam
from privacyidea.lib.utils import is_true, create_tag_dict
from privacyidea.lib.policy import SCOPE, ACTION, get_action_values_from_options
from privacyidea.lib.policy import SCOPE, ACTION, GROUP, get_action_values_from_options
from privacyidea.lib.policy import Match
from privacyidea.lib.log import log_with
from privacyidea.lib import _
@@ -177,6 +177,18 @@ def get_class_info(key=None, ret='all'):
'type': 'str',
'desc': _('Use an alternate challenge text for telling the '
'user to enter the code from the eMail.')
},
},
SCOPE.ENROLL: {
ACTION.MAXTOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of email tokens assigned."),
'group': GROUP.TOKEN
},
ACTION.MAXACTIVETOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of active email tokens assigned."),
'group': GROUP.TOKEN
}
}
}
@@ -41,6 +41,7 @@
from privacyidea.lib.token import check_realm_pass
from privacyidea.lib.decorators import check_token_locked
from privacyidea.lib import _
from privacyidea.lib.policy import ACTION, SCOPE, GROUP

log = logging.getLogger(__name__)
optional = True
@@ -129,7 +130,20 @@ def get_class_info(key=None, ret='all'):
'user': [],
# This tokentype is enrollable in the UI for...
'ui_enroll': ["admin"],
'policy': {},
'policy': {
SCOPE.ENROLL: {
ACTION.MAXTOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of 4eyes tokens assigned."),
'group': GROUP.TOKEN
},
ACTION.MAXACTIVETOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of active 4eyes tokens assigned."),
'group': GROUP.TOKEN
}
}
},
}

if key:
@@ -61,7 +61,7 @@
from privacyidea.lib.utils import (create_img, is_true, b32encode_and_unicode,
hexlify_and_unicode)
from privacyidea.lib.decorators import check_token_locked
from privacyidea.lib.policy import SCOPE, ACTION
from privacyidea.lib.policy import SCOPE, ACTION, GROUP
from privacyidea.lib import _
import traceback
import logging
@@ -130,6 +130,16 @@ def get_class_info(key=None, ret='all'):
'ui_enroll': ["admin", "user"],
'policy': {
SCOPE.ENROLL: {
ACTION.MAXTOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of HOTP tokens assigned."),
'group': GROUP.TOKEN
},
ACTION.MAXACTIVETOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of active HOTP tokens assigned."),
'group': GROUP.TOKEN
},
'yubikey_access_code': {
'type': 'str',
'desc': _("The Yubikey access code used to "
@@ -50,6 +50,7 @@
import traceback
import logging
from privacyidea.lib import _
from privacyidea.lib.policy import SCOPE, ACTION, GROUP

optional = True
required = False
@@ -97,7 +98,20 @@ def get_class_info(key=None, ret='all'):
'user': ['enroll'],
# This tokentype is enrollable in the UI for...
'ui_enroll': ["admin", "user"],
'policy': {}
'policy': {
SCOPE.ENROLL: {
ACTION.MAXTOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of mOTP tokens assigned."),
'group': GROUP.TOKEN
},
ACTION.MAXACTIVETOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of active mOTP tokens assigned."),
'group': GROUP.TOKEN
}
}
}
}

if key:
@@ -41,6 +41,7 @@
from privacyidea.lib import _
from privacyidea.lib.decorators import check_token_locked
from privacyidea.lib.crypto import get_alphanum_str
from privacyidea.lib.policy import SCOPE, ACTION, GROUP

OCRA_DEFAULT_SUITE = "OCRA-1:HOTP-SHA1-8:QH40"

@@ -93,7 +94,20 @@ def get_class_info(key=None, ret='all'):
#'user': ['enroll'],
# This tokentype is enrollable in the UI for...
'ui_enroll': [],
'policy': {},
'policy': {
SCOPE.ENROLL: {
ACTION.MAXTOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of OCRA tokens assigned."),
'group': GROUP.TOKEN
},
ACTION.MAXACTIVETOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of active OCRA tokens assigned."),
'group': GROUP.TOKEN
}
}
},
}

if key:
@@ -29,9 +29,8 @@
from privacyidea.lib.log import log_with
from privacyidea.lib.tokenclass import TokenClass
from privacyidea.lib.tokens.hotptoken import HotpTokenClass
from privacyidea.lib.policy import SCOPE
from privacyidea.lib.policy import SCOPE, ACTION, GROUP
from privacyidea.lib import _
from privacyidea.lib.policydecorators import libpolicy

log = logging.getLogger(__name__)
DEFAULT_COUNT = 100
@@ -107,7 +106,18 @@ def get_class_info(key=None, ret='all'):
"type": "int",
"desc": _("The number of OTP values, which are "
"printed on the paper.")
},
ACTION.MAXTOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of paper tokens assigned."),
'group': GROUP.TOKEN
},
ACTION.MAXACTIVETOKENUSER: {
'type': 'int',
'desc': _("The user may only have this maximum number of active paper tokens assigned."),
'group': GROUP.TOKEN
}

}
}
}

0 comments on commit 5c265bb

Please sign in to comment.
You can’t perform that action at this time.