Commit 12aa1f89 authored by Eric K Idema's avatar Eric K Idema Committed by Rémy Coutable

Import from Github using Personal Access Tokens.

This stands as an alternative to using OAuth to access a user's Github
repositories.  This is setup in such a way that it can be used without OAuth
configuration.

From a UI perspective, the how to import modal has been replaced by a full
page, which includes a form for posting a personal access token back to the
Import::GithubController.

If the user has logged in via GitHub, skip the Personal Access Token and go
directly to Github for an access token via OAuth.
parent c5d164d1
class Import::GithubController < Import::BaseController class Import::GithubController < Import::BaseController
before_action :verify_github_import_enabled before_action :verify_github_import_enabled
before_action :github_auth, except: :callback before_action :github_auth, except: [:callback, :new, :personal_access_token]
rescue_from Octokit::Unauthorized, with: :github_unauthorized rescue_from Octokit::Unauthorized, with: :github_unauthorized
helper_method :logged_in_with_github?
def new
if logged_in_with_github?
go_to_github_for_permissions
elsif session[:github_access_token]
redirect_to status_import_github_url
end
end
def callback def callback
session[:github_access_token] = client.get_token(params[:code]) session[:github_access_token] = client.get_token(params[:code])
redirect_to status_import_github_url redirect_to status_import_github_url
end end
def personal_access_token
session[:github_access_token] = params[:personal_access_token]
redirect_to status_import_github_url
end
def status def status
@repos = client.repos @repos = client.repos
@already_added_projects = current_user.created_projects.where(import_type: "github") @already_added_projects = current_user.created_projects.where(import_type: "github")
...@@ -57,10 +72,14 @@ class Import::GithubController < Import::BaseController ...@@ -57,10 +72,14 @@ class Import::GithubController < Import::BaseController
end end
def github_unauthorized def github_unauthorized
go_to_github_for_permissions session[:github_access_token] = nil
redirect_to new_import_github_url,
alert: 'Access denied to your GitHub account.'
end end
private def logged_in_with_github?
current_user.identities.exists?(provider: 'github')
end
def access_params def access_params
{ github_access_token: session[:github_access_token] } { github_access_token: session[:github_access_token] }
......
- page_title "GitHub Import"
- header_title "Projects", root_path
%h3.page-title
= icon 'github', text: 'Import Projects from GitHub'
%p.light
To import a project from GitHub, you can use a
= link_to 'Personal Access Token', 'https://github.com/settings/tokens'
to access your GitHub account. When you create your Personal Access Token,
you will need to select the <code>repo</code> scope, so we can display a
list of your public and private repositories which are available for import.
= form_tag personal_access_token_import_github_path, method: :post, class: 'form-inline' do
.form-group
= text_field_tag :personal_access_token, '', class: 'form-control', placeholder: "Personal Access Token", size: 40
= submit_tag 'List Repositories', class: 'btn btn-create'
- if github_import_configured?
- unless logged_in_with_github?
%hr
%p.light
Note: If you go to
= link_to 'your profile', profile_account_path
and connect your account to GitHub, you can import projects without
generating a Personal Access Token.
- else
%hr
%p.light
Note:
- if current_user.admin?
As an administrator you may like to configure
- else
Consider asking your GitLab administrator to configure
= link_to 'GitHub integration', help_page_path("integration", "github")
which will allow login via GitHub and allow importing projects without
generating a Personal Access Token.
%div#github_import_modal.modal
.modal-dialog
.modal-content
.modal-header
%a.close{href: "#", "data-dismiss" => "modal"} ×
%h3 Import projects from GitHub
.modal-body
To enable importing projects from GitHub,
- if current_user.admin?
as administrator you need to configure
- else
ask your Gitlab administrator to configure
== #{link_to 'OAuth integration', help_page_path("integration", "github")}.
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
.input-group-addon .input-group-addon
= root_url = root_url
= f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user, display_path: true), {}, {class: 'select2 js-select-namespace', tabindex: 1} = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user, display_path: true), {}, {class: 'select2 js-select-namespace', tabindex: 1}
- else - else
.input-group-addon.static-namespace .input-group-addon.static-namespace
#{root_url}#{current_user.username}/ #{root_url}#{current_user.username}/
...@@ -44,15 +45,8 @@ ...@@ -44,15 +45,8 @@
.col-sm-12.import-buttons .col-sm-12.import-buttons
%div %div
- if github_import_enabled? - if github_import_enabled?
- if github_import_configured? = link_to new_import_github_path, class: 'btn import_github' do
= link_to status_import_github_path, class: 'btn import_github' do = icon 'github', text: 'GitHub'
%i.fa.fa-github
GitHub
- else
= link_to '#', class: 'how_to_import_link btn import_github' do
%i.fa.fa-github
GitHub
= render 'github_import_modal'
%div %div
- if bitbucket_import_enabled? - if bitbucket_import_enabled?
- if bitbucket_import_configured? - if bitbucket_import_configured?
......
...@@ -10,7 +10,8 @@ paths_to_be_protected = [ ...@@ -10,7 +10,8 @@ paths_to_be_protected = [
"#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session", "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session",
"#{Rails.application.config.relative_url_root}/users", "#{Rails.application.config.relative_url_root}/users",
"#{Rails.application.config.relative_url_root}/users/confirmation", "#{Rails.application.config.relative_url_root}/users/confirmation",
"#{Rails.application.config.relative_url_root}/unsubscribes/" "#{Rails.application.config.relative_url_root}/unsubscribes/",
"#{Rails.application.config.relative_url_root}/import/github/personal_access_token"
] ]
......
...@@ -139,6 +139,7 @@ Rails.application.routes.draw do ...@@ -139,6 +139,7 @@ Rails.application.routes.draw do
# #
namespace :import do namespace :import do
resource :github, only: [:create, :new], controller: :github do resource :github, only: [:create, :new], controller: :github do
post :personal_access_token
get :status get :status
get :callback get :callback
get :jobs get :jobs
......
# Import your project from GitHub to GitLab # Import your project from GitHub to GitLab
>**Note:** >**Note:**
In order to enable the GitHub import setting, you should first In order to enable the GitHub import setting, you may also want to
enable the [GitHub integration][gh-import] in your GitLab instance. enable the [GitHub integration][gh-import] in your GitLab instance. This
configuration is optional, you will be able import your GitHub repositories
with a Personal Access Token.
At its current state, GitHub importer can import: At its current state, GitHub importer can import:
...@@ -20,9 +22,13 @@ It is not yet possible to import your cross-repository pull requests (those from ...@@ -20,9 +22,13 @@ It is not yet possible to import your cross-repository pull requests (those from
forks). We are working on improving this in the near future. forks). We are working on improving this in the near future.
The importer page is visible when you [create a new project][new-project]. The importer page is visible when you [create a new project][new-project].
Click on the **GitHub** link and you will be redirected to GitHub for Click on the **GitHub** link and, if you are logged in via the GitHub
permission to access your projects. After accepting, you'll be automatically integration, you will be redirected to GitHub for permission to access your
redirected to the importer. projects. After accepting, you'll be automatically redirected to the importer.
If you are not using the GitHub integration, when you click the **GithHub** link
you'll be presented with instructions for creating Personal Access Token on
GitHub. Once you upload your token, you'll be taken to the importer.
![New project page on GitLab](img/import_projects_from_github_new_project_page.png) ![New project page on GitLab](img/import_projects_from_github_new_project_page.png)
......
...@@ -21,7 +21,7 @@ Background: ...@@ -21,7 +21,7 @@ Background:
Scenario: I should see instructions on how to import from GitHub Scenario: I should see instructions on how to import from GitHub
Given I see "New Project" page Given I see "New Project" page
When I click on "Import project from GitHub" When I click on "Import project from GitHub"
Then I see instructions on how to import from GitHub Then I am redirected to the Github import page
@javascript @javascript
Scenario: I should see Google Code import page Scenario: I should see Google Code import page
......
...@@ -28,14 +28,8 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps ...@@ -28,14 +28,8 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps
first('.import_github').click first('.import_github').click
end end
step 'I see instructions on how to import from GitHub' do step 'I am redirected to the Github import page' do
github_modal = first('.modal-body') expect(current_path).to eq new_import_github_path
expect(github_modal).to be_visible
expect(github_modal).to have_content "To enable importing projects from GitHub"
page.all('.modal-body').each do |element|
expect(element).not_to be_visible unless element == github_modal
end
end end
step 'I click on "Repo by URL"' do step 'I click on "Repo by URL"' do
......
...@@ -4,26 +4,39 @@ module Gitlab ...@@ -4,26 +4,39 @@ module Gitlab
GITHUB_SAFE_REMAINING_REQUESTS = 100 GITHUB_SAFE_REMAINING_REQUESTS = 100
GITHUB_SAFE_SLEEP_TIME = 500 GITHUB_SAFE_SLEEP_TIME = 500
attr_reader :client, :api attr_reader :access_token
def initialize(access_token) def initialize(access_token)
@client = ::OAuth2::Client.new( @access_token = access_token
config.app_id,
config.app_secret,
github_options.merge(ssl: { verify: config['verify_ssl'] })
)
if access_token if access_token
::Octokit.auto_paginate = false ::Octokit.auto_paginate = false
end
end
@api = ::Octokit::Client.new( def api
@api ||= ::Octokit::Client.new(
access_token: access_token, access_token: access_token,
api_endpoint: github_options[:site], api_endpoint: github_options[:site],
# If there is no config, we're connecting to github.com and we
# should verify ssl.
connection_options: { connection_options: {
ssl: { verify: config['verify_ssl'] } ssl: { verify: config ? config['verify_ssl'] : true }
} }
) )
end end
def client
unless config
raise Projects::ImportService::Error,
'OAuth configuration for GitHub missing.'
end
@client ||= ::OAuth2::Client.new(
config.app_id,
config.app_secret,
github_options.merge(ssl: { verify: config['verify_ssl'] })
)
end end
def authorize_url(redirect_uri) def authorize_url(redirect_uri)
...@@ -56,7 +69,11 @@ module Gitlab ...@@ -56,7 +69,11 @@ module Gitlab
end end
def github_options def github_options
if config
config["args"]["client_options"].deep_symbolize_keys config["args"]["client_options"].deep_symbolize_keys
else
OmniAuth::Strategies::GitHub.default_options[:client_options].symbolize_keys
end
end end
def rate_limit def rate_limit
......
...@@ -16,6 +16,24 @@ describe Import::GithubController do ...@@ -16,6 +16,24 @@ describe Import::GithubController do
allow(controller).to receive(:github_import_enabled?).and_return(true) allow(controller).to receive(:github_import_enabled?).and_return(true)
end end
describe "GET new" do
it "redirects to GitHub for an access token if logged in with GitHub" do
allow(controller).to receive(:logged_in_with_github?).and_return(true)
expect(controller).to receive(:go_to_github_for_permissions)
get :new
end
it "redirects to status if we already have a token" do
assign_session_token
allow(controller).to receive(:logged_in_with_github?).and_return(false)
get :new
expect(controller).to redirect_to(status_import_github_url)
end
end
describe "GET callback" do describe "GET callback" do
it "updates access token" do it "updates access token" do
token = "asdasd12345" token = "asdasd12345"
...@@ -32,6 +50,20 @@ describe Import::GithubController do ...@@ -32,6 +50,20 @@ describe Import::GithubController do
end end
end end
describe "POST personal_access_token" do
it "updates access token" do
token = "asdfasdf9876"
allow_any_instance_of(Gitlab::GithubImport::Client).
to receive(:user).and_return(true)
post :personal_access_token, personal_access_token: token
expect(session[:github_access_token]).to eq(token)
expect(controller).to redirect_to(status_import_github_url)
end
end
describe "GET status" do describe "GET status" do
before do before do
@repo = OpenStruct.new(login: 'vim', full_name: 'asd/vim') @repo = OpenStruct.new(login: 'vim', full_name: 'asd/vim')
...@@ -59,6 +91,17 @@ describe Import::GithubController do ...@@ -59,6 +91,17 @@ describe Import::GithubController do
expect(assigns(:already_added_projects)).to eq([@project]) expect(assigns(:already_added_projects)).to eq([@project])
expect(assigns(:repos)).to eq([]) expect(assigns(:repos)).to eq([])
end end
it "handles an invalid access token" do
allow_any_instance_of(Gitlab::GithubImport::Client).
to receive(:repos).and_raise(Octokit::Unauthorized)
get :status
expect(session[:github_access_token]).to eq(nil)
expect(controller).to redirect_to(new_import_github_url)
expect(flash[:alert]).to eq('Access denied to your GitHub account.')
end
end end
describe "POST create" do describe "POST create" do
......
...@@ -20,6 +20,20 @@ describe Gitlab::GithubImport::Client, lib: true do ...@@ -20,6 +20,20 @@ describe Gitlab::GithubImport::Client, lib: true do
expect { client.api }.not_to raise_error expect { client.api }.not_to raise_error
end end
context 'when config is missing' do
before do
allow(Gitlab.config.omniauth).to receive(:providers).and_return([])
end
it 'is still possible to get an Octokit client' do
expect { client.api }.not_to raise_error
end
it 'is not be possible to get an OAuth2 client' do
expect { client.client }.to raise_error(Projects::ImportService::Error)
end
end
context 'allow SSL verification to be configurable on API' do context 'allow SSL verification to be configurable on API' do
before do before do
github_provider['verify_ssl'] = false github_provider['verify_ssl'] = false
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment