initial commit
This commit is contained in:
119
billinglayer/python/jwt/jwt.py
Normal file
119
billinglayer/python/jwt/jwt.py
Normal file
@@ -0,0 +1,119 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2017 Gehirn Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
from typing import AbstractSet, Any, Dict, Optional
|
||||
|
||||
from jwt.utils import (
|
||||
get_time_from_int,
|
||||
)
|
||||
from .exceptions import (
|
||||
JWSEncodeError,
|
||||
JWSDecodeError,
|
||||
JWTEncodeError,
|
||||
JWTDecodeError,
|
||||
)
|
||||
from .jwk import AbstractJWKBase
|
||||
from .jws import JWS
|
||||
|
||||
|
||||
class JWT:
|
||||
|
||||
def __init__(self):
|
||||
self._jws = JWS()
|
||||
|
||||
def encode(self, payload: Dict[str, Any],
|
||||
key: Optional[AbstractJWKBase] = None, alg='HS256',
|
||||
optional_headers: Optional[Dict[str, str]] = None) -> str:
|
||||
if not isinstance(self, JWT): # pragma: no cover
|
||||
# https://github.com/GehirnInc/python-jwt/issues/15
|
||||
raise RuntimeError(
|
||||
'encode must be called on a jwt.JWT() instance. '
|
||||
'Do jwt.JWT().encode(...)')
|
||||
if not isinstance(payload, dict): # pragma: no cover
|
||||
raise TypeError('payload must be a dict')
|
||||
if not (key is None
|
||||
or isinstance(key, AbstractJWKBase)): # pragma: no cover
|
||||
raise TypeError(
|
||||
'key must be an instance of a class implements '
|
||||
'jwt.AbstractJWKBase')
|
||||
if not (optional_headers is None
|
||||
or isinstance(optional_headers, dict)): # pragma: no cover
|
||||
raise TypeError('optional_headers must be a dict')
|
||||
|
||||
try:
|
||||
message = json.dumps(payload).encode('utf-8')
|
||||
except ValueError as why:
|
||||
raise JWTEncodeError(
|
||||
'payload must be able to be encoded to JSON') from why
|
||||
|
||||
optional_headers = optional_headers and optional_headers.copy() or {}
|
||||
optional_headers['typ'] = 'JWT'
|
||||
try:
|
||||
return self._jws.encode(message, key, alg, optional_headers)
|
||||
except JWSEncodeError as why:
|
||||
raise JWTEncodeError('failed to encode to JWT') from why
|
||||
|
||||
def decode(self, message: str, key: Optional[AbstractJWKBase] = None,
|
||||
do_verify=True, algorithms: Optional[AbstractSet[str]] = None,
|
||||
do_time_check: bool = True) -> Dict[str, Any]:
|
||||
if not isinstance(self, JWT): # pragma: no cover
|
||||
# https://github.com/GehirnInc/python-jwt/issues/15
|
||||
raise RuntimeError(
|
||||
'decode must be called on a jwt.JWT() instance. '
|
||||
'Do jwt.JWT().decode(...)')
|
||||
if not isinstance(message, str): # pragma: no cover
|
||||
raise TypeError('message must be a str')
|
||||
if not (key is None
|
||||
or isinstance(key, AbstractJWKBase)): # pragma: no cover
|
||||
raise TypeError(
|
||||
'key must be an instance of a class implements '
|
||||
'jwt.AbstractJWKBase')
|
||||
|
||||
# utc now with timezone
|
||||
now = datetime.now(timezone.utc)
|
||||
try:
|
||||
message_bin = self._jws.decode(message, key, do_verify, algorithms)
|
||||
except JWSDecodeError as why:
|
||||
raise JWTDecodeError('failed to decode JWT') from why
|
||||
try:
|
||||
payload = json.loads(message_bin.decode('utf-8'))
|
||||
except ValueError as why:
|
||||
raise JWTDecodeError(
|
||||
'a payload of the JWT is not valid JSON') from why
|
||||
|
||||
# The "exp" (expiration time) claim identifies the expiration time on
|
||||
# or after which the JWT MUST NOT be accepted for processing.
|
||||
if 'exp' in payload and do_time_check:
|
||||
try:
|
||||
exp = get_time_from_int(payload['exp'])
|
||||
except TypeError:
|
||||
raise JWTDecodeError("Invalid Expired value")
|
||||
if now >= exp:
|
||||
raise JWTDecodeError("JWT Expired")
|
||||
|
||||
# The "nbf" (not before) claim identifies the time before which the JWT
|
||||
# MUST NOT be accepted for processing.
|
||||
if 'nbf' in payload and do_time_check:
|
||||
try:
|
||||
nbf = get_time_from_int(payload['nbf'])
|
||||
except TypeError:
|
||||
raise JWTDecodeError('Invalid "Not valid yet" value')
|
||||
if now < nbf:
|
||||
raise JWTDecodeError("JWT Not valid yet")
|
||||
|
||||
return payload
|
||||
Reference in New Issue
Block a user