forked from lix-project/hydra
Merge pull request #841 from pingiun/github-login
Implement GitHub logins
This commit is contained in:
commit
be0aa7eb85
|
@ -30,6 +30,8 @@ sub noLoginNeeded {
|
||||||
return $whitelisted ||
|
return $whitelisted ||
|
||||||
$c->request->path eq "api/push-github" ||
|
$c->request->path eq "api/push-github" ||
|
||||||
$c->request->path eq "google-login" ||
|
$c->request->path eq "google-login" ||
|
||||||
|
$c->request->path eq "github-redirect" ||
|
||||||
|
$c->request->path eq "github-login" ||
|
||||||
$c->request->path eq "login" ||
|
$c->request->path eq "login" ||
|
||||||
$c->request->path eq "logo" ||
|
$c->request->path eq "logo" ||
|
||||||
$c->request->path =~ /^static\//;
|
$c->request->path =~ /^static\//;
|
||||||
|
|
|
@ -4,6 +4,7 @@ use utf8;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use base 'Hydra::Base::Controller::REST';
|
use base 'Hydra::Base::Controller::REST';
|
||||||
|
use File::Slurp;
|
||||||
use Crypt::RandPasswd;
|
use Crypt::RandPasswd;
|
||||||
use Digest::SHA1 qw(sha1_hex);
|
use Digest::SHA1 qw(sha1_hex);
|
||||||
use Hydra::Helper::Nix;
|
use Hydra::Helper::Nix;
|
||||||
|
@ -154,6 +155,67 @@ sub google_login :Path('/google-login') Args(0) {
|
||||||
doEmailLogin($self, $c, "google", $data->{email}, $data->{name} // undef);
|
doEmailLogin($self, $c, "google", $data->{email}, $data->{name} // undef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub github_login :Path('/github-login') Args(0) {
|
||||||
|
my ($self, $c) = @_;
|
||||||
|
|
||||||
|
my $client_id = $c->config->{github_client_id} or die "github_client_id not configured.";
|
||||||
|
my $client_secret = $c->config->{github_client_secret} // do {
|
||||||
|
my $client_secret_file = $c->config->{github_client_secret_file} or die "github_client_secret nor github_client_secret_file is configured.";
|
||||||
|
my $client_secret = read_file($client_secret_file);
|
||||||
|
$client_secret =~ s/\s+//;
|
||||||
|
$client_secret;
|
||||||
|
};
|
||||||
|
die "No github secret configured" unless $client_secret;
|
||||||
|
|
||||||
|
my $ua = new LWP::UserAgent;
|
||||||
|
my $response = $ua->post(
|
||||||
|
'https://github.com/login/oauth/access_token',
|
||||||
|
{
|
||||||
|
client_id => $client_id,
|
||||||
|
client_secret => $client_secret,
|
||||||
|
code => ($c->req->params->{code} // die "No token."),
|
||||||
|
}, Accept => 'application/json');
|
||||||
|
error($c, "Did not get a response from GitHub.") unless $response->is_success;
|
||||||
|
|
||||||
|
my $data = decode_json($response->decoded_content) or die;
|
||||||
|
my $access_token = $data->{access_token} // die "No access_token in response from GitHub.";
|
||||||
|
|
||||||
|
$response = $ua->get('https://api.github.com/user/emails', Accept => 'application/vnd.github.v3+json', Authorization => "token $access_token");
|
||||||
|
error($c, "Did not get a response from GitHub for email info.") unless $response->is_success;
|
||||||
|
|
||||||
|
$data = decode_json($response->decoded_content) or die;
|
||||||
|
my $email;
|
||||||
|
|
||||||
|
foreach my $eml (@{$data}) {
|
||||||
|
$email = $eml->{email} if $eml->{verified} && $eml->{primary};
|
||||||
|
}
|
||||||
|
|
||||||
|
die "No primary email for this GitHub profile" unless $email;
|
||||||
|
|
||||||
|
$response = $ua->get('https://api.github.com/user', Authorization => "token $access_token");
|
||||||
|
error($c, "Did not get a response from GitHub for user info.") unless $response->is_success;
|
||||||
|
$data = decode_json($response->decoded_content) or die;
|
||||||
|
|
||||||
|
doEmailLogin($self, $c, "github", $email, $data->{name} // undef);
|
||||||
|
|
||||||
|
$c->res->redirect($c->uri_for($c->res->cookies->{'after_github'}));
|
||||||
|
}
|
||||||
|
|
||||||
|
sub github_redirect :Path('/github-redirect') Args(0) {
|
||||||
|
my ($self, $c) = @_;
|
||||||
|
|
||||||
|
my $client_id = $c->config->{github_client_id} or die "github_client_id not configured.";
|
||||||
|
|
||||||
|
my $after = "/" . $c->req->params->{after};
|
||||||
|
|
||||||
|
$c->res->cookies->{'after_github'} = {
|
||||||
|
name => 'after_github',
|
||||||
|
value => $after,
|
||||||
|
};
|
||||||
|
|
||||||
|
$c->res->redirect("https://github.com/login/oauth/authorize?client_id=$client_id&scope=user:email");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub captcha :Local Args(0) {
|
sub captcha :Local Args(0) {
|
||||||
my ($self, $c) = @_;
|
my ($self, $c) = @_;
|
||||||
|
|
|
@ -136,6 +136,10 @@
|
||||||
<li><a href="#" id="google-signin">Sign in with Google</a></li>
|
<li><a href="#" id="google-signin">Sign in with Google</a></li>
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
[% END %]
|
[% END %]
|
||||||
|
[% IF c.config.github_client_id %]
|
||||||
|
<li><a href="/github-redirect?after=[% c.req.path %]">Sign in with GitHub</a></li>
|
||||||
|
<li class="divider"></li>
|
||||||
|
[% END %]
|
||||||
<li>
|
<li>
|
||||||
<a href="#hydra-signin" data-toggle="modal">Sign in with a Hydra account</a>
|
<a href="#hydra-signin" data-toggle="modal">Sign in with a Hydra account</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -11,7 +11,7 @@ sub showHelp {
|
||||||
print <<EOF;
|
print <<EOF;
|
||||||
Usage: $0 NAME
|
Usage: $0 NAME
|
||||||
[--rename-from NAME]
|
[--rename-from NAME]
|
||||||
[--type hydra|google]
|
[--type hydra|google|github]
|
||||||
[--full-name FULLNAME]
|
[--full-name FULLNAME]
|
||||||
[--email-address EMAIL-ADDRESS]
|
[--email-address EMAIL-ADDRESS]
|
||||||
[--password PASSWORD]
|
[--password PASSWORD]
|
||||||
|
@ -49,8 +49,8 @@ GetOptions("rename-from=s" => \$renameFrom,
|
||||||
die "$0: one user name required\n" if scalar @ARGV != 1;
|
die "$0: one user name required\n" if scalar @ARGV != 1;
|
||||||
my $userName = $ARGV[0];
|
my $userName = $ARGV[0];
|
||||||
|
|
||||||
die "$0: type must be `hydra' or `google'\n"
|
die "$0: type must be `hydra', `google' or `github'\n"
|
||||||
if defined $type && $type ne "hydra" && $type ne "google";
|
if defined $type && $type ne "hydra" && $type ne "google" && $type ne "github";
|
||||||
|
|
||||||
my $db = Hydra::Model::DB->new();
|
my $db = Hydra::Model::DB->new();
|
||||||
|
|
||||||
|
@ -67,19 +67,19 @@ $db->txn_do(sub {
|
||||||
{ username => $userName, type => "hydra", emailaddress => "", password => "!" });
|
{ username => $userName, type => "hydra", emailaddress => "", password => "!" });
|
||||||
}
|
}
|
||||||
|
|
||||||
die "$0: Google user names must be email addresses\n"
|
die "$0: Google or GitHub user names must be email addresses\n"
|
||||||
if $user->type eq "google" && $userName !~ /\@/;
|
if ($user->type eq "google" || $user->type eq "github") && $userName !~ /\@/;
|
||||||
|
|
||||||
$user->update({ type => $type }) if defined $type;
|
$user->update({ type => $type }) if defined $type;
|
||||||
|
|
||||||
$user->update({ fullname => $fullName eq "" ? undef : $fullName }) if defined $fullName;
|
$user->update({ fullname => $fullName eq "" ? undef : $fullName }) if defined $fullName;
|
||||||
|
|
||||||
if ($user->type eq "google") {
|
if ($user->type eq "google" || $user->type eq "github") {
|
||||||
die "$0: Google accounts do not have an explicitly set email address.\n"
|
die "$0: Google and GitHub accounts do not have an explicitly set email address.\n"
|
||||||
if defined $emailAddress;
|
if defined $emailAddress;
|
||||||
die "$0: Google accounts do not have a password.\n"
|
die "$0: Google and GitHub accounts do not have a password.\n"
|
||||||
if defined $password;
|
if defined $password;
|
||||||
die "$0: Google accounts do not have a password.\n"
|
die "$0: Google and GitHub accounts do not have a password.\n"
|
||||||
if defined $passwordHash;
|
if defined $passwordHash;
|
||||||
$user->update({ emailaddress => $userName, password => "!" });
|
$user->update({ emailaddress => $userName, password => "!" });
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -10,7 +10,7 @@ create table Users (
|
||||||
emailAddress text not null,
|
emailAddress text not null,
|
||||||
password text not null, -- sha256 hash
|
password text not null, -- sha256 hash
|
||||||
emailOnError integer not null default 0,
|
emailOnError integer not null default 0,
|
||||||
type text not null default 'hydra', -- either "hydra" or "google"
|
type text not null default 'hydra', -- either "hydra", "google" or "github"
|
||||||
publicDashboard boolean not null default false
|
publicDashboard boolean not null default false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue