<commit_before># Copyright (c) <NAME>
# See LICENSE for details.
import string
import time
import json

from abc import ABCMeta, abstractmethod
from twisted.web.resource import Resource

from .errors import InsecureConnectionError, MissingParameterError, InvalidParameterError, \
    InvalidTokenError, InvalidScopeError, UnsupportedGrantType, OK, MultipleParameterError


class TokenFactory(object):
    """ A factory that can generate tokens. """
    __metaclass__ = ABCMeta

    @abstractmethod
    def generateToken(self, lifetime, client, scope, additionalData=None):
        """
        Generate a new token. The generated token must comply to the specification
        (see https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/#token).
        See TokenResource::VALID_TOKEN_CHARS for a list of allowed characters in the token.
        :param lifetime: The lifetime of the new token in seconds or None for infinite lifetime.
        :param client: The client that gets the new token.
        :param scope: A list of scopes that the new token will grant access to.
        :param additionalData: Any additional data that was passes to OAuth2::grantAccess.
        :return: A new token as a string.
        """
        raise NotImplementedError()


class TokenStorage(object):
    """ An object that stores and manages tokens. """
    __metaclass__ = ABCMeta

    @abstractmethod
    def contains(self, token):
        """
        :param token: The token to validate.
        :return: True if the token is stored in this token storage, False otherwise.
        """
        raise NotImplementedError()

    @abstractmethod
    def hasAccess(self, token, scope):
        """
        Return True if the token is stored in this token storage
        and grants access to the given list of scopes (e.g. was
        store called with the token and at least the give scopes).

        :param token: The token to validate.
        :param scope: The scopes the token must grant access to.
        :return: True, if the token grants access to the scopes, False otherwise.
        :raises KeyError: If the token is not in the token store.
        """
        raise NotImplementedError()

    @abstractmethod
    def getTokenData(self, token):
        """
        Get the scope and additional data that was passed to
        store together with the given token.

        :param token: A token.
        :return: A tuple of the scope and the additional data that was stored alongside the token.
        :raises KeyError: If the token was not found in the token storage
        """
        raise NotImplementedError()

    @abstractmethod
    def store(self, token, client, scope, additionalData=None, expireTime=None):
        """
        Store the given token in the token storage alongside
        the optional additional data. If expireTime is not None,
        it marks the point in time in seconds since the epoch at
        which the token should expire
        (contains should then return False for this token).

        :param token: The token to store.
        :param client: The client that this token was made for.
        :param scope: The scope this token grants access to.
        :param additionalData: Optional additional data that
                               was passed to OAuth2.grantAccess.
        :param expireTime: Optionally the seconds since the epoch,
                           when the token should expire.
        :raises ValueError: If the token is not a string.
        """
        raise NotImplementedError()


class PersistentStorage(object):
    """
    A key value storage that can store data between a call to OAuth2.grantAccess
    and the corresponding POST request to the TokenResource from the client.
    """
    __metaclass__ = ABCMeta

    @abstractmethod
    def put(self, key, data, expireTime=None):
        """
        Store the given data with the given key.
        If expireTime is not None, it marks when the data
        should expire in seconds since the epoch.

        :param key: The key of the data.
        :param data: Some data.
        :param expireTime: Optionally the seconds since the epoch,
                           when the data should expire.
        """
        raise NotImplementedError()

    @abstractmethod
    def get(self, key):
        """
        Return the data that was previously stored with the given key.

        :param key: The key the data was stored at.
        :return: The data that was stored.
        :raises KeyError: If no data was stored with the key.
        """
        raise NotImplementedError()


class TokenResource(Resource, object):
    """
    This resource handles creation and refreshing of access tokens.

    If authTokenLifeTime is None, the tokens generated by this resource will never expire.
    Otherwise, the generated access token will have a lifetime in seconds as specified by
    authTokenLifeTime. Additionally, a refresh token will be generated with an unlimited
    lifetime. This refresh token can be used at this resource to generate additional
    access tokens with limited lifetime, if the old one expired.

    There are two scenarios in which this resource plays a role:

    A: 1. The client's authorization request was granted by the user
          and a code was generated in OAuth2.grantAccess.
       2. The client now generates a POST request to this resource,
          passing the code as an argument.
       3. This resource creates and stores an access token and returns it.
          Depending on authTokenLifeTime, a refresh token is also created,
          stored and returned.

    B: 1. The client has a refresh token and an access token which expired.
       2. The client generates a POST request to this resource,
          passing the refresh token as an argument.
       3. This resource creates and stores another access token and returns it.
    """
    VALID_TOKEN_CHARS = string.digits + string.ascii_letters + '-._~+/'
    tokenFactory = None
    persistentStorage = None
    allowInsecureRequestDebug = False
    refreshTokenStorage = None
    # This is the token storage singleton
    _OAuthTokenStorage = None
    clientStorage = None
    authTokenLifeTime = 3600

    def __init__(self, tokenFactory, persistentStorage, refreshTokenStorage, authTokenStorage,
                 clientStorage, authTokenLifeTime=3600, allowInsecureRequestDebug=False):
        """
        Create a new TokenResource.
        The given authTokenStorage will be used to check tokens when
        isAuthorized or the oauth2 is used. The authTokenLifeTime
        parameter governs the lifetime of generated tokens in seconds.
        If it is None, refresh tokens will be generated in addition to
        the access tokens with limited lifetimes.

        :param tokenFactory: A token factory. Will generate both access and refresh tokens.
        :param persistentStorage: Persistent storage to access data stored by OAuth2.grantAccess.
        :param refreshTokenStorage: A token storage for refresh tokens.
        :param authTokenStorage: A token storage for access tokens. Will be used as a singleton.
        :param clientStorage: A handle to the storage of known clients.
        :param authTokenLifeTime: Either lifetime in seconds or None for an unlimited lifetime.
        :param allowInsecureRequestDebug: If True, allow requests over insecure connections.
                                          Do NOT use in production!
        """
        super(TokenResource, self).__init__()
        self.allowedMethods = [b'POST']
        self.allowInsecureRequestDebug = allowInsecureRequestDebug
        self.refreshTokenStorage = refreshTokenStorage
        self.tokenFactory = tokenFactory
        self.persistentStorage = persistentStorage
        self.clientStorage = clientStorage
        self.authTokenLifeTime = authTokenLifeTime
        TokenResource._OAuthTokenStorage = authTokenStorage

    def render_POST(self, request):
        """
        Handle a POST request according to the OAuth2 specification.
        See the docstring of this class for more information.

        :param request: The POST request.
        :return: A response or NOT_DONE_YET
        """
        if not self.allowInsecureRequestDebug and not request.isSecure():
            return InsecureConnectionError().generate(request)
        if b'grant_type' not in request.args:
            return MissingParameterError(name='grant_type').generate(request)
        if len(request.args[b'grant_type']) != 1:
            return MultipleParameterError('grant_type').generate(request)
        grantType = request.args[b'grant_type'][0]
        if grantType == b'refresh_token':
            for argument in [b'client_id', b'client_secret', b'refresh_token']:
                if argument not in request.args:
                    return MissingParameterError(name=argument).generate(request)
            try: # TODO: Support client id and secret in HTTP Authentication
                client = self.clientStorage.getClient(request.args[b'client_id'][0].decode('utf-8'))
            except (KeyError, UnicodeDecodeError):
                return InvalidParameterError('client_id').generate(request)
            if client.clientSecret.encode('utf-8') != request.args[b'client_secret'][0]:
                return InvalidParameterError('client_secret').generate(request)<commit_msg>Dont process HEAD requests in the token resource<commit_after># Copyright (c) <NAME>
# See LICENSE for details.
import string
import time
import json

from abc import ABCMeta, abstractmethod
from twisted.web.resource import Resource

from .errors import InsecureConnectionError, MissingParameterError, InvalidParameterError, \
    InvalidTokenError, InvalidScopeError, UnsupportedGrantType, OK, MultipleParameterError


class TokenFactory(object):
    """ A factory that can generate tokens. """
    __metaclass__ = ABCMeta

    @abstractmethod
    def generateToken(self, lifetime, client, scope, additionalData=None):
        """
        Generate a new token. The generated token must comply to the specification
        (see https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/#token).
        See TokenResource::VALID_TOKEN_CHARS for a list of allowed characters in the token.
        :param lifetime: The lifetime of the new token in seconds or None for infinite lifetime.
        :param client: The client that gets the new token.
        :param scope: A list of scopes that the new token will grant access to.
        :param additionalData: Any additional data that was passes to OAuth2::grantAccess.
        :return: A new token as a string.
        """
        raise NotImplementedError()


class TokenStorage(object):
    """ An object that stores and manages tokens. """
    __metaclass__ = ABCMeta

    @abstractmethod
    def contains(self, token):
        """
        :param token: The token to validate.
        :return: True if the token is stored in this token storage, False otherwise.
        """
        raise NotImplementedError()

    @abstractmethod
    def hasAccess(self, token, scope):
        """
        Return True if the token is stored in this token storage
        and grants access to the given list of scopes (e.g. was
        store called with the token and at least the give scopes).

        :param token: The token to validate.
        :param scope: The scopes the token must grant access to.
        :return: True, if the token grants access to the scopes, False otherwise.
        :raises KeyError: If the token is not in the token store.
        """
        raise NotImplementedError()

    @abstractmethod
    def getTokenData(self, token):
        """
        Get the scope and additional data that was passed to
        store together with the given token.

        :param token: A token.
        :return: A tuple of the scope and the additional data that was stored alongside the token.
        :raises KeyError: If the token was not found in the token storage
        """
        raise NotImplementedError()

    @abstractmethod
    def store(self, token, client, scope, additionalData=None, expireTime=None):
        """
        Store the given token in the token storage alongside
        the optional additional data. If expireTime is not None,
        it marks the point in time in seconds since the epoch at
        which the token should expire
        (contains should then return False for this token).

        :param token: The token to store.
        :param client: The client that this token was made for.
        :param scope: The scope this token grants access to.
        :param additionalData: Optional additional data that
                               was passed to OAuth2.grantAccess.
        :param expireTime: Optionally the seconds since the epoch,
                           when the token should expire.
        :raises ValueError: If the token is not a string.
        """
        raise NotImplementedError()


class PersistentStorage(object):
    """
    A key value storage that can store data between a call to OAuth2.grantAccess
    and the corresponding POST request to the TokenResource from the client.
    """
    __metaclass__ = ABCMeta

    @abstractmethod
    def put(self, key, data, expireTime=None):
        """
        Store the given data with the given key.
        If expireTime is not None, it marks when the data
        should expire in seconds since the epoch.

        :param key: The key of the data.
        :param data: Some data.
        :param expireTime: Optionally the seconds since the epoch,
                           when the data should expire.
        """
        raise NotImplementedError()

    @abstractmethod
    def get(self, key):
        """
        Return the data that was previously stored with the given key.

        :param key: The key the data was stored at.
        :return: The data that was stored.
        :raises KeyError: If no data was stored with the key.
        """
        raise NotImplementedError()


class TokenResource(Resource, object):
    """
    This resource handles creation and refreshing of access tokens.

    If authTokenLifeTime is None, the tokens generated by this resource will never expire.
    Otherwise, the generated access token will have a lifetime in seconds as specified by
    authTokenLifeTime. Additionally, a refresh token will be generated with an unlimited
    lifetime. This refresh token can be used at this resource to generate additional
    access tokens with limited lifetime, if the old one expired.

    There are two scenarios in which this resource plays a role:

    A: 1. The client's authorization request was granted by the user
          and a code was generated in OAuth2.grantAccess.
       2. The client now generates a POST request to this resource,
          passing the code as an argument.
       3. This resource creates and stores an access token and returns it.
          Depending on authTokenLifeTime, a refresh token is also created,
          stored and returned.

    B: 1. The client has a refresh token and an access token which expired.
       2. The client generates a POST request to this resource,
          passing the refresh token as an argument.
       3. This resource creates and stores another access token and returns it.
    """
    VALID_TOKEN_CHARS = string.digits + string.ascii_letters + '-._~+/'
    tokenFactory = None
    persistentStorage = None
    allowInsecureRequestDebug = False
    refreshTokenStorage = None
    # This is the token storage singleton
    _OAuthTokenStorage = None
    clientStorage = None
    authTokenLifeTime = 3600

    def __init__(self, tokenFactory, persistentStorage, refreshTokenStorage, authTokenStorage,
                 clientStorage, authTokenLifeTime=3600, allowInsecureRequestDebug=False):
        """
        Create a new TokenResource.
        The given authTokenStorage will be used to check tokens when
        isAuthorized or the oauth2 is used. The authTokenLifeTime
        parameter governs the lifetime of generated tokens in seconds.
        If it is None, refresh tokens will be generated in addition to
        the access tokens with limited lifetimes.

        :param tokenFactory: A token factory. Will generate both access and refresh tokens.
        :param persistentStorage: Persistent storage to access data stored by OAuth2.grantAccess.
        :param refreshTokenStorage: A token storage for refresh tokens.
        :param authTokenStorage: A token storage for access tokens. Will be used as a singleton.
        :param clientStorage: A handle to the storage of known clients.
        :param authTokenLifeTime: Either lifetime in seconds or None for an unlimited lifetime.
        :param allowInsecureRequestDebug: If True, allow requests over insecure connections.
                                          Do NOT use in production!
        """
        super(TokenResource, self).__init__()
        self.allowedMethods = [b'POST']
        self.allowInsecureRequestDebug = allowInsecureRequestDebug
        self.refreshTokenStorage = refreshTokenStorage
        self.tokenFactory = tokenFactory
        self.persistentStorage = persistentStorage
        self.clientStorage = clientStorage
        self.authTokenLifeTime = authTokenLifeTime
        TokenResource._OAuthTokenStorage = authTokenStorage
        self.render_HEAD = None  # Disable automatic HEAD handling.

    def render_POST(self, request):
        """
        Handle a POST request according to the OAuth2 specification.
        See the docstring of this class for more information.

        :param request: The POST request.
        :return: A response or NOT_DONE_YET
        """
        if not self.allowInsecureRequestDebug and not request.isSecure():
            return InsecureConnectionError().generate(request)
        if b'grant_type' not in request.args:
            return MissingParameterError(name='grant_type').generate(request)
        if len(request.args[b'grant_type']) != 1:
            return MultipleParameterError('grant_type').generate(request)
        grantType = request.args[b'grant_type'][0]
        if grantType == b'refresh_token':
            for argument in [b'client_id', b'client_secret', b'refresh_token']:
                if argument not in request.args:
                    return MissingParameterError(name=argument).generate(request)
            try: # TODO: Support client id and secret in HTTP Authentication
                client = self.clientStorage.getClient(request.args[b'client_id'][0].decode('utf-8'))
            except (KeyError, UnicodeDecodeError):
                return InvalidParameterError('client_id').generate(request)
            if client.clientSecret.encode('utf-8') != request.args[b'client_secret'][0]:
                return InvalidParameterError('client_secret').generate(request)
{'fraction_non_alphanumeric': 0.05966044322208706, 'fraction_numerical': 0.002961865975564606, 'mean_word_length': 2.649488515730554, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7084836', 'n_tokens_mistral': 4602, 'n_tokens_neox': 4448, 'n_words': 2002}