#!/bin/bash
##
## This script configures gitolite for use with Redmine
##
## It needs to be run as the gitolite user
##
## * the user keys are taken from the custom user field 'ssh-authorized-keys'
## * currently only one key per user supported
## * automatically creates one repo per project
## * repository names are identical to project identifier
## * currently just RW access for roles 'Developer' and 'Manager'
##

log() {
	echo " *** $0: $*" >&2
}

die() {
	log "$*"
	exit 1
}

CONFIG=/etc/redmine/default/redmine-gitolite

. $CONFIG || die "Could not load config: $CONFIG"

[ "$REDMINE_DB_HOST" ] || die "Missing config variable: REDMINE_DB_HOST"
[ "$REDMINE_DB_USER" ] || die "Missing config variable: REDMINE_DB_USER"
[ "$REDMINE_DB_PASS" ] || die "Missing config variable: REDMINE_DB_PASS"
[ "$REDMINE_DB_NAME" ] || die "Missing config variable: REDMINE_DB_NAME"
[ "$REDMINE_DB_TYPE" ] || die "Missing config variable: REDMINE_DB_TYPE"
[ "$GITOLITE_USER"   ] || die "Missing config variable: GITOLITE_USER"

MYSQL_COMMAND=mysql
PSQL_COMMAND=psql

GITOLITE_CFDIR=~/.gitolite
KEY_DIR=$GITOLITE_CFDIR/keydir/
CONF_FILE=$GITOLITE_CFDIR/conf/gitolite.conf
CONF_HEAD=$GITOLITE_CFDIR/conf/gitolite.conf.head
ADMIN_PUBKEY=~/gitolite-admin.pub

[ `whoami` != "$GITOLITE_USER" ] && die "Need to be run as user $GITOLITE_USER"

if [ "psql" = "$REDMINE_DB_TYPE" ]; then
  echo "$REDMINE_DB_HOST:5432:$REDMINE_DB_NAME:$REDMINE_DB_USER:$REDMINE_DB_PASS" > ~/.pgpass
  chmod 0600 ~/.pgpass
fi

setup_gitolite() {
	if [ -f ~/.gitolite.rc ] && [ -d ~/.gitolite ]; then
		return 0
	fi

	log "Gitolite doesnt seems to be set up yet"

	if [ ! -f $ADMIN_PUBKEY ]; then
		log "I need an admin pubkey in $ADMIN_PUBKEY"
		exit 4
	fi

	log "The following warnings on missing gl-conf for already existing repos can be ignored"
	log "That's just because the configuration isn't generated yet at this point"
	EDITOR="/bin/false" gl-setup $ADMIN_PUBKEY -q < /dev/null

	log "Removing unwanted autocreated repositories"
	rm -Rf ~/repositories/testing.git
	rm -Rf ~/repositories/gitolite-admin.git

	log "To trigger a fresh initialization, run: rm -Rf ~/.gitolite ~/gitolite ~/.gitolite.conf"
	log "and re-run this script again"

	log "Initial setup finished"
}

sql() {
  case "$REDMINE_DB_TYPE" in
  mysql)
    $MYSQL_COMMAND -N $REDMINE_DB_NAME -h$REDMINE_DB_HOST -u$REDMINE_DB_USER -p$REDMINE_DB_PASS
  ;;
  psql)
    $PSQL_COMMAND -h $REDMINE_DB_HOST -U $REDMINE_DB_USER -w -d $REDMINE_DB_NAME -A -t
  ;;
  esac
}

get_users() {
	echo "
	    SELECT
			users.login
		FROM users, custom_values, custom_fields
		WHERE
			users.status = 1 AND
			users.login IS NOT NULL AND
			users.login != '' AND
			custom_values.customized_id = users.id AND
			custom_values.customized_type = 'Principal' AND
			custom_values.custom_field_id = custom_fields.id AND
			custom_fields.name = 'ssh-authorized-keys'
	;" | sql
}

get_user_key() {
	echo "
	    SELECT
			custom_values.value
		FROM users, custom_values, custom_fields
		WHERE
			custom_values.value IS NOT NULL AND
			custom_values.customized_id = users.id AND
			custom_values.customized_type = 'Principal' AND
			custom_values.custom_field_id = custom_fields.id AND
			custom_fields.name = 'ssh-authorized-keys' AND
			users.login = '$1'
	;" | sql
}

write_keys() {
	local username="$1"
	local count="1"
	while read k ; do
		if [ "$k" ]; then
			mkdir -p $KEY_DIR
			echo "$k" > $KEY_DIR/$username@$count.pub
			let "count=count+1"
		fi
	done
}

sync_user_keys() {
	rm -f $KEY_DIR/*.pub 2>/dev/null
	for user in `get_users` ; do
		get_user_key "$user" | write_keys "$user"
	done
}

get_projects() {
	echo "SELECT identifier FROM projects where status=1" | sql
}

get_project_members_admin() {
	echo "SELECT DISTINCT
		    users.login
		FROM projects, users, roles, members, member_roles
		WHERE
		    projects.identifier = '$1' AND
		    projects.id = members.project_id AND
		    members.user_id = users.id AND
		    roles.id = member_roles.role_id AND
		    member_roles.member_id = members.id AND
		    roles.name IN ( 'Git-Admin', 'Git-Maintainer' );
	    ;" | sql
}

get_project_members_rw() {
	echo "SELECT DISTINCT
		    users.login
		FROM projects, users, roles, members, member_roles
		WHERE
		    projects.identifier = '$1' AND
		    projects.id = members.project_id AND
		    members.user_id = users.id AND
		    roles.id = member_roles.role_id AND
		    member_roles.member_id = members.id AND
		    roles.name IN ( 'Git-Write' ) AND
		    roles.name NOT IN ( 'Git-Admin', 'Git-Developer' )
	    ;" | sql
}

get_project_members_dev() {
        echo "SELECT DISTINCT
                    users.login
                FROM projects, users, roles, members, member_roles
                WHERE
                    projects.identifier = '$1' AND
                    projects.id = members.project_id AND
                    members.user_id = users.id AND
                    roles.id = member_roles.role_id AND
                    member_roles.member_id = members.id AND
                    roles.name IN ( 'Git-Developer' ) AND
                    roles.name NOT IN ( 'Git-Admin', 'Git-Maintainer' )
            ;" | sql
}


get_project_members_restricted() {
	echo "SELECT DISTINCT
		    users.login
		FROM projects, users, roles, members, member_roles
		WHERE
		    projects.identifier = '$1' AND
		    projects.id = members.project_id AND
		    members.user_id = users.id AND
		    roles.id = member_roles.role_id AND
		    member_roles.member_id = members.id AND
		    roles.name IN ( 'Git-Restricted' ) AND
		    roles.name NOT IN ( 'Git-Admin', 'Git-Write' )
	    ;" | sql
}

get_project_members_ro() {
	echo "SELECT DISTINCT
		    users.login
		FROM projects, users, roles, members, member_roles
		WHERE
		    projects.identifier = '$1' AND
		    projects.id = members.project_id AND
		    members.user_id = users.id AND
		    roles.id = member_roles.role_id AND
		    member_roles.member_id = members.id AND
		    roles.name IN ( 'Git-Readonly' ) AND
		    roles.name NOT IN ( 'Git-Admin', 'Git-Write' )
	    ;" | sql
}

create_conf() {
	echo "
# dont need these autogenerated repos
#repo gitolite-admin
#    RW+     =   gitolite-admin
#
#repo testing
#    RW+     =   @all

"
	for prj in `get_projects` ; do
		local rw_members=`get_project_members_rw "$prj"`
		local ro_members=`get_project_members_ro "$prj"`
		local ad_members=`get_project_members_admin "$prj"`
		local dev_members=`get_project_members_dev "$prj"`
		local rs_members=`get_project_members_restricted "$prj"`
		if [ "$rw_members" ] || [ "$ro_members" ] || [ "$ad_members" ] || [ "$dev_members" ]; then
			echo -e "repo $prj"

			if [ "$ad_members" ]; then
				echo -en "    RW+     =   "
				for m in $ad_members ; do
					echo -n "$m "
				done
				echo
			fi

			if [ "$rw_members" ]; then
				echo -en "    RW      =   "
				for m in $rw_members ; do
					echo -n "$m "
				done
				echo
			fi

			if [ "$ro_members" ]; then
				echo -en "    R       =   "
				for m in $ro_members ; do
					echo -n "$m "
				done
				echo
			fi

			if [ "$rs_members" ]; then
				for m in $rs_members ; do
					echo "    RW+ refs/heads/users/$m/* = $m"
					echo "    RW+ refs/tags/users/$m/* = $m"
				done
			fi

			if [ "$dev_members" ]; then
				echo -en "    - master = "
				for m in $dev_members ; do
					echo -n "$m "
				done
				echo " "
				echo -en "    RW      =   "
				for m in $dev_members ; do
					echo -n "$m "
				done
			fi
			echo
			echo
		fi
	done
}

fix_repos() {
	cd ~/repositories
	find -type d -name "*.git" | ( \
		while read d ; do
			( cd $d ; git config --unset receive.denyNonFastforwards )
		done
	)
}

## initial download and setup. will skip itself automatically, if already done
setup_gitolite

## synchronize user keys from redmine
sync_user_keys

## create gitolite configuration
create_conf > $CONF_FILE
GL_BINDIR=/usr/share/gitolite GL_RC=~/.gitolite.rc /usr/share/gitolite/gl-compile-conf

## fix the repositories
fix_repos
