def test_02_token(self): r = get_token_list() self.assertTrue("privacyidea.lib.tokens.totptoken" in r, r) self.assertTrue("privacyidea.lib.tokens.hotptoken" in r, r) # check modules mlist = get_token_module_list() mod_name = "privacyidea.lib.tokens.totptoken" exec("import %s" % mod_name) module = eval(mod_name) self.assertTrue(module in mlist, mlist) # r = get_resolver_classes() # self.assertTrue(UserResolver in r, r) # self.assertTrue(PWResolver in r, r) # get_token_class_dict (classes, types) = get_token_class_dict() self.assertTrue('privacyidea.lib.tokens.hotptoken.HotpTokenClass' in classes, classes) self.assertTrue(classes.get( 'privacyidea.lib.tokens.hotptoken.HotpTokenClass') == HotpTokenClass, classes) self.assertTrue('privacyidea.lib.tokens.totptoken.TotpTokenClass' in classes, classes) self.assertTrue(classes.get( 'privacyidea.lib.tokens.totptoken.TotpTokenClass') == TotpTokenClass, classes) self.assertTrue('privacyidea.lib.tokens.hotptoken.HotpTokenClass' in types, types) self.assertTrue('privacyidea.lib.tokens.totptoken.TotpTokenClass' in types, types) self.assertTrue(types.get('privacyidea.lib.tokens.hotptoken' '.HotpTokenClass') == "hotp", types) self.assertTrue(types.get('privacyidea.lib.tokens.totptoken' '.TotpTokenClass') == "totp", types) types = get_token_types() self.assertTrue("totp" in types, types) self.assertTrue("hotp" in types, types) # Now the resolver types are contained. self.assertTrue("pi_token_types" in current_app.config, current_app.config) types = get_token_types() self.assertTrue("totp" in types, types) self.assertTrue("hotp" in types, types) # At the beginning the token classes are not cached. self.assertFalse("pi_token_classes" in current_app.config, current_app.config) r = get_token_classes() self.assertTrue(TotpTokenClass in r, r) self.assertTrue(HotpTokenClass in r, r) # Now the token classes are cached self.assertTrue("pi_token_classes" in current_app.config, current_app.config) r = get_token_classes() self.assertTrue(TotpTokenClass in r, r) self.assertTrue(HotpTokenClass in r, r)
def conditions(cls): """ The UserNotification can filter for conditions like * type of logged in user and * successful or failed value.success allowed types are str, multi, text, regexp :return: dict """ realms = get_realms() cond = { "realm": { "type": "str", "desc": _("The user realm, for which this event should apply."), "value": realms.keys() }, "tokenrealm": { "type": "multi", "desc": _("The token realm, for which this event should " "apply."), "value": [{"name": r} for r in realms.keys()] }, "tokentype": { "type": "multi", "desc": _("The type of the token."), "value": [{"name": r} for r in get_token_types()] }, "logged_in_user": { "type": "str", "desc": _("The logged in user is of the following type."), "value": (ROLE.ADMIN, ROLE.USER) }, "result_value": { "type": "str", "desc": _("The result.value within the response is " "True or False."), "value": ("True", "False") }, "token_locked": { "type": "str", "desc": _("Check if the max failcounter of the token is " "reached."), "value": ("True", "False") }, "serial": { "type": "regexp", "desc": _("Action is triggered, if the serial matches this " "regular expression.") } } return cond
def test_02_token(self): r = get_token_list() self.assertTrue("privacyidea.lib.tokens.totptoken" in r, r) self.assertTrue("privacyidea.lib.tokens.hotptoken" in r, r) # check modules mlist = get_token_module_list() mod_name = "privacyidea.lib.tokens.totptoken" module = importlib.import_module(mod_name) self.assertTrue(module in mlist, mlist) # r = get_resolver_classes() # self.assertTrue(UserResolver in r, r) # self.assertTrue(PWResolver in r, r) # get_token_class_dict (classes, types) = get_token_class_dict() self.assertTrue( 'privacyidea.lib.tokens.hotptoken.HotpTokenClass' in classes, classes) self.assertTrue( classes.get('privacyidea.lib.tokens.hotptoken.HotpTokenClass') == HotpTokenClass, classes) self.assertTrue( 'privacyidea.lib.tokens.totptoken.TotpTokenClass' in classes, classes) self.assertTrue( classes.get('privacyidea.lib.tokens.totptoken.TotpTokenClass') == TotpTokenClass, classes) self.assertTrue( 'privacyidea.lib.tokens.hotptoken.HotpTokenClass' in types, types) self.assertTrue( 'privacyidea.lib.tokens.totptoken.TotpTokenClass' in types, types) self.assertTrue( types.get('privacyidea.lib.tokens.hotptoken' '.HotpTokenClass') == "hotp", types) self.assertTrue( types.get('privacyidea.lib.tokens.totptoken' '.TotpTokenClass') == "totp", types) types = get_token_types() self.assertTrue("totp" in types, types) self.assertTrue("hotp" in types, types) # token classes are cached with calling 'get_token_types()' self.assertTrue("pi_token_classes" in this.config, this.config) self.assertTrue("pi_token_types" in this.config, this.config) r = get_token_classes() self.assertTrue(TotpTokenClass in r, r) self.assertTrue(HotpTokenClass in r, r)
def test_02_get_tokens(self): # get All tokens tokenobject_list = get_tokens() # Check if these are valid tokentypes self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) for token_object in tokenobject_list: self.assertTrue(token_object.type in get_token_types(), token_object.type) # get assigned tokens tokenobject_list = get_tokens(assigned=True) self.assertTrue(len(tokenobject_list) == 0, tokenobject_list) # get unassigned tokens tokenobject_list = get_tokens(assigned=False) self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) # pass the wrong parameter # This will ignore the filter! tokenobject_list = get_tokens(assigned="True") self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) # get tokens of type HOTP tokenobject_list = get_tokens(tokentype="hotp") self.assertTrue(len(tokenobject_list) == 0, tokenobject_list) # get tokens of type TOTP tokenobject_list = get_tokens(tokentype="totp") self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) # Search for tokens in realm db_token = Token("hotptoken", tokentype="hotp", userid=1000, resolver=self.resolvername1, realm=self.realm1) db_token.update_otpkey(self.otpkey) db_token.save() tokenobject_list = get_tokens(realm=self.realm1) self.assertTrue(len(tokenobject_list) == 1, tokenobject_list) self.assertTrue(tokenobject_list[0].type == "hotp", tokenobject_list[0].type) # get tokens for a given serial number tokenobject_list = get_tokens(serial="hotptoken") self.assertTrue(len(tokenobject_list) == 1, tokenobject_list) # ...but not in an unassigned state! tokenobject_list = get_tokens(serial="hotptoken", assigned=False) self.assertTrue(len(tokenobject_list) == 0, tokenobject_list) # get the tokens for the given user tokenobject_list = get_tokens( user=User(login="******", realm=self.realm1)) self.assertTrue(len(tokenobject_list) == 1, tokenobject_list)
def test_02_get_tokens(self): # get All tokens tokenobject_list = get_tokens() # Check if these are valid tokentypes self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) for token_object in tokenobject_list: self.assertTrue(token_object.type in get_token_types(), token_object.type) # get assigned tokens tokenobject_list = get_tokens(assigned=True) self.assertTrue(len(tokenobject_list) == 0, tokenobject_list) # get unassigned tokens tokenobject_list = get_tokens(assigned=False) self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) # pass the wrong parameter # This will ignore the filter! tokenobject_list = get_tokens(assigned="True") self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) # get tokens of type HOTP tokenobject_list = get_tokens(tokentype="hotp") self.assertTrue(len(tokenobject_list) == 0, tokenobject_list) # get tokens of type TOTP tokenobject_list = get_tokens(tokentype="totp") self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) # Search for tokens in realm db_token = Token("hotptoken", tokentype="hotp", userid=1000, resolver=self.resolvername1, realm=self.realm1) db_token.update_otpkey(self.otpkey) db_token.save() tokenobject_list = get_tokens(realm=self.realm1) self.assertTrue(len(tokenobject_list) == 1, tokenobject_list) self.assertTrue(tokenobject_list[0].type == "hotp", tokenobject_list[0].type) # get tokens for a given serial number tokenobject_list = get_tokens(serial="hotptoken") self.assertTrue(len(tokenobject_list) == 1, tokenobject_list) # ...but not in an unassigned state! tokenobject_list = get_tokens(serial="hotptoken", assigned=False) self.assertTrue(len(tokenobject_list) == 0, tokenobject_list) # get the tokens for the given user tokenobject_list = get_tokens(user=User(login="******", realm=self.realm1)) self.assertTrue(len(tokenobject_list) == 1, tokenobject_list)
def test_02_token(self): r = get_token_list() self.assertTrue("privacyidea.lib.tokens.totptoken" in r, r) self.assertTrue("privacyidea.lib.tokens.hotptoken" in r, r) # check modules mlist = get_token_module_list() mod_name = "privacyidea.lib.tokens.totptoken" module = importlib.import_module(mod_name) self.assertTrue(module in mlist, mlist) # r = get_resolver_classes() # self.assertTrue(UserResolver in r, r) # self.assertTrue(PWResolver in r, r) # get_token_class_dict (classes, types) = get_token_class_dict() self.assertTrue('privacyidea.lib.tokens.hotptoken.HotpTokenClass' in classes, classes) self.assertTrue(classes.get( 'privacyidea.lib.tokens.hotptoken.HotpTokenClass') == HotpTokenClass, classes) self.assertTrue('privacyidea.lib.tokens.totptoken.TotpTokenClass' in classes, classes) self.assertTrue(classes.get( 'privacyidea.lib.tokens.totptoken.TotpTokenClass') == TotpTokenClass, classes) self.assertTrue('privacyidea.lib.tokens.hotptoken.HotpTokenClass' in types, types) self.assertTrue('privacyidea.lib.tokens.totptoken.TotpTokenClass' in types, types) self.assertTrue(types.get('privacyidea.lib.tokens.hotptoken' '.HotpTokenClass') == "hotp", types) self.assertTrue(types.get('privacyidea.lib.tokens.totptoken' '.TotpTokenClass') == "totp", types) types = get_token_types() self.assertTrue("totp" in types, types) self.assertTrue("hotp" in types, types) # token classes are cached with calling 'get_token_types()' self.assertTrue("pi_token_classes" in this.config, this.config) self.assertTrue("pi_token_types" in this.config, this.config) r = get_token_classes() self.assertTrue(TotpTokenClass in r, r) self.assertTrue(HotpTokenClass in r, r)
def get_static_policy_definitions(scope=None): """ These are the static hard coded policy definitions. They can be enhanced by token based policy definitions, that can be found in lib.token.get_dynamic_policy_definitions. :param scope: Optional the scope of the policies :type scope: basestring :return: allowed scopes with allowed actions, the type of action and a description. :rtype: dict """ pol = { SCOPE.ADMIN: { ACTION.ENABLE: {'type': 'bool', 'desc': _('Admin is allowed to enable tokens.')}, ACTION.DISABLE: {'type': 'bool', 'desc': _('Admin is allowed to disable tokens.')}, ACTION.SET: {'type': 'bool', 'desc': _( 'Admin is allowed to set token properties.')}, ACTION.SETPIN: {'type': 'bool', 'desc': _( 'Admin is allowed to set the OTP PIN of ' 'tokens.')}, ACTION.RESYNC: {'type': 'bool', 'desc': _('Admin is allowed to resync tokens.')}, ACTION.RESET: {'type': 'bool', 'desc': _( 'Admin is allowed to reset the Failcounter of ' 'a token.')}, ACTION.REVOKE: {'tpye': 'bool', 'desc': _("Admin is allowed to revoke a token")}, ACTION.ASSIGN: {'type': 'bool', 'desc': _( 'Admin is allowed to assign a token to a ' 'user.')}, ACTION.UNASSIGN: {'type': 'bool', 'desc': _( 'Admin is allowed to remove the token from ' 'a user, ' 'i.e. unassign a token.')}, ACTION.IMPORT: {'type': 'bool', 'desc': _( 'Admin is allowed to import token files.')}, ACTION.DELETE: {'type': 'bool', 'desc': _( 'Admin is allowed to remove tokens from the ' 'database.')}, ACTION.USERLIST: {'type': 'bool', 'desc': _( 'Admin is allowed to view the list of the ' 'users.')}, ACTION.MACHINELIST: {'type': 'bool', 'desc': _('The Admin is allowed to list ' 'the machines.')}, ACTION.MACHINETOKENS: {'type': 'bool', 'desc': _('The Admin is allowed to attach ' 'and detach tokens to machines.')}, ACTION.AUTHITEMS: {'type': 'bool', 'desc': _('The Admin is allowed to fetch ' 'authentication items of tokens ' 'assigned to machines.')}, # 'checkstatus': {'type': 'bool', # 'desc' : _('Admin is allowed to check the # status of a challenge' # ' resonse token.'), # "group": "tools"}, ACTION.TOKENREALMS: {'type': 'bool', 'desc': _('Admin is allowed to manage the ' 'realms of a token.')}, ACTION.GETSERIAL: {'type': 'bool', 'desc': _('Admin is allowed to retrieve a serial' ' for a given OTP value.'), "group": "tools"}, # 'checkserial': {'type': 'bool', # 'desc': _('Admin is allowed to check if a serial ' # 'is unique'), # "group": "tools"}, ACTION.COPYTOKENPIN: {'type': 'bool', 'desc': _( 'Admin is allowed to copy the PIN of ' 'one token ' 'to another token.'), "group": "tools"}, ACTION.COPYTOKENUSER: {'type': 'bool', 'desc': _( 'Admin is allowed to copy the assigned ' 'user to another' ' token, i.e. assign a user ot ' 'another token.'), "group": "tools"}, ACTION.LOSTTOKEN: {'type': 'bool', 'desc': _('Admin is allowed to trigger the ' 'lost token workflow.'), "group": "tools"}, # 'getotp': { # 'type': 'bool', # 'desc': _('Allow the administrator to retrieve OTP values # for tokens.'), # "group": "tools"}, ACTION.SYSTEMWRITE: {'type': 'bool', "desc": _("Admin is allowed to write and " "modify the system configuration."), "group": "system"}, ACTION.SYSTEMDELETE: {'type': 'bool', "desc": _("Admin is allowed to delete " "keys in the system " "configuration."), "group": "system"}, ACTION.CONFIGDOCUMENTATION: {'type': 'bool', 'desc': _('Admin is allowed to ' 'export a documentation ' 'of the complete ' 'configuration including ' 'resolvers and realm.'), 'group': 'system'}, ACTION.POLICYWRITE: {'type': 'bool', "desc": _("Admin is allowed to write and " "modify the policies."), "group": "system"}, ACTION.POLICYDELETE: {'type': 'bool', "desc": _("Admin is allowed to delete " "policies."), "group": "system"}, ACTION.RESOLVERWRITE: {'type': 'bool', "desc": _("Admin is allowed to write and " "modify the " "resolver and realm " "configuration."), "group": "system"}, ACTION.RESOLVERDELETE: {'type': 'bool', "desc": _("Admin is allowed to delete " "resolvers and realms."), "group": "system"}, ACTION.CACONNECTORWRITE: {'type': 'bool', "desc": _("Admin is allowed to create new" " CA Connector definitions " "and modify existing ones."), "group": "system"}, ACTION.CACONNECTORDELETE: {'type': 'bool', "desc": _("Admin is allowed to delete " "CA Connector definitions."), "group": "system"}, ACTION.MACHINERESOLVERWRITE: {'type': 'bool', 'desc': _("Admin is allowed to " "write and modify the " "machine resolvers."), 'group': "system"}, ACTION.MACHINERESOLVERDELETE: {'type': 'bool', 'desc': _("Admin is allowed to " "delete " "machine resolvers."), 'group': "system"}, ACTION.AUDIT: {'type': 'bool', "desc": _("Admin is allowed to view the Audit log."), "group": "system"}, ACTION.ADDUSER: {'type': 'bool', "desc": _("Admin is allowed to add users in a " "userstore/UserIdResolver."), "group": "system"}, ACTION.UPDATEUSER: {'type': 'bool', "desc": _("Admin is allowed to update the " "users data in a userstore."), "group": "system"}, ACTION.DELETEUSER: {'type': 'bool', "desc": _("Admin is allowed to delete a user " "object in a userstore.")}, ACTION.SETHSM: {'type': 'bool', 'desc': _("Admin is allowed to set the password " "of the HSM/Security Module.")} }, # 'gettoken': { # 'max_count_dpw': {'type': 'int', # 'desc' : _('When OTP values are retrieved for # a DPW token, ' # 'this is the maximum number of # retrievable OTP values.')}, # 'max_count_hotp': {'type': 'int', # 'desc' : _('When OTP values are retrieved # for a HOTP token, ' # 'this is the maximum number of # retrievable OTP values.')}, # 'max_count_totp': {'type': 'int', # 'desc' : _('When OTP values are retrieved # for a TOTP token, ' # 'this is the maximum number of # retrievable OTP values.')}, # }, SCOPE.USER: { ACTION.ASSIGN: { 'type': 'bool', 'desc': _("The user is allowed to assign an existing token" " that is not yet assigned" " using the token serial number.")}, ACTION.DISABLE: {'type': 'bool', 'desc': _( 'The user is allowed to disable his own ' 'tokens.')}, ACTION.ENABLE: {'type': 'bool', 'desc': _( "The user is allowed to enable his own " "tokens.")}, ACTION.DELETE: {'type': 'bool', "desc": _( "The user is allowed to delete his own " "tokens.")}, ACTION.UNASSIGN: {'type': 'bool', "desc": _("The user is allowed to unassign his " "own tokens.")}, ACTION.RESYNC: {'type': 'bool', "desc": _("The user is allowed to resyncronize his " "tokens.")}, ACTION.REVOKE: {'tpye': 'bool', 'desc': _("The user is allowed to revoke a token")}, ACTION.RESET: {'type': 'bool', 'desc': _('The user is allowed to reset the ' 'failcounter of his tokens.')}, ACTION.SETPIN: {'type': 'bool', "desc": _("The user is allowed to set the OTP PIN " "of his tokens.")}, ACTION.OTPPINMAXLEN: {'type': 'int', 'value': range(0, 32), "desc": _("Set the maximum allowed length " "of the OTP PIN.")}, ACTION.OTPPINMINLEN: {'type': 'int', 'value': range(0, 32), "desc": _("Set the minimum required length " "of the OTP PIN.")}, ACTION.OTPPINCONTENTS: {'type': 'str', "desc": _("Specifiy the required " "contents of the OTP PIN. " "(c)haracters, (n)umeric, " "(s)pecial, (o)thers. [+/-]!")}, # 'setMOTPPIN': {'type': 'bool', # "desc": _("The user is allowed to set the mOTP # PIN of his mOTP tokens.")}, # 'getotp': {'type': 'bool', # "desc": _("The user is allowed to retrieve OTP # values for his own tokens.")}, # 'activateQR': {'type': 'bool', # "desc": _("The user is allowed to enroll a QR # token.")}, # 'max_count_dpw': {'type': 'int', # "desc": _("This is the maximum number of OTP # values, the user is allowed to retrieve for a DPW token.")}, # 'max_count_hotp': {'type': 'int', # "desc": _("This is the maximum number of OTP # values, the user is allowed to retrieve for a HOTP token.")}, # 'max_count_totp': {'type': 'int', # "desc": _("This is the maximum number of OTP # values, the user is allowed to retrieve for a TOTP token.")}, ACTION.AUDIT: { 'type': 'bool', 'desc': _('Allow the user to view his own token history.')}, ACTION.UPDATEUSER: {'type': 'bool', 'desc': _("The user is allowed to update his " "own user information, like changing " "his password.")} # 'getserial': { # 'type': 'bool', # 'desc': _('Allow the user to search an unassigned token by # OTP value.')}, }, SCOPE.ENROLL: { ACTION.MAXTOKENREALM: { 'type': 'int', 'desc': _('Limit the number of allowed tokens in a realm.')}, ACTION.MAXTOKENUSER: { 'type': 'int', 'desc': _('Limit the number of tokens a user may have ' 'assigned.')}, ACTION.OTPPINRANDOM: { 'type': 'int', 'value': range(0, 32), "desc": _("Set a random OTP PIN with this length for a " "token.")}, ACTION.PINHANDLING: { 'type': 'str', 'desc': _('In case of a random OTP PIN use this python ' 'module to process the PIN.')}, ACTION.ENCRYPTPIN: { 'type': 'bool', "desc": _("The OTP PIN can be hashed or encrypted. Hashing " "the PIN is the default behaviour.")}, ACTION.TOKENLABEL: { 'type': 'str', 'desc': _("Set label for a new enrolled Google Authenticator. " "Possible tags are <u> (user), <r> (" "realm), <s> (serial).")}, ACTION.AUTOASSIGN: { 'type': 'str', 'value': [AUTOASSIGNVALUE.NONE, AUTOASSIGNVALUE.USERSTORE], 'desc': _("Users can assign a token just by using the " "unassigned token to authenticate.")}, ACTION.LOSTTOKENPWLEN: { 'type': 'int', 'value': range(1, 32), 'desc': _('The length of the password in case of ' 'temporary token (lost token).')}, ACTION.LOSTTOKENPWCONTENTS: { 'type': 'str', 'desc': _('The contents of the temporary password, ' 'described by the characters C, c, n, s.')}, ACTION.LOSTTOKENVALID: { 'type': 'int', 'value': range(1, 61), 'desc': _('The length of the validity for the temporary ' 'token (in days).')}, }, SCOPE.AUTH: { ACTION.OTPPIN: { 'type': 'str', 'value': [ACTIONVALUE.TOKENPIN, ACTIONVALUE.USERSTORE, ACTIONVALUE.NONE], 'desc': _('Either use the Token PIN , use the Userstore ' 'Password or use no fixed password ' 'component.')}, ACTION.CHALLENGERESPONSE: { 'type': 'str', 'desc': _('This is a whitespace separated list of tokentypes, ' 'that can be used with challenge response.') }, ACTION.PASSTHRU: { 'type': 'bool', 'desc': _('If set, the user in this realm will be ' 'authenticated against the UserIdResolver,' ' if the user has no tokens assigned.') }, ACTION.PASSNOTOKEN: { 'type': 'bool', 'desc': _('If the user has no token, the authentication ' 'request for this user will always be true.') }, ACTION.PASSNOUSER: { 'type': 'bool', 'desc': _('If the user user does not exist, ' 'the authentication request for this ' 'non-existing user will always be true.') }, ACTION.MANGLE: { 'type': 'str', 'desc': _('Can be used to modify the parameters pass, ' 'user and realm in an authentication request. See ' 'the documentation for an example.') } # 'qrtanurl': { # 'type': 'str', # 'desc': _('The URL for the half automatic mode that should # be ' # 'used in a QR Token') # }, # 'challenge_response': { # 'type': 'str', # 'desc': _('A list of tokentypes for which challenge response ' # 'should be used.') # } }, SCOPE.AUTHZ: { ACTION.AUTHMAXSUCCESS: { 'type': 'str', '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.") }, 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.") }, 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.") }, ACTION.TOKENTYPE: { 'type': 'str', 'desc': _('The user will only be authenticated with this ' 'very tokentype.')}, ACTION.SERIAL: { 'type': 'str', 'desc': _('The user will only be authenticated if the serial ' 'number of the token matches this regexp.')}, ACTION.SETREALM: { 'type': 'str', '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.')}, ACTION.NODETAILSUCCESS: { 'type': 'bool', 'desc': _('In case of successful authentication additional ' 'no detail information will be returned.')}, ACTION.NODETAILFAIL: { 'type': 'bool', 'desc': _('In case of failed authentication additional ' 'no detail information will be returned.')}, 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.') } }, SCOPE.WEBUI: { ACTION.LOGINMODE: { 'type': 'str', 'desc': _( 'If set to "privacyIDEA" the users and admins need to ' 'authenticate against privacyIDEA when they log in ' 'to the Web UI. Defaults to "userstore"'), 'value': [LOGINMODE.USERSTORE, LOGINMODE.PRIVACYIDEA, LOGINMODE.DISABLE], }, ACTION.REMOTE_USER: { 'type': 'str', 'value': [REMOTE_USER.ACTIVE, REMOTE_USER.DISABLE], 'desc': _('The REMOTE_USER set by the webserver can be used ' 'to login to privacyIDEA or it will be ignored. ' 'Defaults to "disable".') }, ACTION.LOGOUTTIME: { 'type': 'int', 'desc': _("Set the time in seconds after which the user will " "be logged out from the WebUI. Default: 120") }, ACTION.TOKENPAGESIZE: { 'type': 'int', 'desc': _("Set how many tokens should be displayed in the " "token view on one page.") }, ACTION.USERPAGESIZE: { 'type': 'int', 'desc': _("Set how many users should be displayed in the user " "view on one page.") }, ACTION.USERDETAILS: { 'type': 'bool', 'desc': _("Whether the user ID and the resolver should be " "displayed in the token list.") }, ACTION.POLICYTEMPLATEURL: { 'type': 'str', 'desc': _("The URL of a repository, where the policy " "templates can be found. (Default " "https://raw.githubusercontent.com/privacyidea/" "policy-templates/master/templates/)") }, ACTION.DEFAULT_TOKENTYPE: { 'type': 'str', 'desc': _("This is the default token type in the token " "enrollment dialog."), 'value': get_token_types() } } # 'ocra': { # 'request': { # 'type': 'bool', # 'desc': _('Allow to do a ocra/request.')}, # 'status': { # 'type': 'bool', # 'desc': _('Allow to check the transaction status.')}, # 'activationcode': { # 'type': 'bool', # 'desc': _('Allow to do an ocra/getActivationCode.')}, # 'calcOTP': { # 'type': 'bool', # 'desc': _('Allow to do an ocra/calculateOtp.')} # }, } if scope: ret = pol.get(scope, {}) else: ret = pol return ret
def test_02_get_tokens(self): # get All tokens tokenobject_list = get_tokens() # Check if these are valid tokentypes self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) for token_object in tokenobject_list: self.assertTrue(token_object.type in get_token_types(), token_object.type) # get assigned tokens tokenobject_list = get_tokens(assigned=True) self.assertTrue(len(tokenobject_list) == 0, tokenobject_list) # get unassigned tokens tokenobject_list = get_tokens(assigned=False) self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) # pass the wrong parameter # This will ignore the filter! tokenobject_list = get_tokens(assigned="True") self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) # get tokens of type HOTP tokenobject_list = get_tokens(tokentype="hotp") self.assertTrue(len(tokenobject_list) == 0, tokenobject_list) # get tokens of type TOTP tokenobject_list = get_tokens(tokentype="totp") self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) # Search for tokens in realm db_token = Token("hotptoken", tokentype="hotp", userid=1000, resolver=self.resolvername1, realm=self.realm1) db_token.update_otpkey(self.otpkey) db_token.save() tokenobject_list = get_tokens(realm=self.realm1) self.assertTrue(len(tokenobject_list) == 1, tokenobject_list) self.assertTrue(tokenobject_list[0].type == "hotp", tokenobject_list[0].type) # get tokens for a given serial number tokenobject_list = get_tokens(serial="hotptoken") self.assertTrue(len(tokenobject_list) == 1, tokenobject_list) # ...but not in an unassigned state! tokenobject_list = get_tokens(serial="hotptoken", assigned=False) self.assertTrue(len(tokenobject_list) == 0, tokenobject_list) # get the tokens for the given user tokenobject_list = get_tokens(user=User(login="******", realm=self.realm1)) self.assertTrue(len(tokenobject_list) == 1, tokenobject_list) # get tokens for a given tokeninfo of the token!!! token = init_token({"type": "yubikey", "serial": "yk1", "yubikey.prefix": "vv123456", "otpkey": self.otpkey}) self.assertEqual(token.token.serial, "yk1") tokenobject_list = get_tokens(tokeninfo={"yubikey.prefix": "vv123456"}) self.assertEqual(len(tokenobject_list), 1) self.assertEqual(tokenobject_list[0].get_tokeninfo("yubikey.prefix"), "vv123456") remove_token("yk1") # Tokeninfo with more than one entry is not supported self.assertRaises(privacyIDEAError, get_tokens, tokeninfo={"key1": "value1", "key2": "value2"})
def test_02_token(self): r = get_token_list() self.assertTrue("privacyidea.lib.tokens.totptoken" in r, r) self.assertTrue("privacyidea.lib.tokens.hotptoken" in r, r) # check modules mlist = get_token_module_list() mod_name = "privacyidea.lib.tokens.totptoken" module = importlib.import_module(mod_name) self.assertTrue(module in mlist, mlist) # r = get_resolver_classes() # self.assertTrue(UserResolver in r, r) # self.assertTrue(PWResolver in r, r) # get_token_class_dict (classes, types) = get_token_class_dict() self.assertTrue( 'privacyidea.lib.tokens.hotptoken.HotpTokenClass' in classes, classes) self.assertTrue( classes.get('privacyidea.lib.tokens.hotptoken.HotpTokenClass') == HotpTokenClass, classes) self.assertTrue( 'privacyidea.lib.tokens.totptoken.TotpTokenClass' in classes, classes) self.assertTrue( classes.get('privacyidea.lib.tokens.totptoken.TotpTokenClass') == TotpTokenClass, classes) self.assertTrue( 'privacyidea.lib.tokens.hotptoken.HotpTokenClass' in types, types) self.assertTrue( 'privacyidea.lib.tokens.totptoken.TotpTokenClass' in types, types) self.assertTrue( types.get('privacyidea.lib.tokens.hotptoken' '.HotpTokenClass') == "hotp", types) self.assertTrue( types.get('privacyidea.lib.tokens.totptoken' '.TotpTokenClass') == "totp", types) types = get_token_types() self.assertTrue("totp" in types, types) self.assertTrue("hotp" in types, types) # token classes are cached with calling 'get_token_types()' self.assertTrue("pi_token_classes" in this.config, this.config) self.assertTrue("pi_token_types" in this.config, this.config) r = get_token_classes() self.assertTrue(TotpTokenClass in r, r) self.assertTrue(HotpTokenClass in r, r) # Test custom token types with self.app_context: self.app.config['PI_TOKEN_MODULES'] = 'tests.testdata.fancytoken' r = get_token_list() self.assertIn("tests.testdata.fancytoken", r, r) mlist = get_token_module_list() mod = importlib.import_module('tests.testdata.fancytoken') self.assertTrue(mod in mlist, mlist) (classes, types) = get_token_class_dict() self.assertIn('tests.testdata.fancytoken.FancyTokenClass', classes, classes) self.assertIn('tests.testdata.fancytoken.FancyTokenClass', types, types) self.assertEqual( types['tests.testdata.fancytoken.FancyTokenClass'], 'fancy', types) self.app.config.pop('PI_TOKEN_MODULES')
def conditions(cls): """ The UserNotification can filter for conditions like * type of logged in user and * successful or failed value.success allowed types are str, multi, text, regexp :return: dict """ realms = get_realms() cond = { "realm": { "type": "str", "desc": _("The user realm, for which this event should apply."), "value": realms.keys() }, "tokenrealm": { "type": "multi", "desc": _("The token realm, for which this event should " "apply."), "value": [{ "name": r } for r in realms.keys()] }, CONDITION.TOKENTYPE: { "type": "multi", "desc": _("The type of the token."), "value": [{ "name": r } for r in get_token_types()] }, "logged_in_user": { "type": "str", "desc": _("The logged in user is of the following type."), "value": (ROLE.ADMIN, ROLE.USER) }, "result_value": { "type": "str", "desc": _("The result.value within the response is " "True or False."), "value": ("True", "False") }, "token_locked": { "type": "str", "desc": _("Check if the max failcounter of the token is " "reached."), "value": ("True", "False") }, CONDITION.TOKEN_HAS_OWNER: { "type": "str", "desc": _("The token has a user assigned."), "value": ("True", "False") }, CONDITION.TOKEN_IS_ORPHANED: { "type": "str", "desc": _("The token has a user assigned, but the user does " "not exist in the userstore anymore."), "value": ("True", "False") }, CONDITION.TOKEN_VALIDITY_PERIOD: { "type": "str", "desc": _("Check if the token is within its validity period."), "value": ("True", "False") }, "serial": { "type": "regexp", "desc": _("Action is triggered, if the serial matches this " "regular expression.") }, CONDITION.USER_TOKEN_NUMBER: { "type": "str", "desc": _("Action is triggered, if the user has this number " "of tokens assigned.") }, CONDITION.OTP_COUNTER: { "type": "str", "desc": _("Action is triggered, if the counter of the token " "equals this setting.") }, CONDITION.LAST_AUTH: { "type": "str", "desc": _("Action is triggered, if the last authentication of " "the token is older than 7h, 10d or 1y.") }, CONDITION.COUNT_AUTH: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the tokeninfo field 'count_auth' is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.COUNT_AUTH_SUCCESS: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the tokeninfo field " "'count_auth_success' is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.COUNT_AUTH_FAIL: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the difference between the tokeninfo " "field 'count_auth' and 'count_auth_success is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.TOKENINFO: { "type": "str", "desc": _("This condition can check any arbitrary tokeninfo " "field. You need to enter something like " "'<fieldname> == <fieldvalue>', '<fieldname> > " "<fieldvalue>' or '<fieldname> < <fieldvalue>'") } } return cond
def conditions(cls): """ The UserNotification can filter for conditions like * type of logged in user and * successful or failed value.success allowed types are str, multi, text, regexp :return: dict """ realms = get_realms() resolvers = get_resolver_list() cond = { CONDITION.ROLLOUT_STATE: { "type": "str", "desc": _("The rollout_state of the token has a certain value like 'clientwait' or 'enrolled'.") }, CONDITION.REALM: { "type": "str", "desc": _("The realm of the user, for which this event should apply."), "value": list(realms) }, CONDITION.RESOLVER: { "type": "str", "desc": _("The resolver of the user, for which this event should apply."), "value": list(resolvers) }, CONDITION.TOKENREALM: { "type": "multi", "desc": _("The realm of the token, for which this event should " "apply."), "value": [{"name": r} for r in realms] }, CONDITION.TOKENRESOLVER: { "type": "multi", "desc": _("The resolver of the token, for which this event should " "apply."), "value": [{"name": r} for r in resolvers] }, CONDITION.TOKENTYPE: { "type": "multi", "desc": _("The type of the token."), "value": [{"name": r} for r in get_token_types()] }, "logged_in_user": { "type": "str", "desc": _("The logged in user is of the following type."), "value": (ROLE.ADMIN, ROLE.USER) }, CONDITION.RESULT_VALUE: { "type": "str", "desc": _("The result.value within the response is " "True or False."), "value": ("True", "False") }, CONDITION.RESULT_STATUS: { "type": "str", "desc": _("The result.status within the response is " "True or False."), "value": ("True", "False") }, "token_locked": { "type": "str", "desc": _("Check if the max failcounter of the token is " "reached."), "value": ("True", "False") }, CONDITION.TOKEN_HAS_OWNER: { "type": "str", "desc": _("The token has a user assigned."), "value": ("True", "False") }, CONDITION.TOKEN_IS_ORPHANED: { "type": "str", "desc": _("The token has a user assigned, but the user does " "not exist in the userstore anymore."), "value": ("True", "False") }, CONDITION.TOKEN_VALIDITY_PERIOD: { "type": "str", "desc": _("Check if the token is within its validity period."), "value": ("True", "False") }, "serial": { "type": "regexp", "desc": _("Action is triggered, if the serial matches this " "regular expression.") }, CONDITION.USER_TOKEN_NUMBER: { "type": "str", "desc": _("Action is triggered, if the user has this number " "of tokens assigned.") }, CONDITION.OTP_COUNTER: { "type": "str", "desc": _("Action is triggered, if the counter of the token " "equals this setting. Can also be " "'>100' or '<99' for no exact match.") }, CONDITION.LAST_AUTH: { "type": "str", "desc": _("Action is triggered, if the last authentication of " "the token is older than 7h, 10d or 1y.") }, CONDITION.COUNT_AUTH: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the tokeninfo field 'count_auth' is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.COUNT_AUTH_SUCCESS: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the tokeninfo field " "'count_auth_success' is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.COUNT_AUTH_FAIL: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the difference between the tokeninfo " "field 'count_auth' and 'count_auth_success is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.TOKENINFO: { "type": "str", "desc": _("This condition can check any arbitrary tokeninfo " "field. You need to enter something like " "'<fieldname> == <fieldvalue>', '<fieldname> > " "<fieldvalue>' or '<fieldname> < <fieldvalue>'") }, CONDITION.DETAIL_ERROR_MESSAGE: { "type": "str", "desc": _("Here you can enter a regular expression. The " "condition only applies if the regular expression " "matches the detail->error->message in the response.") }, CONDITION.DETAIL_MESSAGE: { "type": "str", "desc": _("Here you can enter a regular expression. The " "condition only applies if the regular expression " "matches the detail->message in the response.") }, CONDITION.CLIENT_IP: { "type": "str", "desc": _("Trigger the action, if the client IP matches.") } } return cond
def test_02_token(self): r = get_token_list() self.assertTrue("privacyidea.lib.tokens.totptoken" in r, r) self.assertTrue("privacyidea.lib.tokens.hotptoken" in r, r) # check modules mlist = get_token_module_list() mod_name = "privacyidea.lib.tokens.totptoken" module = importlib.import_module(mod_name) self.assertTrue(module in mlist, mlist) # r = get_resolver_classes() # self.assertTrue(UserResolver in r, r) # self.assertTrue(PWResolver in r, r) # get_token_class_dict (classes, types) = get_token_class_dict() self.assertTrue( 'privacyidea.lib.tokens.hotptoken.HotpTokenClass' in classes, classes) self.assertTrue( classes.get('privacyidea.lib.tokens.hotptoken.HotpTokenClass') == HotpTokenClass, classes) self.assertTrue( 'privacyidea.lib.tokens.totptoken.TotpTokenClass' in classes, classes) self.assertTrue( classes.get('privacyidea.lib.tokens.totptoken.TotpTokenClass') == TotpTokenClass, classes) self.assertTrue( 'privacyidea.lib.tokens.hotptoken.HotpTokenClass' in types, types) self.assertTrue( 'privacyidea.lib.tokens.totptoken.TotpTokenClass' in types, types) self.assertTrue( types.get('privacyidea.lib.tokens.hotptoken' '.HotpTokenClass') == "hotp", types) self.assertTrue( types.get('privacyidea.lib.tokens.totptoken' '.TotpTokenClass') == "totp", types) types = get_token_types() self.assertTrue("totp" in types, types) self.assertTrue("hotp" in types, types) # Now the resolver types are contained. self.assertTrue("pi_token_types" in current_app.config, current_app.config) types = get_token_types() self.assertTrue("totp" in types, types) self.assertTrue("hotp" in types, types) # At the beginning the token classes are not cached. self.assertFalse("pi_token_classes" in current_app.config, current_app.config) r = get_token_classes() self.assertTrue(TotpTokenClass in r, r) self.assertTrue(HotpTokenClass in r, r) # Now the token classes are cached self.assertTrue("pi_token_classes" in current_app.config, current_app.config) r = get_token_classes() self.assertTrue(TotpTokenClass in r, r) self.assertTrue(HotpTokenClass in r, r)
def conditions(cls): """ The UserNotification can filter for conditions like * type of logged in user and * successful or failed value.success allowed types are str, multi, text, regexp :return: dict """ realms = get_realms() cond = { "realm": { "type": "str", "desc": _("The user realm, for which this event should apply."), "value": list(realms) }, "tokenrealm": { "type": "multi", "desc": _("The token realm, for which this event should " "apply."), "value": [{"name": r} for r in realms.keys()] }, CONDITION.TOKENTYPE: { "type": "multi", "desc": _("The type of the token."), "value": [{"name": r} for r in get_token_types()] }, "logged_in_user": { "type": "str", "desc": _("The logged in user is of the following type."), "value": (ROLE.ADMIN, ROLE.USER) }, CONDITION.RESULT_VALUE: { "type": "str", "desc": _("The result.value within the response is " "True or False."), "value": ("True", "False") }, CONDITION.RESULT_STATUS: { "type": "str", "desc": _("The result.status within the response is " "True or False."), "value": ("True", "False") }, "token_locked": { "type": "str", "desc": _("Check if the max failcounter of the token is " "reached."), "value": ("True", "False") }, CONDITION.TOKEN_HAS_OWNER: { "type": "str", "desc": _("The token has a user assigned."), "value": ("True", "False") }, CONDITION.TOKEN_IS_ORPHANED: { "type": "str", "desc": _("The token has a user assigned, but the user does " "not exist in the userstore anymore."), "value": ("True", "False") }, CONDITION.TOKEN_VALIDITY_PERIOD: { "type": "str", "desc": _("Check if the token is within its validity period."), "value": ("True", "False") }, "serial": { "type": "regexp", "desc": _("Action is triggered, if the serial matches this " "regular expression.") }, CONDITION.USER_TOKEN_NUMBER: { "type": "str", "desc": _("Action is triggered, if the user has this number " "of tokens assigned.") }, CONDITION.OTP_COUNTER: { "type": "str", "desc": _("Action is triggered, if the counter of the token " "equals this setting.") }, CONDITION.LAST_AUTH: { "type": "str", "desc": _("Action is triggered, if the last authentication of " "the token is older than 7h, 10d or 1y.") }, CONDITION.COUNT_AUTH: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the tokeninfo field 'count_auth' is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.COUNT_AUTH_SUCCESS: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the tokeninfo field " "'count_auth_success' is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.COUNT_AUTH_FAIL: { "type": "str", "desc": _("This can be '>100', '<99', or '=100', to trigger " "the action, if the difference between the tokeninfo " "field 'count_auth' and 'count_auth_success is " "bigger than 100, less than 99 or exactly 100.") }, CONDITION.TOKENINFO: { "type": "str", "desc": _("This condition can check any arbitrary tokeninfo " "field. You need to enter something like " "'<fieldname> == <fieldvalue>', '<fieldname> > " "<fieldvalue>' or '<fieldname> < <fieldvalue>'") }, CONDITION.DETAIL_ERROR_MESSAGE: { "type": "str", "desc": _("Here you can enter a regular expression. The " "condition only applies if the regular expression " "matches the detail->error->message in the response.") }, CONDITION.DETAIL_MESSAGE: { "type": "str", "desc": _("Here you can enter a regular expression. The " "condition only applies if the regular expression " "matches the detail->message in the response.") } } return cond
def test_02_get_tokens(self): # get All tokens tokenobject_list = get_tokens() # Check if these are valid tokentypes self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) for token_object in tokenobject_list: self.assertTrue(token_object.type in get_token_types(), token_object.type) # get assigned tokens tokenobject_list = get_tokens(assigned=True) self.assertTrue(len(tokenobject_list) == 0, tokenobject_list) # get unassigned tokens tokenobject_list = get_tokens(assigned=False) self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) # pass the wrong parameter # This will ignore the filter! tokenobject_list = get_tokens(assigned="True") self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) # get tokens of type HOTP tokenobject_list = get_tokens(tokentype="hotp") self.assertTrue(len(tokenobject_list) == 0, tokenobject_list) # get tokens of type TOTP tokenobject_list = get_tokens(tokentype="totp") self.assertTrue(len(tokenobject_list) > 0, tokenobject_list) # Search for tokens in realm db_token = Token("hotptoken", tokentype="hotp", userid=1000, resolver=self.resolvername1, realm=self.realm1) db_token.update_otpkey(self.otpkey) db_token.save() tokenobject_list = get_tokens(realm=self.realm1) self.assertTrue(len(tokenobject_list) == 1, tokenobject_list) self.assertTrue(tokenobject_list[0].type == "hotp", tokenobject_list[0].type) # get tokens for a given serial number tokenobject_list = get_tokens(serial="hotptoken") self.assertTrue(len(tokenobject_list) == 1, tokenobject_list) # ...but not in an unassigned state! tokenobject_list = get_tokens(serial="hotptoken", assigned=False) self.assertTrue(len(tokenobject_list) == 0, tokenobject_list) # get the tokens for the given user tokenobject_list = get_tokens( user=User(login="******", realm=self.realm1)) self.assertTrue(len(tokenobject_list) == 1, tokenobject_list) # get tokens for a given tokeninfo of the token!!! token = init_token({ "type": "yubikey", "serial": "yk1", "yubikey.prefix": "vv123456", "otpkey": self.otpkey }) self.assertEqual(token.token.serial, "yk1") tokenobject_list = get_tokens(tokeninfo={"yubikey.prefix": "vv123456"}) self.assertEqual(len(tokenobject_list), 1) self.assertEqual(tokenobject_list[0].get_tokeninfo("yubikey.prefix"), "vv123456") remove_token("yk1") # Tokeninfo with more than one entry is not supported self.assertRaises(privacyIDEAError, get_tokens, tokeninfo={ "key1": "value1", "key2": "value2" })
def conditions(cls): """ The UserNotification can filter for conditions like * type of logged in user and * successful or failed value.success allowed types are str, multi, text, regexp :return: dict """ realms = get_realms() cond = { "realm": { "type": "str", "desc": _("The user realm, for which this event should apply."), "value": realms.keys() }, "tokenrealm": { "type": "multi", "desc": _("The token realm, for which this event should " "apply."), "value": [{ "name": r } for r in realms.keys()] }, CONDITION.TOKENTYPE: { "type": "multi", "desc": _("The type of the token."), "value": [{ "name": r } for r in get_token_types()] }, "logged_in_user": { "type": "str", "desc": _("The logged in user is of the following type."), "value": (ROLE.ADMIN, ROLE.USER) }, "result_value": { "type": "str", "desc": _("The result.value within the response is " "True or False."), "value": ("True", "False") }, "token_locked": { "type": "str", "desc": _("Check if the max failcounter of the token is " "reached."), "value": ("True", "False") }, CONDITION.TOKEN_HAS_OWNER: { "type": "str", "desc": _("The token has a user assigned."), "value": ("True", "False") }, CONDITION.TOKEN_IS_ORPHANED: { "type": "str", "desc": _("The token has a user assigned, but the user does " "not exist in the userstore anymore."), "value": ("True", "False") }, "serial": { "type": "regexp", "desc": _("Action is triggered, if the serial matches this " "regular expression.") }, CONDITION.USER_TOKEN_NUMBER: { "type": "str", "desc": _("Action is triggered, if the user has this number " "of tokens assigned.") }, CONDITION.OTP_COUNTER: { "type": "str", "desc": _("Action is triggered, if the counter of the token " "equals this setting.") } } return cond