#!/usr/bin/python3
# Copyright (c) 2018 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
"""
This script will accept some parameters and will print out some SQL you can run against the Ipsilon
database, and a token you can give to an application to authenticate against a service.
"""
import base64
import json
import os
import time
import uuid

import click


secret = base64.urlsafe_b64encode(os.urandom(64))[:64].decode()


template = """
Run this SQL against Ipsilon's database:

--------START CUTTING HERE--------
BEGIN;
insert into token values ('{uuid}','username','{username}');
insert into token values ('{uuid}','security_check','{secret}');
insert into token values ('{uuid}','client_id','{service_name}');
insert into token values ('{uuid}','expires_at','{expiration}');
insert into token values ('{uuid}','type','Bearer');
insert into token values ('{uuid}','issued_at','{now}');
insert into token values ('{uuid}','scope','{scope}');
COMMIT;
-------- END CUTTING HERE --------

"""


def validate_scopes(ctx, param, scopes):
    """
    Ensure that the user provided at least one scope.

    Args:
        ctx(click.core.Context): Unused.
        param (click.core.Option): Unused.
        scopes (tuple): The scopes provided by the user that we are validating.
    Raises:
        click.BadParameter: If the length of the scopes tuple is less than 1.
    """
    if len(scopes) < 1:
        raise click.BadParameter('At least one scope must be provided.')

    return scopes


@click.command()
@click.argument('service_name')
@click.option('--expiration', '-e', prompt='Number of days until expiration', type=int,
              help='The number of days from now until this token expires.')
@click.option('--scope', '-s', multiple=True, callback=validate_scopes,
              help='A scope to include for this token. May be supplied multiple times.')
@click.option('--no-openid', is_flag=True, help='Do not use "openid" as the first item in scope.')
@click.option('--username', '-u', default=None,
              help='The username associated with the token. Defaults to SERVICE_NAME@service.')
def generate_token(service_name, expiration, scope, no_openid, username):
    """
    Print out SQL to insert a token in the Ipsilon database, and the token itself.

    SERVICE_NAME is the name of the service that the token will be used by, (e.g., bodhi).
    """
    identifier = uuid.uuid4()

    now = int(time.time())
    expiration = now + (expiration * 24 * 3600)

    scope = list(scope)
    if not no_openid:
        scope.insert(0, 'openid')
    scope = json.dumps(scope)

    username = username or "{}@service".format(service_name)

    print(template.format(uuid=identifier, service_name=service_name, secret=secret,
                          expiration=expiration, scope=scope, username=username, now=now))

    print("Token: {}_{}\n".format(identifier, secret))


if __name__ == '__main__':
    generate_token()
