Example #1
0
def _validate_metadata_values_are_valid(claims, required_metadata):
    """
    Validate metadata from the JWT claims that are required by the schema/runner.
    Ensures that the values adhere to the expected format.
    :param claims:
    :param required_metadata:
    """
    try:
        for metadata_field in required_metadata:
            name = metadata_field['name']
            claim = claims.get(name)
            if name not in claims:
                raise InvalidTokenException(
                    'Missing required key {} from claims'.format(name))

            logger.debug('parsing metadata', key=name, value=claim)
            VALIDATORS[metadata_field['validator']](claim)

    except (RuntimeError, ValueError, TypeError) as error:
        logger.error('Unable to parse metadata',
                     key=name,
                     value=claim,
                     exc_info=error)
        raise InvalidTokenException(
            'incorrect data in token for {}'.format(name)) from error
    except KeyError as key_error:
        error_msg = 'Invalid validator for schema metadata - {}'.format(
            key_error.args[0])
        logger.error(error_msg, exc_info=key_error)
        raise KeyError(error_msg) from key_error
Example #2
0
def login():
    """
    Initial url processing - expects a token parameter and then will authenticate this token. Once authenticated
    it will be placed in the users session
    :return: a 302 redirect to the next location for the user
    """
    # logging in again clears any session state
    if cookie_session:
        cookie_session.clear()

    decrypted_token = decrypt_token(request.args.get("token"))

    validate_jti(decrypted_token)

    try:
        runner_claims = validate_runner_claims(decrypted_token)
    except ValidationError as e:
        raise InvalidTokenException("Invalid runner claims") from e

    g.schema = load_schema_from_metadata(runner_claims)
    schema_metadata = g.schema.json["metadata"]

    try:
        questionnaire_claims = validate_questionnaire_claims(
            decrypted_token, schema_metadata)
    except ValidationError as e:
        raise InvalidTokenException("Invalid questionnaire claims") from e

    claims = {**runner_claims, **questionnaire_claims}

    schema_name = claims["schema_name"]
    tx_id = claims["tx_id"]
    ru_ref = claims["ru_ref"]
    case_id = claims["case_id"]

    logger.bind(
        schema_name=schema_name,
        tx_id=tx_id,
        ru_ref=ru_ref,
        case_id=case_id,
    )
    logger.info("decrypted token and parsed metadata")

    store_session(claims)

    cookie_session["theme"] = g.schema.json["theme"]
    cookie_session["survey_title"] = g.schema.json["title"]
    cookie_session["expires_in"] = get_session_timeout_in_seconds(g.schema)

    if claims.get("account_service_url"):
        cookie_session["account_service_url"] = claims.get(
            "account_service_url")

    if claims.get("account_service_log_out_url"):
        cookie_session["account_service_log_out_url"] = claims.get(
            "account_service_log_out_url")

    return redirect(url_for("questionnaire.get_questionnaire"))
def extract_kid_from_header(token):
    header = token.split('.')[:1][0]

    if not header:
        raise InvalidTokenException("Missing Headers")

    try:
        protected_header = base64url_decode(header).decode()

        protected_header_data = json.loads(protected_header)

        if 'kid' in protected_header:
            return protected_header_data['kid']
    except ValueError:
        raise InvalidTokenException("Invalid Header")

    raise InvalidTokenException("Missing kid")
Example #4
0
 def get_key_by_kid(self, purpose, kid, key_type):
     try:
         key = self.keys[kid]
         if key.purpose != purpose or key.key_type != key_type:
             raise InvalidTokenException
     except(KeyError, InvalidTokenException):
         raise InvalidTokenException("Invalid {} Key Identifier [{}] for Purpose [{}]".format(key_type, kid, purpose))
     else:
         return key
Example #5
0
    def _get_token_data(metadata):
        ru_ref = metadata.get('ru_ref')
        collection_exercise_sid = metadata.get('collection_exercise_sid')
        eq_id = metadata.get('eq_id')
        form_type = metadata.get('form_type')

        if ru_ref and collection_exercise_sid and eq_id and form_type:
            return collection_exercise_sid, eq_id, form_type, ru_ref

        logger.error(
            'missing values for ru_ref, collection_exercise_sid, form_type or eq_id',
            metadata=metadata)
        raise InvalidTokenException('Missing values in JWT token')
Example #6
0
    def decrypt_with_key(encrypted_token, key):
        """
        Decrypts JWE token with supplied key
        :param encrypted_token:
        :param key: A (:class:`jwcrypto.jwk.JWK`) decryption key or a password
        :returns: The payload of the decrypted token
        """
        try:
            jwe_token = jwe.JWE(algs=['RSA-OAEP', 'A256GCM'])
            jwe_token.deserialize(encrypted_token)

            jwe_token.decrypt(key)

            return jwe_token.payload.decode()
        except (ValueError, InvalidJWEData) as e:
            raise InvalidTokenException(str(e)) from e
Example #7
0
    def decrypt(encrypted_token, key_store=None, purpose=None):
        try:
            jwe_token = jwe.JWE(algs=['RSA-OAEP', 'A256GCM'])
            jwe_token.deserialize(encrypted_token)

            jwe_kid = extract_kid_from_header(encrypted_token)

            logger.info("Decrypting JWE kid is {}".format(jwe_kid))

            private_jwk = key_store.get_private_key_by_kid(purpose,
                                                           jwe_kid).as_jwk()

            jwe_token.decrypt(private_jwk)

            return jwe_token.payload.decode()
        except (ValueError, InvalidJWEData) as e:
            raise InvalidTokenException(str(e)) from e
Example #8
0
    def encrypt_with_key(payload, kid, key):
        try:
            logger.info(
                "Encrypting JWE with provided key and kid {}".format(kid))

            protected_header = {
                "alg": "RSA-OAEP",
                "enc": "A256GCM",
                "kid": kid,
            }

            token = jwe.JWE(plaintext=payload, protected=protected_header)

            token.add_recipient(key)
        except (ValueError, InvalidJWEData) as e:
            raise InvalidTokenException(str(e)) from e

        return token.serialize(compact=True)
Example #9
0
    def encrypt(payload, kid, key_store=None, purpose=None):
        try:
            logger.info("Encrypting JWE kid is {}".format(kid))

            public_jwk = key_store.get_public_key_by_kid(purpose, kid).as_jwk()

            protected_header = {
                "alg": "RSA-OAEP",
                "enc": "A256GCM",
                "kid": kid,
            }

            token = jwe.JWE(plaintext=payload, protected=protected_header)

            token.add_recipient(public_jwk)
        except (ValueError, InvalidJWEData) as e:
            raise InvalidTokenException(str(e)) from e

        return token.serialize(compact=True)
def decrypt(token, key_store, key_purpose, leeway=120):
    """This decrypts the provided jwe token, then decodes resulting jwt token and returns
    the payload.

    :param str token: The jwe token.
    :param key_store: The key store.
    :param str key_purpose: Context for the key.
    :param int leeway: Extra allowed time in seconds after expiration to account for clock skew.
    :return: The decrypted payload.

    """
    tokens = token.split('.')
    if len(tokens) != 5:
        raise InvalidTokenException("Incorrect number of tokens")

    decrypted_token = JWEHelper.decrypt(token, key_store, key_purpose)

    payload = JWTHelper.decode(decrypted_token, key_store, key_purpose, leeway)

    return payload
    def decode(jwt_token, key_store, purpose, leeway=None, check_claims={}):
        try:
            jwt_kid = extract_kid_from_header(jwt_token)

            logger.info("Decoding JWT kid is {}".format(jwt_kid))

            public_jwk = key_store.get_public_key_by_kid(purpose,
                                                         jwt_kid).as_jwk()

            signed_token = jwt.JWT(algs=['RS256'], check_claims=check_claims)

            if leeway:
                signed_token.leeway = leeway

            signed_token.deserialize(jwt_token, key=public_jwk)

            return json.loads(signed_token.claims)
        except (InvalidJWSObject, InvalidJWSSignature, JWTInvalidClaimFormat,
                JWTMissingClaim, JWTExpired, ValueError) as e:
            raise InvalidTokenException(str(e)) from e
Example #12
0
def _validate_metadata_is_present(metadata, required_metadata):
    """
    Validate that JWT claims contain the required metadata (both eq-runner and survey specific required metadata).
    :param metadata:
    :param required_metadata:
    """
    for metadata_field in required_metadata:
        name = metadata_field['name']
        if name == 'trad_as_or_ru_name':
            # either of 'trad_as' or 'ru_name' is required
            valid = bool(metadata.get('trad_as') or metadata.get('ru_name'))
        else:
            # Validate that the value is one of:
            # a) A boolean value
            # b) A non empty value
            value = metadata.get(name)
            valid = isinstance(value, bool) or bool(value)

        if not valid:
            raise InvalidTokenException(
                'Missing key/value for {}'.format(name))