# -*- 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## This code is free software; you can redistribute it and/or# modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE# License as published by the Free Software Foundation; either# version 3 of the License, or any later version.## This code is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU AFFERO GENERAL PUBLIC LICENSE for more details.## You should have received a copy of the GNU Affero General Public# License along with this program. If not, see <http://www.gnu.org/licenses/>.#__doc__="""The SSHKeyTokenClass provides a TokenClass that stores the publicSSH key. This can be used to manage SSH keys and retrieve the public ssh keyto import it to authorized keys files.The code is tested in tests/test_lib_tokens_ssh"""importloggingfromedumfa.libimport_fromedumfa.api.lib.utilsimportgetParamfromedumfa.lib.errorimportTokenAdminErrorfromedumfa.lib.logimportlog_withfromedumfa.lib.tokenclassimportTokenClass,ROLLOUTSTATE,AUTHENTICATIONMODEfromedumfa.lib.policyimportSCOPE,ACTION,GROUPlog=logging.getLogger(__name__)optional=Truerequired=False##TODO: We should save a fingerprint of the SSH Key in the encrypted OTP# field, so that we can be sure, that the public ssh key was not changed in# the database!
[docs]classSSHkeyTokenClass(TokenClass):""" The SSHKeyTokenClass provides a TokenClass that stores the public SSH key. This can be used to manage SSH keys and retrieve the public ssh key to import it to authorized keys files. """mode=[AUTHENTICATIONMODE.AUTHENTICATE]using_pin=Falsedef__init__(self,db_token):TokenClass.__init__(self,db_token)self.set_type("sshkey")
[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: dictionary """res={'type':'sshkey','title':'SSHkey Token','description':_('SSH Public Key: The public SSH key.'),'config':{},'user':['enroll'],# This tokentype is enrollable in the UI for...'ui_enroll':["admin","user"],'policy':{SCOPE.ENROLL:{ACTION.MAXTOKENUSER:{'type':'int','desc':_("The user may only have this maximum number of SSH keys assigned."),'group':GROUP.TOKEN},ACTION.MAXACTIVETOKENUSER:{'type':'int','desc':_("The user may only have this maximum number of active SSH keys assigned."),'group':GROUP.TOKEN}}},}ifkey:ret=res.get(key,{})else:ifret=='all':ret=resreturnret
[docs]defupdate(self,param):""" The key holds the public ssh key and this is required The key probably is of the form "ssh-rsa BASE64 comment" """# We need to save the token, so that we can later add the tokeninfo# Otherwise we might not have created the DB entry, yet and we would# be missing the token.idself.token.save()getParam(param,"sshkey",required)key_elem=param.get("sshkey").split(" ",2)ifkey_elem[0]notin["ssh-rsa","ssh-ed25519","ecdsa-sha2-nistp256","sk-ecdsa-sha2-nistp256@openssh.com","sk-ssh-ed25519@openssh.com"]:self.token.rollout_state=ROLLOUTSTATE.BROKENself.token.save()raiseTokenAdminError("The keytype you specified is not supported.")iflen(key_elem)<2:self.token.rollout_state=ROLLOUTSTATE.BROKENself.token.save()raiseTokenAdminError("Missing key.")key_type=key_elem[0]key=key_elem[1]iflen(key_elem)>2:key_comment=key_elem[2]else:key_comment=""# convert key to hexself.add_tokeninfo("ssh_key",key,value_type="password")self.add_tokeninfo("ssh_type",key_type)self.add_tokeninfo("ssh_comment",key_comment)# call the parents functionTokenClass.update(self,param)
[docs]@log_with(log)defget_sshkey(self):""" returns the public SSH key :return: SSH pub key :rtype: string """ti=self.get_tokeninfo()key_type=ti.get("ssh_type")key_comment=ti.get("ssh_comment")# get the ssh key directly, otherwise it will not be decryptedsshkey=self.get_tokeninfo("ssh_key")r="{0!s}{1!s}".format(key_type,sshkey)ifkey_comment:r+=" "+key_commentreturnr