Skip to content

Commit

Permalink
Update process to add a repository (#134)
Browse files Browse the repository at this point in the history
* Add GitHub App credentials

* Add Github service to generate an installation access token

* Add Author#list_repositories

* Add link to install GitHub App

* Add 'installation_id' to Authors

* Store the 'installation_id' returned when installing the GitHub App

* Modify Github service to use Author's installation_id

* Remove Sessions::Authors since no longer required with GitHub App installation

* Remove 'Login with GitHub' button from settings sidebar

* Remove omniauth-github gem

* Modify Auth::GithubController#create to create new Author

* Require installation_id for Author model

* Change wording from 'Login with GitHub' to 'Link GitHub account'

* Add request test for Auth::GithubController

* Add 'owner' column to Repository

* Remove 'git_url' and 'token' columns from Repository

* Update Settings::Authors::RepositoriesController#index to use GitHub App

* Update Settings::Authors::RepositoriesController#new to use GitHub App

* Extract available repositories into separate view and action

* Revert changes to create and index specs for Settings::Authors::Repositories

* Change index for repository name from author_id to owner

* Modify Settings::Authors::Repositories#available_repositories to only show repos not yet added

* Remove sessions related to author_id

* Protect new repository page behind before_action

* Add set_available_repositories method

* Combine new and edit forms

* Modify update repository process to only allow title and branch to be modified

* Update RemoveRepoJob

* Update RepositoryDirectory#define to use owner instead of author

* Update GetGithubDescriptionJob, CloneGithubRepoJob, PullGithubRepoJob

* Use Repository.full_name when possible

* Update CreateGithubWebhookJob, TestGithubWebhookJob

* Replace 'repository.author.github_username' by 'repository.owner'

* Update RespondWebhookPushJob

* Update system test for Repositories#update

* Update system tests for Settings::Authors::Repositories#update

* Update request test for Webhooks::GithubController

* Remove sessions related to author_id

* Remove unused VCR cassettes

* Split VCR cassettes into smaller files for each HTTP request

* Modify location of VCR and WebMock being turned on/off

* Update Rails credentials

* Display alert if GitHub App is removed from author's GitHub account

* Modify Auth::GithubController to handle update in installation_id and github_username

* Modify installation_id in Author factory

* Modify Auth::GithubController to accept callbacks without installation_id

* Create GithubInstallations table

* Create a GithubInstallation when an author is created

* Add reference GithubInstallation on Repositories

* Modify logic to use GithubInstallation instead of Author

* Remove 'installation_id' from Authors

* Remove 'owner' from Repositories

* Rename factories to use singular file name

* Modify storage path for repos to include Author's github_username

* Replace Repository uuid by uid from GitHub

* Add model tests for GitHubInstallation

* Add model tests for Author

* Update GithubInstallation methods to use uid and full_name

* Add uid to Repository upon creation

* Modify available repositories view

* Remove creation & testing of GitHub webhook from creation of Repository process

* Modify system test for creating Repository

* Rename RemoveRepoJob to RemoveDirectoryJob

* Remove hook_id and author_id from Repositories

* Update logic of Auth::GithubController

* Refactor Webhooks::GithubController

* Modify Webhook::GithubController to respond to 'create installation' event

* Modify Webhook::GithubController to respond to 'delete installation' event

* Keep repositories if GithubInstallation is deleted

* Add AuthorMailer#github_installation_deleted

* Add tests for AuthorMailer#github_installation_deleted

* Refactor Webhooks::GithubController

* Modify Webhooks::GithubController to response to 'rename repository' event

* Modify Webhooks::GithubController to response to 'delete repository' event

* Refactor Webhooks::GithubController

* Add AuthorMailer#repository_deleted

* Change level of logs for repository events

* Update RespondWebhookPushJob

* Fix request test for delete repository event

* Refactor Webhooks::GithubController

* Modify push event to use github_installation_id

* Extract code into RespondWebhookRenameRepoJob

* Fix warning from Capybara::RackTest::Node#set

* Build OctokitHelpers for tests with git_clone and git_pull

* Update job tests to use OctokitHelpers

* Add Webhooks::GithubController#find_github_installation

* Change name of job RespondWebhookRenameRepoJob

* Add GithubInstallation#list_repository_directories

* Add RenameGithubInstallationUsernameJob

* Modify Author and GithubInstallation factories

* Fix RenameGithubInstallationUsernameJob

* Modify Webhooks::GithubController to respond to 'rename installation_target' events

* Update GithubInstallation#github_client to show up to 100 results per page

* Add GitHub App related environment variables

* Update deployment docs
  • Loading branch information
jp524 committed Feb 26, 2024
1 parent e79b234 commit 70e38a2
Show file tree
Hide file tree
Showing 129 changed files with 2,801 additions and 1,851 deletions.
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ FactoryBot/AssociationStyle:
EnforcedStyle: explicit

Metrics/AbcSize:
CountRepeatedAttributes: false
Exclude:
- app/controllers/webauthn/**

Expand All @@ -32,6 +33,8 @@ Metrics/BlockLength:

Metrics/ClassLength:
Max: 130
Exclude:
- app/controllers/webhooks/github_controller.rb

Metrics/MethodLength:
Exclude:
Expand Down
7 changes: 2 additions & 5 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,13 @@ gem 'git'
# Toolkit for the GitHub API
gem 'octokit'

# Catches Faraday exceptions and retries requests
# HTTP client
gem 'faraday'
gem 'faraday-retry'

# Parse front matter from files
gem 'front_matter_parser'

# Authentication using OmniAuth
gem 'omniauth-github'
gem 'omniauth-rails_csrf_protection'

# Authentication using WebAuthn standard
gem 'webauthn'

Expand Down
30 changes: 1 addition & 29 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ GEM
mini_mime (1.1.5)
minitest (5.22.2)
msgpack (1.7.2)
multi_xml (0.6.0)
mutex_m (0.2.0)
net-http (0.4.1)
uri
Expand All @@ -237,30 +236,10 @@ GEM
racc (~> 1.4)
nokogiri (1.16.2-x86_64-linux)
racc (~> 1.4)
oauth2 (2.0.9)
faraday (>= 0.17.3, < 3.0)
jwt (>= 1.0, < 3.0)
multi_xml (~> 0.5)
rack (>= 1.2, < 4)
snaky_hash (~> 2.0)
version_gem (~> 1.1)
octokit (8.1.0)
base64
faraday (>= 1, < 3)
sawyer (~> 0.9)
omniauth (2.1.1)
hashie (>= 3.4.6)
rack (>= 2.2.3)
rack-protection
omniauth-github (2.0.1)
omniauth (~> 2.0)
omniauth-oauth2 (~> 1.8)
omniauth-oauth2 (1.8.0)
oauth2 (>= 1.4, < 3)
omniauth (~> 2.0)
omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
openssl (3.2.0)
openssl-signature_algorithm (1.3.0)
openssl (> 2.0)
Expand All @@ -277,8 +256,6 @@ GEM
nio4r (~> 2.0)
racc (1.7.3)
rack (2.2.8)
rack-protection (3.1.0)
rack (~> 2.2, >= 2.2.4)
rack-session (1.0.2)
rack (< 3)
rack-test (2.1.0)
Expand Down Expand Up @@ -426,9 +403,6 @@ GEM
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4)
snaky_hash (2.0.1)
hashie
version_gem (~> 1.1, >= 1.1.1)
sprockets (4.2.1)
concurrent-ruby (~> 1.0)
rack (>= 2.2.4, < 4)
Expand Down Expand Up @@ -457,7 +431,6 @@ GEM
unicode-display_width (2.5.0)
uri (0.13.0)
vcr (6.2.0)
version_gem (1.1.3)
warden (1.2.9)
rack (>= 2.0.9)
web-console (4.2.1)
Expand Down Expand Up @@ -507,15 +480,14 @@ DEPENDENCIES
debug
devise
factory_bot_rails
faraday
faraday-retry
front_matter_parser
git
importmap-rails
jbuilder
letter_opener
octokit
omniauth-github
omniauth-rails_csrf_protection
pg (~> 1.5)
puma (~> 6.4)
rack_session_access
Expand Down
63 changes: 63 additions & 0 deletions app/controllers/auth/github_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
module Auth
class GithubController < ApplicationController
before_action :verify_params, :verify_user, :verify_author

def create
github_uid, github_username = user_info

Author.create!(user_id: current_user.id, github_uid:, github_username:)
redirect_to settings_author_repositories_path, notice: 'Author account successfully added.'
rescue ActiveRecord::RecordInvalid => e
logger.error "Could not create Author for user ##{current_user.id}: #{e}"
end

private

def access_token
client_id = ENV.fetch('GITHUB_CLIENT_ID', Rails.application.credentials.dig(:github, :client_id))
client_secret = ENV.fetch('GITHUB_CLIENT_SECRET', Rails.application.credentials.dig(:github, :client_secret))
request_params = { client_id:, client_secret:, code: params[:code] }

conn = Faraday.new(url: 'https://github.com')
response = conn.post('/login/oauth/access_token', request_params, { Accept: 'application/json' })

token_data = JSON.parse(response.body)
token_data['access_token']
end

def user_info
client = Octokit::Client.new(access_token:)
user = client.user
[user['id'].to_s, user['login']]
rescue Octokit::Unauthorized, Octokit::Forbidden => e
logger.error "Could not get GitHub user info to create Author: #{e}"
end

def verify_user
return if user_signed_in?

notice = <<~MSG
If you approved a GitHub App installation for another user, thank you. Otherwise please log in to continue.
MSG
redirect_to root_path, notice:
end

def verify_author
return if current_user.author.nil?

github_uid, _github_username = user_info
if current_user.author.github_uid == github_uid
redirect_to settings_author_repositories_path, notice: 'This installation will be added to your author account.'
else
alert = <<~MSG
You are already an author on KNEWHUB with the GitHub username #{current_user.author.github_username}. Only one GitHub account can be linked per author account.
MSG
redirect_to root_path, alert:
end
end

def verify_params
redirect_to root_path, alert: 'Invalid request.' if params[:code].nil?
end
end
end
21 changes: 11 additions & 10 deletions app/controllers/collections_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ class CollectionsController < ApplicationController
layout 'collections'

def index
file_path = "#{params[:owner]}/#{params[:name]}/index"
render_not_found and return unless valid_render?(file_path, params[:owner], params[:name])
file_path = "#{params[:author_username]}/#{params[:owner]}/#{params[:name]}/index"
render_not_found and return unless valid_render?(file_path, params[:author_username], params[:owner], params[:name])

extract_markdown_file(file_path)
render file_path
end

def show
file_path = "#{params[:owner]}/#{params[:name]}/#{params[:path]}"
render_not_found and return unless valid_render?(file_path, params[:owner], params[:name])
file_path = "#{params[:author_username]}/#{params[:owner]}/#{params[:name]}/#{params[:path]}"
render_not_found and return unless valid_render?(file_path, params[:author_username], params[:owner], params[:name])

RequestPath.store(request)
show_actions(file_path)
Expand Down Expand Up @@ -47,15 +47,16 @@ def file_exists?(file_path)
|| File.exist?(Rails.root.join("repos/#{file_path}.#{request.format.to_sym}").to_s)
end

def repository_visible?(owner, name)
author_id = Author.find_by(github_username: owner).id
@repository = Repository.find_by(author_id:, name:)

def repository_visible?(author_username, owner, name)
github_installation_id = GithubInstallation.includes(:author)
.find_by(username: owner, author: { github_username: author_username })
.id
@repository = Repository.find_by(github_installation_id:, name:)
@repository.visible?
end

def valid_render?(file_path, owner, name)
return true if file_exists?(file_path) && repository_visible?(owner, name)
def valid_render?(file_path, author_username, owner, name)
return true if file_exists?(file_path) && repository_visible?(author_username, owner, name)

false
end
Expand Down
6 changes: 4 additions & 2 deletions app/controllers/concerns/author_authentication.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ module AuthorAuthentication
helper_method :current_author

def require_author_authentication
redirect_to root_path, alert: 'Please log in with GitHub.' unless author_logged_in?
redirect_to root_path, alert: 'Please link your GitHub account.' unless author_logged_in?
end

def current_author
@current_author ||= Author.find(session[:author_id]) if session[:author_id]
return if current_user.nil?

current_user.author
end

def author_logged_in?
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/repositories_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def set_repository

def require_author_or_admin_authentication
repository = Repository.find(params[:id])
return if administrator_signed_in? || current_author.id == repository.author_id
return if administrator_signed_in? || current_author.id == repository.author.id

redirect_to root_path, alert: 'Please log in as an author or administrator.'
end
Expand Down
1 change: 0 additions & 1 deletion app/controllers/sessions/administrators_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ def create_without_multi_factor_enabled(administrator)

def destroy_other_sessions
session[:user_id] = nil if session[:user_id]
session[:author_id] = nil if session[:author_id]
end
end
end
21 changes: 0 additions & 21 deletions app/controllers/sessions/authors_controller.rb

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ def index
private

def set_repository
@repository = Repository.find_by(id: params[:repository_id], author_id: current_author.id)
@repository = Repository.includes(:github_installation)
.find_by(id: params[:repository_id],
github_installation: { author_id: current_author.id })
end
end
end
Expand Down
Loading

0 comments on commit 70e38a2

Please sign in to comment.