Repository: privacyidea/privacyidea

Issue:
group the actions in scope authorization
We can easily add some groups in all the actions in scope authorization.

There are some actions, that only add some things to the response and some actions that also modify the outcome of the authentication request.

To do so, we need to adapt the file ``lib/policy.py`` and adapt the definitions in 

~~~~python
SCOPE.AUTHZ: {
~~~~

and add certain groups like:

~~~~python
SCOPE.AUTHZ: {
            ACTION.AUTHORIZED: {
                'type': 'str',
                'desc': _("Allow the user to authenticate (default). If set to '{0!s}', "
                          "the authentication of the user will be denied.").format(AUTHORIZED.DENY),
                'value': [AUTHORIZED.ALLOW, AUTHORIZED.DENY]
                'group': 'modifying'
            },
~~~~


Discussion:
The challenge is, what groups should exist?

Available actions:

## Setting actions

add_resolver_in_response
add_user_in_response
setrealm
no_detail_on_fail
no_detail_on_success

## Conditions

api_key_required
application_tokentype
auth_max_fail
auth_max_success
last_auth
serial
tokeninfo
tokentype
u2f_req
webauthn_authenticator_selection_list
webauthn_req

## Modifying Response

authorized

Resolving patch:
diff --git a/privacyidea/lib/policy.py b/privacyidea/lib/policy.py
--- a/privacyidea/lib/policy.py
+++ b/privacyidea/lib/policy.py
@@ -377,7 +377,9 @@ class GROUP(object):
     MACHINE = "machine"
     USER = "user"
     PIN = "pin"
-
+    MODIFYING_RESPONSE = "modifying response"
+    CONDITIONS = "conditions"
+    SETTING_ACTIONS = "setting actions"
 
 class MAIN_MENU(object):
     __doc__ = """These are the allowed top level menu items. These are used
@@ -391,6 +393,7 @@ class MAIN_MENU(object):
     COMPONENTS = "components"
 
 
+
 class LOGINMODE(object):
     __doc__ = """This is the list of possible values for the login mode."""
     USERSTORE = "userstore"
@@ -2021,7 +2024,8 @@ def get_static_policy_definitions(scope=None):
                 'type': 'str',
                 'desc': _("Allow the user to authenticate (default). If set to '{0!s}', "
                           "the authentication of the user will be denied.").format(AUTHORIZED.DENY),
-                'value': [AUTHORIZED.ALLOW, AUTHORIZED.DENY]
+                'value': [AUTHORIZED.ALLOW, AUTHORIZED.DENY],
+                'group': GROUP.MODIFYING_RESPONSE,
             },
             ACTION.APPLICATION_TOKENTYPE: {
                 'type': 'bool',
@@ -2035,63 +2039,84 @@ def get_static_policy_definitions(scope=None):
                 'desc': _("You can specify how many successful authentication "
                           "requests a user is allowed to do in a given time. "
                           "Specify like 1/5s, 2/10m, 10/1h - s, m, h being "
-                          "second, minute and hour.")
+                          "second, minute and hour."),
+                'group': GROUP.CONDITIONS,
             },
             ACTION.AUTHMAXFAIL: {
                 'type': 'str',
                 'desc': _("You can specify how many failed authentication "
                           "requests a user is allowed to do in a given time. "
                           "Specify like 1/5s, 2/10m, 10/1h - s, m, h being "
-                          "second, minute and hour.")
+                          "second, minute and hour."),
+                'group': GROUP.CONDITIONS,
             },
             ACTION.LASTAUTH: {
                 'type': 'str',
                 'desc': _("You can specify in which time frame the user needs "
                           "to authenticate again with this token. If the user "
                           "authenticates later, authentication will fail. "
-                          "Specify like 30h, 7d or 1y.")
+                          "Specify like 30h, 7d or 1y."),
+                'group': GROUP.CONDITIONS,
             },
             ACTION.TOKENTYPE: {
                 'type': 'str',
                 'desc': _('The user will only be authenticated with this '
-                          'very tokentype.')},
+                          'very tokentype.'),
+                'group': GROUP.CONDITIONS,
+            },
+
             ACTION.SERIAL: {
                 'type': 'str',
                 'desc': _('The user will only be authenticated if the serial '
-                          'number of the token matches this regexp.')},
+                          'number of the token matches this regexp.'),
+                'group': GROUP.CONDITIONS,
+            },
             ACTION.TOKENINFO: {
                 'type': 'str',
                 'desc': _("The user will only be authenticated if the tokeninfo "
-                          "field matches the regexp. key/<regexp>/")},
+                          "field matches the regexp. key/<regexp>/"),
+                'group': GROUP.CONDITIONS,
+            },
             ACTION.SETREALM: {
                 'type': 'str',
                 'value': realms,
                 'desc': _('The Realm of the user is set to this very realm. '
                           'This is important if the user is not contained in '
-                          'the default realm and can not pass his realm.')},
+                          'the default realm and can not pass his realm.'),
+                'group': GROUP.SETTING_ACTIONS,
+            },
             ACTION.NODETAILSUCCESS: {
                 'type': 'bool',
                 'desc': _('In case of successful authentication additional '
-                          'no detail information will be returned.')},
+                          'no detail information will be returned.'),
+                'group': GROUP.SETTING_ACTIONS,
+            },
             ACTION.NODETAILFAIL: {
                 'type': 'bool',
                 'desc': _('In case of failed authentication additional '
-                          'no detail information will be returned.')},
+                          'no detail information will be returned.'),
+                'group': GROUP.SETTING_ACTIONS,
+            },
             ACTION.ADDUSERINRESPONSE: {
                 'type': 'bool',
                 'desc': _('In case of successful authentication user data '
                           'will be added in the detail branch of the '
-                          'authentication response.')},
+                          'authentication response.'),
+                'group': GROUP.SETTING_ACTIONS,
+            },
             ACTION.ADDRESOLVERINRESPONSE: {
                 'type': 'bool',
                 'desc': _('In case of successful authentication the user resolver and '
                           'realm will be added in the detail branch of the '
-                          'authentication response.')},
+                          'authentication response.'),
+                'group': GROUP.SETTING_ACTIONS,
+            },
             ACTION.APIKEY: {
                 'type': 'bool',
                 'desc': _('The sending of an API Auth Key is required during'
                           'authentication. This avoids rogue authenticate '
-                          'requests against the /validate/check interface.')
+                          'requests against the /validate/check interface.'),
+                'group': GROUP.SETTING_ACTIONS,
             }
         },
 
diff --git a/privacyidea/lib/tokens/u2ftoken.py b/privacyidea/lib/tokens/u2ftoken.py
--- a/privacyidea/lib/tokens/u2ftoken.py
+++ b/privacyidea/lib/tokens/u2ftoken.py
@@ -262,7 +262,8 @@ def get_class_info(key=None, ret='all'):
                        U2FACTION.REQ: {
                            'type': 'str',
                            'desc': _("Only specified U2F tokens are "
-                                     "authorized.")
+                                     "authorized."),
+                           'group': GROUP.CONDITIONS,
                        }
                    },
                    SCOPE.ENROLL: {
diff --git a/privacyidea/lib/tokens/webauthntoken.py b/privacyidea/lib/tokens/webauthntoken.py
--- a/privacyidea/lib/tokens/webauthntoken.py
+++ b/privacyidea/lib/tokens/webauthntoken.py
@@ -589,11 +589,13 @@ def get_class_info(key=None, ret='all'):
                     WEBAUTHNACTION.AUTHENTICATOR_SELECTION_LIST: {
                         'type': 'str',
                         'desc': _("A list of WebAuthn authenticators acceptable for authorization, given as "
-                                  "a space-separated list of AAGUIDs. Per default all authenticators are acceptable.")
+                                  "a space-separated list of AAGUIDs. Per default all authenticators are acceptable."),
+                        'group': GROUP.CONDITIONS,
                     },
                     WEBAUTHNACTION.REQ: {
                         'type': 'str',
-                        'desc': _("Only the specified WebAuthn-tokens are authorized.")
+                        'desc': _("Only the specified WebAuthn-tokens are authorized."),
+                        'group': GROUP.CONDITIONS,
                     }
                 },
                 SCOPE.ENROLL: {


Test patch: