# -*- coding: utf-8 -*-## License: AGPLv3# This file is part of eduMFA. eduMFA is a fork of privacyIDEA which was forked from LinOTP.# Copyright (c) 2024 eduMFA Project-Team# Previous authors by privacyIDEA project:## 2014 Cornelius Kölbel <cornelius@privacyidea.org>## Copyright (C) 2010 - 2014 LSE Leading Security Experts GmbH# License: LSE# contact: http://www.linotp.org# http://www.lsexperts.de# linotp@lsexperts.de"""This file contains the definition of the password token class"""importloggingfromedumfa.lib.cryptoimportzerome,safe_comparefromedumfa.lib.utilsimportto_unicodefromedumfa.lib.tokenclassimportTokenClassfromedumfa.lib.logimportlog_withfromedumfa.lib.decoratorsimportcheck_token_lockedfromedumfa.libimport_fromedumfa.lib.policyimportSCOPE,ACTION,GROUPfromedumfa.api.lib.prepolicyimport_generate_pin_from_policyfromedumfa.api.lib.utilsimportgetParamfromedumfa.lib.utilsimportis_trueoptional=Truerequired=False# We use an easier length of 12 for password tokensDEFAULT_LENGTH=12DEFAULT_CONTENTS='cn'log=logging.getLogger(__name__)
[docs]classPasswordTokenClass(TokenClass):""" This Token does use a fixed Password as the OTP value. In addition, the OTP PIN can be used with this token. This Token can be used for a scenario like losttoken """password_detail_key="password"# nosec B105 # key namedefault_length=DEFAULT_LENGTHdefault_contents=DEFAULT_CONTENTS
[docs]defcheck_password(self,password):""" :param password: :type password: str :return: result of password check: 0 if success, -1 if failed :rtype: int """res=-1key=self.secretObject.getKey()# getKey() returns bytes and since we can not assume, that the# password only contains printable characters, we need to compare# bytes strings here. This also avoids making another copy of 'key'.ifsafe_compare(key,password):res=0zerome(key)delkeyreturnres
[docs]@staticmethod@log_with(log)defget_class_info(key=None,ret='all'):""" returns a subtree of the token definition :param key: subsection identifier :type key: string :param ret: default return value, if nothing is found :type ret: user defined :return: subsection if key exists or user defined :rtype: dict or scalar """res={'type':'pw','title':'Password Token','description':_('A token with a fixed password. Can be ''combined with the OTP PIN. Is used for the ''lost token scenario.'),'init':{},'config':{},'user':[],# This tokentype is enrollable in the UI for...'ui_enroll':[],'policy':{SCOPE.ENROLL:{ACTION.MAXTOKENUSER:{'type':'int','desc':_("The user may only have this maximum number of password tokens assigned."),'group':GROUP.TOKEN},ACTION.MAXACTIVETOKENUSER:{'type':'int','desc':_("The user may only have this maximum number of active password tokens assigned."),'group':GROUP.TOKEN}}},}# I don't think we need to define the lost token policies here...ifkey:ret=res.get(key)else:ifret=='all':ret=resreturnret
[docs]@log_with(log,log_entry=False)defupdate(self,param):""" This method is called during the initialization process. :param param: parameters from the token init :type param: dict :return: None """genkey=is_true(getParam(param,"genkey",optional=True))ifgenkey:# Otherwise genkey and otpkey will raise an exception in# PasswordTokenClassdelparam["genkey"]type_prefix=self.get_class_type()length_param="{0!s}.length".format(type_prefix)contents_param="{0!s}.contents".format(type_prefix)iflength_paraminparam:size=param[length_param]delparam[length_param]else:size=self.otp_lenifcontents_paraminparam:contents=param[contents_param]delparam[contents_param]else:contents=self.otp_contentsparam["otpkey"]=_generate_pin_from_policy(contents,size=int(size))if"otpkey"inparam:param["otplen"]=len(param["otpkey"])TokenClass.update(self,param)
[docs]@log_with(log,log_entry=False)@check_token_lockeddefcheck_otp(self,anOtpVal,counter=None,window=None,options=None):""" This checks the static password :param anOtpVal: This contains the "OTP" value, which is the static password :return: result of password check, 0 in case of success, -1 if fail :rtype: int """secretHOtp=self.token.get_otpkey()sp=PasswordTokenClass.SecretPassword(secretHOtp)res=sp.check_password(anOtpVal)returnres
[docs]@log_with(log)defget_init_detail(self,params=None,user=None):""" At the end of the initialization we return the registration code. """response_detail=TokenClass.get_init_detail(self,params,user)secretHOtp=self.token.get_otpkey()password=secretHOtp.getKey()response_detail[self.password_detail_key]=to_unicode(password)returnresponse_detail