Add Docker ID integration
This commit is contained in:
@@ -38,6 +38,7 @@ var SecureCookie *securecookie.SecureCookie
|
|||||||
|
|
||||||
var GithubClientID, GithubClientSecret string
|
var GithubClientID, GithubClientSecret string
|
||||||
var FacebookClientID, FacebookClientSecret string
|
var FacebookClientID, FacebookClientSecret string
|
||||||
|
var DockerClientID, DockerClientSecret string
|
||||||
|
|
||||||
type stringslice []string
|
type stringslice []string
|
||||||
|
|
||||||
@@ -78,6 +79,9 @@ func ParseFlags() {
|
|||||||
flag.StringVar(&FacebookClientID, "oauth-facebook-client-id", "", "Facebook OAuth Client ID")
|
flag.StringVar(&FacebookClientID, "oauth-facebook-client-id", "", "Facebook OAuth Client ID")
|
||||||
flag.StringVar(&FacebookClientSecret, "oauth-facebook-client-secret", "", "Facebook OAuth Client Secret")
|
flag.StringVar(&FacebookClientSecret, "oauth-facebook-client-secret", "", "Facebook OAuth Client Secret")
|
||||||
|
|
||||||
|
flag.StringVar(&DockerClientID, "oauth-docker-client-id", "", "Docker OAuth Client ID")
|
||||||
|
flag.StringVar(&DockerClientSecret, "oauth-docker-client-secret", "", "Docker OAuth Client Secret")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
SecureCookie = securecookie.New([]byte(CookieHashKey), []byte(CookieBlockKey))
|
SecureCookie = securecookie.New([]byte(CookieHashKey), []byte(CookieBlockKey))
|
||||||
@@ -107,6 +111,20 @@ func registerOAuthProviders() {
|
|||||||
|
|
||||||
Providers["facebook"] = conf
|
Providers["facebook"] = conf
|
||||||
}
|
}
|
||||||
|
if DockerClientID != "" && DockerClientSecret != "" {
|
||||||
|
oauth2.RegisterBrokenAuthHeaderProvider(".id.docker.com")
|
||||||
|
conf := &oauth2.Config{
|
||||||
|
ClientID: DockerClientID,
|
||||||
|
ClientSecret: DockerClientSecret,
|
||||||
|
Scopes: []string{"openid"},
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
AuthURL: "https://id.docker.com/id/oauth/authorize/",
|
||||||
|
TokenURL: "https://id.docker.com/id/oauth/token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Providers["docker"] = conf
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDindImageName() string {
|
func GetDindImageName() string {
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ func Register(extend HandlerExtender) {
|
|||||||
http.ServeFile(rw, r, "./www/landing.html")
|
http.ServeFile(rw, r, "./www/landing.html")
|
||||||
}).Methods("GET")
|
}).Methods("GET")
|
||||||
|
|
||||||
|
r.HandleFunc("/users/me", LoggedInUser).Methods("GET")
|
||||||
r.HandleFunc("/oauth/providers", ListProviders).Methods("GET")
|
r.HandleFunc("/oauth/providers", ListProviders).Methods("GET")
|
||||||
r.HandleFunc("/oauth/providers/{provider}/login", Login).Methods("GET")
|
r.HandleFunc("/oauth/providers/{provider}/login", Login).Methods("GET")
|
||||||
r.HandleFunc("/oauth/providers/{provider}/callback", LoginCallback).Methods("GET")
|
r.HandleFunc("/oauth/providers/{provider}/callback", LoginCallback).Methods("GET")
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ type CookieID struct {
|
|||||||
func (c *CookieID) SetCookie(rw http.ResponseWriter) error {
|
func (c *CookieID) SetCookie(rw http.ResponseWriter) error {
|
||||||
if encoded, err := config.SecureCookie.Encode("id", c); err == nil {
|
if encoded, err := config.SecureCookie.Encode("id", c); err == nil {
|
||||||
cookie := &http.Cookie{
|
cookie := &http.Cookie{
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Value: encoded,
|
Value: encoded,
|
||||||
Path: "/",
|
Path: "/",
|
||||||
Secure: config.UseLetsEncrypt,
|
Secure: config.UseLetsEncrypt,
|
||||||
|
HttpOnly: true,
|
||||||
}
|
}
|
||||||
http.SetCookie(rw, cookie)
|
http.SetCookie(rw, cookie)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -14,8 +14,25 @@ import (
|
|||||||
fb "github.com/huandu/facebook"
|
fb "github.com/huandu/facebook"
|
||||||
"github.com/play-with-docker/play-with-docker/config"
|
"github.com/play-with-docker/play-with-docker/config"
|
||||||
"github.com/play-with-docker/play-with-docker/pwd/types"
|
"github.com/play-with-docker/play-with-docker/pwd/types"
|
||||||
|
"github.com/twinj/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func LoggedInUser(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
cookie, err := ReadCookie(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Cannot read cookie")
|
||||||
|
rw.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, err := core.UserGet(cookie.Id)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Couldn't get user with id %s. Got: %v\n", cookie.Id, err)
|
||||||
|
rw.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
json.NewEncoder(rw).Encode(user)
|
||||||
|
}
|
||||||
|
|
||||||
func ListProviders(rw http.ResponseWriter, req *http.Request) {
|
func ListProviders(rw http.ResponseWriter, req *http.Request) {
|
||||||
providers := []string{}
|
providers := []string{}
|
||||||
for name, _ := range config.Providers {
|
for name, _ := range config.Providers {
|
||||||
@@ -51,7 +68,7 @@ func Login(rw http.ResponseWriter, req *http.Request) {
|
|||||||
host = req.Host
|
host = req.Host
|
||||||
}
|
}
|
||||||
provider.RedirectURL = fmt.Sprintf("%s://%s/oauth/providers/%s/callback", scheme, host, providerName)
|
provider.RedirectURL = fmt.Sprintf("%s://%s/oauth/providers/%s/callback", scheme, host, providerName)
|
||||||
url := provider.AuthCodeURL(loginRequest.Id)
|
url := provider.AuthCodeURL(loginRequest.Id, oauth2.SetAuthURLParam("nonce", uuid.NewV4().String()))
|
||||||
|
|
||||||
http.Redirect(rw, req, url, http.StatusFound)
|
http.Redirect(rw, req, url, http.StatusFound)
|
||||||
}
|
}
|
||||||
@@ -125,6 +142,28 @@ func LoginCallback(rw http.ResponseWriter, req *http.Request) {
|
|||||||
user.Name = res.Get("name").(string)
|
user.Name = res.Get("name").(string)
|
||||||
user.Avatar = res.Get("picture.data.url").(string)
|
user.Avatar = res.Get("picture.data.url").(string)
|
||||||
user.Email = res.Get("email").(string)
|
user.Email = res.Get("email").(string)
|
||||||
|
} else if providerName == "docker" {
|
||||||
|
ts := oauth2.StaticTokenSource(
|
||||||
|
&oauth2.Token{AccessToken: tok.AccessToken},
|
||||||
|
)
|
||||||
|
tc := oauth2.NewClient(ctx, ts)
|
||||||
|
resp, err := tc.Get("https://id.docker.com/api/id/v1/openid/userinfo")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Could not get user from docker. Got: %v\n", err)
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userInfo := map[string]string{}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil {
|
||||||
|
log.Printf("Could not decode user info. Got: %v\n", err)
|
||||||
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user.ProviderUserId = userInfo["sub"]
|
||||||
|
user.Name = userInfo["preferred_username"]
|
||||||
|
user.Email = userInfo["email"]
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err = core.UserLogin(loginRequest, user)
|
user, err = core.UserLogin(loginRequest, user)
|
||||||
@@ -142,5 +181,5 @@ func LoginCallback(rw http.ResponseWriter, req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(rw, req, "/", http.StatusFound)
|
fmt.Fprintf(rw, `<html><head><script>window.close();</script></head><body></body></html>`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,3 +119,7 @@ func (m *Mock) UserLogin(loginRequest *types.LoginRequest, user *types.User) (*t
|
|||||||
args := m.Called(loginRequest, user)
|
args := m.Called(loginRequest, user)
|
||||||
return args.Get(0).(*types.User), args.Error(1)
|
return args.Get(0).(*types.User), args.Error(1)
|
||||||
}
|
}
|
||||||
|
func (m *Mock) UserGet(id string) (*types.User, error) {
|
||||||
|
args := m.Called(id)
|
||||||
|
return args.Get(0).(*types.User), args.Error(1)
|
||||||
|
}
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ type PWDApi interface {
|
|||||||
UserNewLoginRequest(providerName string) (*types.LoginRequest, error)
|
UserNewLoginRequest(providerName string) (*types.LoginRequest, error)
|
||||||
UserGetLoginRequest(id string) (*types.LoginRequest, error)
|
UserGetLoginRequest(id string) (*types.LoginRequest, error)
|
||||||
UserLogin(loginRequest *types.LoginRequest, user *types.User) (*types.User, error)
|
UserLogin(loginRequest *types.LoginRequest, user *types.User) (*types.User, error)
|
||||||
|
UserGet(id string) (*types.User, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPWD(f docker.FactoryApi, e event.EventApi, s storage.StorageApi, sp provisioner.SessionProvisionerApi, ipf provisioner.InstanceProvisionerFactoryApi) *pwd {
|
func NewPWD(f docker.FactoryApi, e event.EventApi, s storage.StorageApi, sp provisioner.SessionProvisionerApi, ipf provisioner.InstanceProvisionerFactoryApi) *pwd {
|
||||||
|
|||||||
@@ -41,3 +41,10 @@ func (p *pwd) UserLogin(loginRequest *types.LoginRequest, user *types.User) (*ty
|
|||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
func (p *pwd) UserGet(id string) (*types.User, error) {
|
||||||
|
if user, err := p.storage.UserGet(id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -353,6 +353,16 @@ func (store *storage) UserPut(user *types.User) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (store *storage) UserGet(id string) (*types.User, error) {
|
||||||
|
store.rw.Lock()
|
||||||
|
defer store.rw.Unlock()
|
||||||
|
|
||||||
|
if user, found := store.db.Users[id]; !found {
|
||||||
|
return nil, NotFoundError
|
||||||
|
} else {
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (store *storage) load() error {
|
func (store *storage) load() error {
|
||||||
file, err := os.Open(store.path)
|
file, err := os.Open(store.path)
|
||||||
|
|||||||
@@ -102,3 +102,7 @@ func (m *Mock) UserPut(user *types.User) error {
|
|||||||
args := m.Called(user)
|
args := m.Called(user)
|
||||||
return args.Error(0)
|
return args.Error(0)
|
||||||
}
|
}
|
||||||
|
func (m *Mock) UserGet(id string) (*types.User, error) {
|
||||||
|
args := m.Called(id)
|
||||||
|
return args.Get(0).(*types.User), args.Error(1)
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,4 +41,5 @@ type StorageApi interface {
|
|||||||
|
|
||||||
UserFindByProvider(providerName, providerUserId string) (*types.User, error)
|
UserFindByProvider(providerName, providerUserId string) (*types.User, error)
|
||||||
UserPut(user *types.User) error
|
UserPut(user *types.User) error
|
||||||
|
UserGet(id string) (*types.User, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
Login
|
Login
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
||||||
<a ng-repeat="provider in providers" class="dropdown-item" href="/oauth/providers/{{provider}}/login">{{provider}}</a>
|
<a ng-repeat="provider in providers" class="dropdown-item" ng-click="login(provider)">{{provider}}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form id="landingForm" method="POST" action="/">
|
<form id="landingForm" method="POST" action="/">
|
||||||
@@ -68,10 +68,27 @@
|
|||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
|
||||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
|
||||||
<script>
|
<script>
|
||||||
angular.module('PWDLanding', ['ngCookies'])
|
angular.module('PWDLanding', [])
|
||||||
.controller('LoginController', ['$cookies', '$scope', '$http', function($cookies, $scope, $http) {
|
.controller('LoginController', ['$scope', '$http', '$window', function($scope, $http, $window) {
|
||||||
$scope.providers = [];
|
$scope.providers = [];
|
||||||
$scope.loggedIn = $cookies.get('id') !== undefined;
|
$scope.loggedIn = false;
|
||||||
|
$scope.user = null;
|
||||||
|
|
||||||
|
function checkLoggedIn() {
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/users/me'
|
||||||
|
}).then(function(response) {
|
||||||
|
$scope.user = response.data;
|
||||||
|
$scope.loggedIn = true;
|
||||||
|
}, function(response) {
|
||||||
|
console.log('ERROR', response);
|
||||||
|
$scope.user = null;
|
||||||
|
$scope.loggedIn = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkLoggedIn();
|
||||||
|
|
||||||
$http({
|
$http({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@@ -85,6 +102,18 @@
|
|||||||
console.log('ERROR', response);
|
console.log('ERROR', response);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$scope.login = function(provider) {
|
||||||
|
var width = screen.width*0.6;
|
||||||
|
var height = screen.height*0.6;
|
||||||
|
var x = screen.width/2 - width/2;
|
||||||
|
var y = screen.height/2 - height/2;
|
||||||
|
var loginWnd = $window.open('/oauth/providers/' + provider + '/login', 'PWDLogin', 'width='+width+',height='+height+',left='+x+',top='+y);
|
||||||
|
loginWnd.onunload = function() {
|
||||||
|
checkLoggedIn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$scope.start = function() {
|
$scope.start = function() {
|
||||||
function getParameterByName(name, url) {
|
function getParameterByName(name, url) {
|
||||||
if (!url) url = window.location.href;
|
if (!url) url = window.location.href;
|
||||||
|
|||||||
Reference in New Issue
Block a user