#!/bin/bash

CLIENT_CREDENTIALS=''
CLIENT_ID=''
SCOPE=''
CLIENT_AUTHENTICATION_METHODS='client_secret_post'
AUTHORIZATION_GRANT_TYPES='authorization_code'
REDIRECT_URIS=''
LOGIN='f'
ACCESS_TOKEN_TIME_TO_LIVE=3600000000000 # 1h in nanoseconds
REFRESH_TOKEN_TIME_TO_LIVE=86400000000000 # 1day in nanoseconds

join_by_char() {
  local IFS="$1"
  shift
  echo "$*"
}

public_api_scopes() {
  local chosen_scopes=()
  local chosen_authorization_grant_types=()

  local use_oauth_flow_password=false
  local use_oauth_flow_client_credentials=false
  # scopes that explicitly need a user
  # -> password flow
  # -> authorization code flow
  local user_based_scopes=("people" "wiki" "news")
  for scope in "${user_based_scopes[@]}" ; do
    echo "Allow scopes for '$scope' ?"
    select yn in "Read" "Write" "Both" "None"; do
        case $yn in
            Read ) chosen_scopes+=("user:$scope.read"); use_oauth_flow_password=true; break;;
            Write ) chosen_scopes+=("user:$scope.write"); use_oauth_flow_password=true; break;;
            Both ) chosen_scopes+=("user:$scope.read" "user:$scope.write"); use_oauth_flow_password=true; break;;
            None ) break;;
        esac
    done
  done

  # scopes that do NOT need a user
  # client credentials flow
  local generic_based_scoped=()
  for scope in "${generic_based_scoped[@]}" ; do
    echo "Allow scopes for '$scope' ?"
    select yn in "Read" "Write" "Both" "None"; do
        case $yn in
            Read ) chosen_scopes+=("system:$scope.read"); use_oauth_flow_client_credentials=true; break;;
            Write ) chosen_scopes+=("system:$scope.write"); use_oauth_flow_client_credentials=true; break;;
            Both ) chosen_scopes+=("system:$scope.read" "system:$scope.write"); use_oauth_flow_client_credentials=true; break;;
            None ) break;;
        esac
    done
  done

  if [ "$use_oauth_flow_password" = true ]; then
      chosen_authorization_grant_types+=("password");
  fi

  if [ "$use_oauth_flow_client_credentials" = true ]; then
      chosen_authorization_grant_types+=("client_credentials");
  fi

  read -rep "Add redirect URIs? (comma separated; leave empty if not needed by client)`echo $'\n> '`" redirect_uris

  if [ -n "$redirect_uris" ]; then
    chosen_authorization_grant_types+=("authorization_code" "refresh_token");
    REDIRECT_URIS="$redirect_uris"
  fi

  SCOPE=$(join_by_char , "${chosen_scopes[@]}")
  AUTHORIZATION_GRANT_TYPES=$(join_by_char , "${chosen_authorization_grant_types[@]}")
}
print_help() {

cat << EOF
Usage:  $0 --client_id CLIENT_ID  [--import | --export | --api | --login | --public_api ] [--redirect_uri=uri] [--scope=scopes] [--grant_types=types]

Generate SQL-script to create an oauth_client. You will be asked for client-credentials.

  --import              Generate an oauth_client for user-import
  --export              Generate an oauth_client for user-export
  --api                 Generate an oauth_client for api access
  --login               Generate an oauth_client for "login with just"
  --public_api          Guided generation of a oauth_client for the Public API
  --redirect_uri        Comma separated list of customer provided redirect_uris
                          You have to specify a redirect_uri if using --login
  --grant_types         Comma separated list of OAuth2 Flows
  --scope               Comma separated list of OAuth2 Scopes a oauth_client should have. Scopes are not checked for
                          correctness and OAuth2 Flows must be given accordingly

OAuth2 Flows:
  - client_credentials
  - authorization_code
  - refresh_token
  - password

EXAMPLES

    1. Generate a client for api access (news/wiki/profile api):

        $0 --client_id api_client --api

    2. Generate a client for rest-api import

        $0 --client_id profile-import --import

    3. Generate a client for "Login with Just"

        $0 --client_id login_client --login

    4. Guided generation of a client for the Public API

        $0 --client_id public_api_client --public_api

    5. Manual generation of a client for the Public API

        $0 --client_id public_api_client --scope user:people.read,user:wiki.read --grant_types password
EOF
}

while [ $# -ne 0 ]
do
    case "$1" in
        -h| --help)
            print_help
            exit 0
            ;;
        -c|--client_id)
            CLIENT_ID=$2
            shift
            shift
            ;;
        --scope)
            SCOPE=$2
            shift
            shift
            ;;
        --grant_types)
            AUTHORIZATION_GRANT_TYPES=$2
            shift
            shift
            ;;
        --redirect_uri)
            REDIRECT_URIS=$2
            echo "redirect uri $REDIRECT_URIS"
            shift
            shift
            ;;
        --import)
            SCOPE='ROLE_USER_IMPORT'
            AUTHORIZATION_GRANT_TYPES='client_credentials'
            shift
            ;;
        --export)
            SCOPE='ROLE_USER_EXPORT'
            AUTHORIZATION_GRANT_TYPES='client_credentials'
            shift
            ;;
        --api)
            SCOPE='api'
            AUTHORIZATION_GRANT_TYPES='password'
            shift
            ;;
        --login)
            AUTHORIZATION_GRANT_TYPES='authorization_code,refresh_token'
            SCOPE='USER_LOGIN'
            LOGIN='t'
            shift
            ;;
        --public_api)
          public_api_scopes
          shift
          ;;
        -*|--*)
          echo "Unknown option $1"
          exit 1
          ;;
        *)
          POSITIONAL_ARGS+=("$1") # save positional arg
          shift # past argument
          ;;
    esac
done
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters



if [ "$LOGIN" = "t" ]; then
  if [ "x$REDIRECT_URIS" = "x" ]; then
    echo "Please enter a redirect uri with --redirect_uri REDIRECT_URI"
    exit 0
  fi
fi

if [ "x$CLIENT_CREDENTIALS" = "x" ]; then
    #ask for a CLIENT_CREDENTIALS
    HASHED_CLIENT_CREDENTIALS=`htpasswd -nBC 12 "" | tr -d ':\n'`
else
    #use the CLIENT_CREDENTIALS provided by command line
    HASHED_CLIENT_CREDENTIALS=`htpasswd -bnBC 12 "" $CLIENT_CREDENTIALS | tr -d ':\n'`
fi

if [ "x$CLIENT_ID" = "x" ]; then
  echo "Hashed password: $HASHED_CLIENT_CREDENTIALS"
  echo ""
  exit 0
fi

cat << EOF
CLIENT_ID: $CLIENT_ID
SCOPE: $SCOPE
AUTHORIZATION_GRANT_TYPES: $AUTHORIZATION_GRANT_TYPES
CLIENT_AUTHENTICATION_METHODS: $CLIENT_AUTHENTICATION_METHODS
REDIRECT_URIS: $REDIRECT_URIS
EOF

cat << EOF
-- Copy this script and execute it on the justconnect database.
-- This SQL script will insert or update a new OAuth client details record.

INSERT INTO justauth.oauth2_registered_client(id, client_id, client_name, client_secret, authorization_grant_types, client_authentication_methods, scopes, redirect_uris, access_token_time_to_live, refresh_token_time_to_live, tenant_id)
  SELECT gen_random_uuid(), '$CLIENT_ID', '$CLIENT_ID', CONCAT('{bcrypt}','$HASHED_CLIENT_CREDENTIALS'), '$AUTHORIZATION_GRANT_TYPES', '$CLIENT_AUTHENTICATION_METHODS' as methods, '$SCOPE','$REDIRECT_URIS',
  $ACCESS_TOKEN_TIME_TO_LIVE, $REFRESH_TOKEN_TIME_TO_LIVE,
  CASE WHEN tenantCount.count = 1 THEN global_tenant.id ELSE NULL END
  FROM
    (SELECT id from public.tenant LIMIT 1) as global_tenant,
    (SELECT count(id) as count FROM public.tenant) as tenantCount
  ON CONFLICT(client_id) DO UPDATE set
    client_secret=CONCAT('{bcrypt}','$HASHED_CLIENT_CREDENTIALS'),
    scopes='$SCOPE',
    authorization_grant_types='$AUTHORIZATION_GRANT_TYPES',
    client_authentication_methods='$CLIENT_AUTHENTICATION_METHODS',
    redirect_uris='$REDIRECT_URIS',
    access_token_time_to_live=$ACCESS_TOKEN_TIME_TO_LIVE,
    refresh_token_time_to_live=$REFRESH_TOKEN_TIME_TO_LIVE;

EOF
