From a1e7c6a97ad65bfca1e6dd6840ab44f0da674e71 Mon Sep 17 00:00:00 2001
From: Alexander Medvednikov <alexander@vlang.io>
Date: Fri, 1 Jan 2021 04:21:13 +0100
Subject: [PATCH] 1

---
 gitly.v        |   3 +-
 oauth.v        |  49 +++------
 repo_db.v      | 284 ++++++++++++++++++++++++-------------------------
 security_log.v |   8 +-
 user.v         |  14 +++
 5 files changed, 174 insertions(+), 184 deletions(-)

diff --git a/gitly.v b/gitly.v
index c0607239..f8e7f8b7 100644
--- a/gitly.v
+++ b/gitly.v
@@ -38,7 +38,6 @@ mut:
 	settings      GitlySettings
 	file_log      log.Log
 	cli_log       log.Log
-pub mut:
 	db            sqlite.DB
 	logged_in     bool
 	user          User
@@ -143,7 +142,7 @@ pub fn (mut app App) init() {
 	app.page_gen_time = ''
 	app.info('\n\ninit() url=$url')
 	app.path = ''
-	app.info('path=$app.path')
+	// app.info('path=$app.path')
 	app.logged_in = app.logged_in()
 	app.repo = Repo{}
 	app.user = User{}
diff --git a/oauth.v b/oauth.v
index 2166e308..df3c3733 100644
--- a/oauth.v
+++ b/oauth.v
@@ -24,23 +24,18 @@ pub fn (mut app App) oauth() vweb.Result {
 	code := app.query['code']
 	state := app.query['state']
 	if code == '' {
-		app.security_log({
-			user_id: app.user.id
-			kind: .empty_oauth_code
-		})
+		app.security_log(user_id: app.user.id, kind: .empty_oauth_code)
 		app.info('Code is empty')
 		return app.r_home()
 	}
-	csrf := app.get_cookie('csrf') or {
-		return app.r_home()
-	}
+	csrf := app.get_cookie('csrf') or { return app.r_home() }
 	if csrf != state || csrf == '' {
-		app.security_log({
+		app.security_log(
 			user_id: app.user.id
 			kind: .wrong_oauth_state
 			arg1: 'csrf=$csrf'
 			arg2: 'state=$state'
-		})
+		)
 		return app.r_home()
 	}
 	req := OAuthRequest{
@@ -69,34 +64,21 @@ pub fn (mut app App) oauth() vweb.Result {
 		app.info(user_js.text)
 		return app.text('Received $user_js.status_code error while attempting to contact GitHub')
 	}
-	gh_user := json.decode(GitHubUser, user_js.text) or {
-		return app.r_home()
-	}
+	gh_user := json.decode(GitHubUser, user_js.text) or { return app.r_home() }
 	println('gh user:')
 	println(user_js.text)
 	println(gh_user)
 	if gh_user.email.trim_space().len == 0 {
-		app.security_log({
-			user_id: app.user.id
-			kind: .empty_oauth_email
-			arg1: user_js.text
-		})
+		app.security_log(user_id: app.user.id, kind: .empty_oauth_email, arg1: user_js.text)
 		app.info('Email is empty')
-		return app.r_home()
-	}
-	mut user := app.find_user_by_email(gh_user.email) or {
-		User{}
+		// return app.r_home()
 	}
+	mut user := app.find_user_by_github_username(gh_user.username) or { User{} }
 	if !user.is_github {
-		app.security_log({
-			user_id: user.id
-			kind: .registered_via_github
-			arg1: user_js.text
-		})
+		// Register a new user via github
+		app.security_log(user_id: user.id, kind: .registered_via_github, arg1: user_js.text)
 		app.add_user(gh_user.username, '', [gh_user.email], true)
-		user = app.find_user_by_email(gh_user.email) or {
-			return app.r_home()
-		}
+		user = app.find_user_by_github_username(gh_user.username) or { return app.r_home() }
 		app.update_user_avatar(gh_user.avatar, user.id)
 	}
 	ip := app.client_ip(user.id.str()) or {
@@ -104,11 +86,7 @@ pub fn (mut app App) oauth() vweb.Result {
 		return app.r_home()
 	}
 	app.auth_user(user, ip)
-	app.security_log({
-		user_id: user.id
-		kind: .logged_in_via_github
-		arg1: user_js.text
-	})
+	app.security_log(user_id: user.id, kind: .logged_in_via_github, arg1: user_js.text)
 	return app.r_home()
 }
 
@@ -126,6 +104,7 @@ fn (mut app App) update_settings() {
 	only_gh_login := if app.settings.only_gh_login { 1 } else { 0 }
 	repo_storage_path := app.settings.repo_storage_path
 	sql app.db {
-		update GitlySettings set oauth_client_id = oauth_client_id, oauth_client_secret = oauth_client_secret, repo_storage_path = repo_storage_path where id == id
+		update GitlySettings set oauth_client_id = oauth_client_id, oauth_client_secret = oauth_client_secret,
+		repo_storage_path = repo_storage_path where id == id
 	}
 }
diff --git a/repo_db.v b/repo_db.v
index 512aa391..96435f34 100644
--- a/repo_db.v
+++ b/repo_db.v
@@ -8,175 +8,176 @@ fn (mut app App) create_table(name string, fields []string) {
 
 fn (mut app App) create_tables() {
 	app.create_table('Repo', [
-		'id integer primary key'
-		"git_dir text default ''"
-		"name text default ''"
-		"description text default ''"
-		'user_id int default 0'
-		"user_name text default ''"
-		'primary_branch text default ""'
-		'is_public int default 0'
-		'nr_views int default 0'
-		'nr_commits int default 0'
-		'nr_open_issues int default 0'
-		'nr_tags int default 0'
-		'nr_releases int default 0'
-		'nr_open_prs int default 0'
-		'webhook_secret text default ""'
-		'nr_branches int default 0'
-		'nr_contributors int default 0'
-		"created_at int default (strftime('%s', 'now'))"
+		'id integer primary key',
+		"git_dir text default ''",
+		"name text default ''",
+		"description text default ''",
+		'user_id int default 0',
+		"user_name text default ''",
+		'primary_branch text default ""',
+		'is_public int default 0',
+		'nr_views int default 0',
+		'nr_commits int default 0',
+		'nr_open_issues int default 0',
+		'nr_tags int default 0',
+		'nr_releases int default 0',
+		'nr_open_prs int default 0',
+		'webhook_secret text default ""',
+		'nr_branches int default 0',
+		'nr_contributors int default 0',
+		"created_at int default (strftime('%s', 'now'))",
 	])
-    // unix time default now
+	// unix time default now
 	app.create_table('File', [
-		'id integer primary key'
-		"name text default ''"
-		'repo_id int default 0'
-		"parent_path text default ''"
-		"branch text default ''"
-		'is_dir int default 0'
-		"last_hash text default ''"
-		"last_msg text default ''"
-		"last_time int default 0"
-		'size int default 0'
-		'nr_contributors int default 0'
-		'nr_views int default 0'
-		'UNIQUE(parent_path, name, repo_id, branch) ON CONFLICT REPLACE'
+		'id integer primary key',
+		"name text default ''",
+		'repo_id int default 0',
+		"parent_path text default ''",
+		"branch text default ''",
+		'is_dir int default 0',
+		"last_hash text default ''",
+		"last_msg text default ''",
+		'last_time int default 0',
+		'size int default 0',
+		'nr_contributors int default 0',
+		'nr_views int default 0',
+		'UNIQUE(parent_path, name, repo_id, branch) ON CONFLICT REPLACE',
 	])
 	//"created_at int default (strftime('%s', 'now'))"
 	app.create_table('Issue', [
-		'id integer primary key'
-		'author_id int default 0'
-		'is_pr int default 0'
-		'repo_id int default 0'
-		"title text default ''"
-		"text text default ''"
-		'created_at integer default 0'
-		'nr_comments int default 0'
+		'id integer primary key',
+		'author_id int default 0',
+		'is_pr int default 0',
+		'repo_id int default 0',
+		"title text default ''",
+		"text text default ''",
+		'created_at integer default 0',
+		'nr_comments int default 0',
 	])
 	//		"created_at int default (strftime('%s', 'now'))"
 	app.create_table('Commit', [
-		'id integer primary key'
-		'author_id int default 0'
-		"author text default ''"
-		"hash text default ''"
-		'repo_id int default 0'
-		"message text default ''"
-		"created_at int default (strftime('%s', 'now'))"
-		'UNIQUE(hash, repo_id)'
+		'id integer primary key',
+		'author_id int default 0',
+		"author text default ''",
+		"hash text default ''",
+		'repo_id int default 0',
+		"message text default ''",
+		"created_at int default (strftime('%s', 'now'))",
+		'UNIQUE(hash, repo_id)',
 	])
 	// author text default '' is to to avoid joins
 	app.create_table('LangStat', [
-		'id integer primary key'
-		'repo_id int default 0'
-		'name text default ""'
-		'nr_lines int default 0'
-		'pct int default 0'
-		'color text default ""'
-		'UNIQUE(repo_id, name) ON CONFLICT REPLACE'
+		'id integer primary key',
+		'repo_id int default 0',
+		'name text default ""',
+		'nr_lines int default 0',
+		'pct int default 0',
+		'color text default ""',
+		'UNIQUE(repo_id, name) ON CONFLICT REPLACE',
 	])
 	app.create_table('User', [
-		'id integer primary key'
-		'name text default ""'
-		'username text default ""'
-		'password text default ""'
-		'avatar text default ""'
-		'nr_posts integer default 0'
-		'last_post_time integer default 0'
-		'nr_namechanges integer default 0'
-		'last_namechange_time integer default 0'
-		'is_github int default 0'
-		'is_blocked int default 0'
-		'is_registered int default 0'
-		'is_admin int default 0'
-		'login_attempts int default 0'
-		'UNIQUE(username)'
-		])
+		'id integer primary key',
+		'name text default ""',
+		'username text default ""',
+		'password text default ""',
+		'avatar text default ""',
+		'nr_posts integer default 0',
+		'last_post_time integer default 0',
+		'nr_namechanges integer default 0',
+		'last_namechange_time integer default 0',
+		'is_github int default 0',
+		'is_blocked int default 0',
+		'is_registered int default 0',
+		'is_admin int default 0',
+		'login_attempts int default 0',
+		'github_username text default ""',
+		'UNIQUE(username)',
+	])
 	app.create_table('Email', [
-		'id integer primary key'
-		'user integer default 0'
-		'email text default ""'
-		'UNIQUE(email)'
+		'id integer primary key',
+		'user integer default 0',
+		'email text default ""',
+		'UNIQUE(email)',
 	])
 	app.create_table('Contributor', [
-		'id integer primary key'
-		'user integer default 0'
-		'repo integer default 0'
-		'UNIQUE(user, repo)'
+		'id integer primary key',
+		'user integer default 0',
+		'repo integer default 0',
+		'UNIQUE(user, repo)',
 	])
 	app.create_table('Tag', [
-		'id integer primary key'
-		'name text default ""'
-		'hash text default ""'
-		'user_id integer default 0'
-		'repo_id integer default 0'
-		'date integer default 0'
-		'UNIQUE(name, repo_id)'
+		'id integer primary key',
+		'name text default ""',
+		'hash text default ""',
+		'user_id integer default 0',
+		'repo_id integer default 0',
+		'date integer default 0',
+		'UNIQUE(name, repo_id)',
 	])
 	app.create_table('Release', [
-		'id integer primary key'
-		'tag_id integer not null'
-		'repo_id integer not null'
-		'notes text default ""'
-		'UNIQUE(tag_id, repo_id)'
+		'id integer primary key',
+		'tag_id integer not null',
+		'repo_id integer not null',
+		'notes text default ""',
+		'UNIQUE(tag_id, repo_id)',
 	])
 	app.create_table('SshKey', [
-		'id integer primary key'
-		'user integer default 0'
-		'title text default ""'
-		'sshkey text default ""'
-		'is_deleted integer default 0'
+		'id integer primary key',
+		'user integer default 0',
+		'title text default ""',
+		'sshkey text default ""',
+		'is_deleted integer default 0',
 	])
 	app.create_table('Comment', [
-		'id integer primary key'
-		'author_id integer default 0'
-		'issue_id integer default 0'
-		'created_at integer default 0'
-		'text text default ""'
+		'id integer primary key',
+		'author_id integer default 0',
+		'issue_id integer default 0',
+		'created_at integer default 0',
+		'text text default ""',
 	])
 	app.create_table('Branch', [
-		'id integer primary key'
-		'repo_id integer default 0'
-		'name text default ""'
-		'author text default ""'
-		'hash text default ""'
-		'date integer default 0'
-		'UNIQUE(repo_id, name)'
+		'id integer primary key',
+		'repo_id integer default 0',
+		'name text default ""',
+		'author text default ""',
+		'hash text default ""',
+		'date integer default 0',
+		'UNIQUE(repo_id, name)',
 	])
 	app.create_table('Visit', [
-		'id integer primary key'
-		'repo_id integer default 0'
-		"url text default ''"
-		"referer text default ''"
-		'created_at integer default 0'
+		'id integer primary key',
+		'repo_id integer default 0',
+		"url text default ''",
+		"referer text default ''",
+		'created_at integer default 0',
 	])
 	app.create_table('GitlySettings', [
-		'id integer primary key'
-		'oauth_client_id text default ""'
-		'oauth_client_secret text default ""'
-		'only_gh_login int default 1'
-		'repo_storage_path text default "./repos"'
+		'id integer primary key',
+		'oauth_client_id text default ""',
+		'oauth_client_secret text default ""',
+		'only_gh_login int default 1',
+		'repo_storage_path text default "./repos"',
 	])
 	app.create_table('Token', [
-		'id integer primary key'
-		'user_id integer default 0'
-		"value text defaut ''"
-		'ip text default ""'
+		'id integer primary key',
+		'user_id integer default 0',
+		"value text defaut ''",
+		'ip text default ""',
 	])
 	app.create_table('Token2', [
-		'id integer primary key'
-		'user_id integer default 0'
-		"value text defaut ''"
-		'ip text default ""'
+		'id integer primary key',
+		'user_id integer default 0',
+		"value text defaut ''",
+		'ip text default ""',
 	])
 	app.create_table('SecurityLog', [
-		'id integer primary key'
-		'user_id integer default 0'
-		"kind int default 0"
-		"ip text default ''"
-		"arg1 text default ''"
-		"arg2 text default ''"
-		"created_at int default (strftime('%s', 'now'))"
+		'id integer primary key',
+		'user_id integer default 0',
+		'kind int default 0',
+		"ip text default ''",
+		"arg1 text default ''",
+		"arg2 text default ''",
+		"created_at int default (strftime('%s', 'now'))",
 	])
 }
 
@@ -194,7 +195,10 @@ fn (mut app App) update_repo_in_db(repo &Repo) {
 	nr_contributors := repo.nr_contributors
 	nr_commits := repo.nr_commits
 	sql app.db {
-		update Repo set description = desc, nr_views = nr_views, is_public = is_public, webhook_secret = webhook_secret, nr_tags = nr_tags, nr_open_issues = nr_open_issues, nr_open_prs = nr_open_prs, nr_releases = nr_releases, nr_contributors = nr_contributors, nr_commits = nr_commits, nr_branches = nr_branches where id == id
+		update Repo set description = desc, nr_views = nr_views, is_public = is_public, webhook_secret = webhook_secret,
+		nr_tags = nr_tags, nr_open_issues = nr_open_issues, nr_open_prs = nr_open_prs, nr_releases = nr_releases,
+		nr_contributors = nr_contributors, nr_commits = nr_commits, nr_branches = nr_branches
+		where id == id
 	}
 }
 
@@ -262,19 +266,19 @@ fn (mut app App) retrieve_repo(id int) Repo {
 
 fn (mut app App) inc_repo_views(repo_id int) {
 	sql app.db {
-		update Repo set nr_views=nr_views+1 where id == repo_id
+		update Repo set nr_views = nr_views + 1 where id == repo_id
 	}
 }
 
 fn (mut app App) inc_file_views(file_id int) {
 	sql app.db {
-		update File set nr_views=nr_views+1 where id == file_id
+		update File set nr_views = nr_views + 1 where id == file_id
 	}
 }
 
 fn (mut app App) inc_repo_issues(repo_id int) {
 	sql app.db {
-		update Repo set nr_open_issues=nr_open_issues+1 where id==repo_id
+		update Repo set nr_open_issues = nr_open_issues + 1 where id == repo_id
 	}
 	app.repo.nr_open_issues++
 }
@@ -311,29 +315,23 @@ fn (mut app App) delete_repo(id int, path string, name string) {
 		delete from Repo where id == id
 	}
 	app.info('Removed repo entry ($id, $name)')
-
 	// Remove all commits
 	sql app.db {
 		delete from Commit where repo_id == id
 	}
 	app.info('Removed repo commits ($id, $name)')
-
 	// Remove all issues & prs
 	app.delete_repo_issues(id)
 	app.info('Removed repo issues ($id, $name)')
-
 	// Remove all branches
 	app.delete_repo_branches(id)
 	app.info('Removed repo branches ($id, $name)')
-
 	// Remove all releases
 	app.delete_repo_releases(id)
 	app.info('Removed repo releases ($id, $name)')
-
 	// Remove all files
 	app.delete_repo_files(id)
 	app.info('Removed repo files ($id, $name)')
-
 	// Remove physical files
 	app.delete_repo_folder(path)
 	app.info('Removed repo folder ($id, $name)')
diff --git a/security_log.v b/security_log.v
index 4b4b4701..a02234ec 100644
--- a/security_log.v
+++ b/security_log.v
@@ -5,13 +5,13 @@ module main
 import vweb
 
 enum SecurityLogKind {
-	registered
+	registered // 0
 	logged_in
-	registered_via_github
+	registered_via_github // 2
 	logged_in_via_github
-	wrong_password
+	wrong_password // 4
 	wrong_oauth_state
-	empty_oauth_code
+	empty_oauth_code // 6
 	empty_oauth_email
 }
 
diff --git a/user.v b/user.v
index b657945a..f5b78da5 100644
--- a/user.v
+++ b/user.v
@@ -11,6 +11,7 @@ struct User {
 	id                   int
 	name                 string
 	username             string
+	github_username      string
 	password             string
 	is_github            bool
 	is_registered        bool
@@ -80,6 +81,7 @@ pub fn (mut app App) add_user(username string, password string, emails []string,
 			password: password
 			is_registered: true
 			is_github: github
+			github_username: username
 		}
 		app.insert_user(user)
 		mut u := app.find_user_by_username(user.username) or {
@@ -243,6 +245,18 @@ pub fn (mut app App) find_user_by_id(id2 int) ?User {
 	return user
 }
 
+pub fn (mut app App) find_user_by_github_username(name string) ?User {
+	mut user := sql app.db {
+		select from User where github_username == name limit 1
+	}
+	if user.id == 0 {
+		return none
+	}
+	emails := app.find_user_emails(user.id)
+	user.emails = emails
+	return user
+}
+
 pub fn (mut app App) find_user_by_email(email string) ?User {
 	emails := sql app.db {
 		select from Email where email == email