Source code for securid.jsontoken

#!/usr/bin/env python

import json
import os
import os.path
from typing import Any, Dict, Optional, Union

from .exceptions import InvalidSeed, InvalidSerial, ParseException
from .token import SERIAL_LENGTH, AbstractTokenFile, Token
from .utils import Bytes, fromisoformat

__all__ = [
    "JSONTokenFile",
]


[docs] class JSONTokenFile(AbstractTokenFile): """ Handler for JSON file format Example: { "digits": 6, "exp_date": "2035-12-31", "pin": 1234, "period": 60, "secret": [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25], "serial": "000512377827", "issuerInt": "myorg", "label": "myaccount", "type": "SecurID" } """ filename: Optional[str] token: Token def __init__( self, filename: Optional[str] = None, data: Union[bytes, bytearray, str, Dict[str, Any], None] = None, token: Optional[Token] = None, ) -> None: """ :param filename: JSON file path :param data: token as string in JSON format or as a dictionary :param token: Token instance """ if token is not None: self.filename = None self.token = token elif data is not None: if isinstance(data, str): data = bytes(data, "ascii") self.filename = None self.token = self.json_decode_token(data) elif filename is not None: self.filename = os.path.expanduser(filename) data = self.parse_file(self.filename) self.token = self.json_decode_token(data)
[docs] @classmethod def parse_file(cls, filename: str) -> bytes: """ Parse JSON file, return content as string :param filename: JSON file path """ with open(filename, "rb") as f: return f.read()
@classmethod def json_decode_token(cls, data: Union[Bytes, Dict[str, Any]]) -> Token: try: if isinstance(data, dict): dct = data else: dct = json.loads(data) token = Token( digits=dct["digits"], interval=dct["period"], exp_date=fromisoformat(dct["exp_date"]) if dct.get("exp_date") else None, seed=bytes(dct["secret"]), serial=dct["serial"], issuer=dct.get("issuerInt"), label=dct.get("label"), pin=dct.get("pin"), ) return token except json.decoder.JSONDecodeError as ex: raise ParseException(ex)
[docs] def get_token(self, password: Optional[str] = None) -> Token: """ Return the Token instance :param password: optional password for decrypting the token """ return self.token
[docs] def export_token(self) -> bytes: """ Export token as JSON """ if not self.token.seed: raise InvalidSeed("Missing seed") if not self.token.serial: raise InvalidSerial("Missing serial") if len(self.token.serial) != SERIAL_LENGTH: raise InvalidSerial("Serial length != {}".format(SERIAL_LENGTH)) data = { "digits": self.token.digits, "period": self.token.interval, "exp_date": self.token.exp_date.isoformat() if self.token.exp_date else "", "secret": [x for x in self.token.seed], "serial": self.token.serial, "type": "SecurID", } if self.token.issuer is not None: data["issuerInt"] = self.token.issuer if self.token.label is not None: data["label"] = self.token.label if self.token.pin is not None: data["pin"] = self.token.pin j = json.dumps(data, sort_keys=True) return bytes(j, "ascii")