''' Exports Authenticator. '''

import base64
import falcon
from szurubooru.model.user import User
from szurubooru.services.errors import AuthError

class Authenticator(object):
    '''
    Authenticates every request and puts information on active user in the
    request context.
    '''

    def __init__(self, auth_service, user_service):
        self._auth_service = auth_service
        self._user_service = user_service

    def process_request(self, request, response):
        ''' Executed before passing the request to the API. '''
        request.context.user = self._get_user(request)

    def _get_user(self, request):
        if not request.auth:
            return self._create_anonymous_user()

        try:
            auth_type, user_and_password = request.auth.split(' ', 1)

            if auth_type.lower() != 'basic':
                raise falcon.HTTPBadRequest(
                    'Invalid authentication type',
                    'Only basic authorization is supported.')

            username, password = base64.decodebytes(
                user_and_password.encode('ascii')).decode('utf8').split(':')

            session = request.context.session
            return self._authenticate(session, username, password)
        except ValueError as err:
            msg = 'Basic authentication header value not properly formed. ' \
                + 'Supplied header {0}. Got error: {1}'
            raise falcon.HTTPBadRequest(
                'Malformed authentication request',
                msg.format(request.auth, str(err)))

    def _authenticate(self, session, username, password):
        ''' Tries to authenticate user. Throws AuthError for invalid users. '''
        user = self._user_service.get_by_name(session, username)
        if not user:
            raise AuthError('No such user.')
        if not self._auth_service.is_valid_password(user, password):
            raise AuthError('Invalid password.')
        return user

    def _create_anonymous_user(self):
        user = User()
        user.name = None
        user.access_rank = 'anonymous'
        user.password = None
        return user