Add support for openid with github and facebook
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
"github.com/play-with-docker/play-with-docker/config"
|
||||
"github.com/play-with-docker/play-with-docker/event"
|
||||
"github.com/play-with-docker/play-with-docker/pwd"
|
||||
"github.com/play-with-docker/play-with-docker/templates"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/urfave/negroni"
|
||||
)
|
||||
@@ -33,9 +31,6 @@ func Bootstrap(c pwd.PWDApi, ev event.EventApi) {
|
||||
}
|
||||
|
||||
func Register(extend HandlerExtender) {
|
||||
|
||||
bypassCaptcha := len(os.Getenv("GOOGLE_RECAPTCHA_DISABLED")) > 0
|
||||
|
||||
server, err := socketio.NewServer(nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -81,17 +76,13 @@ func Register(extend HandlerExtender) {
|
||||
|
||||
// Generic routes
|
||||
r.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
|
||||
if bypassCaptcha {
|
||||
http.ServeFile(rw, r, "./www/bypass.html")
|
||||
} else {
|
||||
welcome, tmplErr := templates.GetWelcomeTemplate()
|
||||
if tmplErr != nil {
|
||||
log.Fatal(tmplErr)
|
||||
}
|
||||
rw.Write(welcome)
|
||||
}
|
||||
http.ServeFile(rw, r, "./www/landing.html")
|
||||
}).Methods("GET")
|
||||
|
||||
r.HandleFunc("/oauth/providers", ListProviders).Methods("GET")
|
||||
r.HandleFunc("/oauth/providers/{provider}/login", Login).Methods("GET")
|
||||
r.HandleFunc("/oauth/providers/{provider}/callback", LoginCallback).Methods("GET")
|
||||
|
||||
corsRouter.HandleFunc("/", NewSession).Methods("POST")
|
||||
|
||||
if extend != nil {
|
||||
|
||||
40
handlers/cookie_id.go
Normal file
40
handlers/cookie_id.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/play-with-docker/play-with-docker/config"
|
||||
)
|
||||
|
||||
type CookieID struct {
|
||||
Id string `json:"id"`
|
||||
UserName string `json:"user_name"`
|
||||
UserAvatar string `json:"user_avatar"`
|
||||
}
|
||||
|
||||
func (c *CookieID) SetCookie(rw http.ResponseWriter) error {
|
||||
if encoded, err := config.SecureCookie.Encode("id", c); err == nil {
|
||||
cookie := &http.Cookie{
|
||||
Name: "id",
|
||||
Value: encoded,
|
||||
Path: "/",
|
||||
Secure: config.UseLetsEncrypt,
|
||||
}
|
||||
http.SetCookie(rw, cookie)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func ReadCookie(r *http.Request) (*CookieID, error) {
|
||||
if cookie, err := r.Cookie("id"); err == nil {
|
||||
value := &CookieID{}
|
||||
if err = config.SecureCookie.Decode("id", cookie.Value, &value); err == nil {
|
||||
return value, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
146
handlers/login.go
Normal file
146
handlers/login.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/google/go-github/github"
|
||||
"github.com/gorilla/mux"
|
||||
fb "github.com/huandu/facebook"
|
||||
"github.com/play-with-docker/play-with-docker/config"
|
||||
"github.com/play-with-docker/play-with-docker/pwd/types"
|
||||
)
|
||||
|
||||
func ListProviders(rw http.ResponseWriter, req *http.Request) {
|
||||
providers := []string{}
|
||||
for name, _ := range config.Providers {
|
||||
providers = append(providers, name)
|
||||
}
|
||||
json.NewEncoder(rw).Encode(providers)
|
||||
}
|
||||
|
||||
func Login(rw http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
providerName := vars["provider"]
|
||||
|
||||
provider, found := config.Providers[providerName]
|
||||
if !found {
|
||||
log.Printf("Could not find provider %s\n", providerName)
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
loginRequest, err := core.UserNewLoginRequest(providerName)
|
||||
if err != nil {
|
||||
log.Printf("Could not start a new user login request for provider %s. Got: %v\n", providerName, err)
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
scheme := "http"
|
||||
if req.URL.Scheme != "" {
|
||||
scheme = req.URL.Scheme
|
||||
}
|
||||
host := "localhost"
|
||||
if req.URL.Host != "" {
|
||||
host = req.URL.Host
|
||||
}
|
||||
provider.RedirectURL = fmt.Sprintf("%s://%s/oauth/providers/%s/callback", scheme, host, providerName)
|
||||
url := provider.AuthCodeURL(loginRequest.Id)
|
||||
|
||||
http.Redirect(rw, req, url, http.StatusFound)
|
||||
}
|
||||
|
||||
func LoginCallback(rw http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
providerName := vars["provider"]
|
||||
|
||||
provider, found := config.Providers[providerName]
|
||||
if !found {
|
||||
log.Printf("Could not find provider %s\n", providerName)
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
query := req.URL.Query()
|
||||
|
||||
code := query.Get("code")
|
||||
loginRequestId := query.Get("state")
|
||||
|
||||
loginRequest, err := core.UserGetLoginRequest(loginRequestId)
|
||||
if err != nil {
|
||||
log.Printf("Could not get login request %s for provider %s. Got: %v\n", loginRequestId, providerName, err)
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := req.Context()
|
||||
tok, err := provider.Exchange(ctx, code)
|
||||
if err != nil {
|
||||
log.Printf("Could not exchage code for access token for provider %s. Got: %v\n", providerName, err)
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
user := &types.User{Provider: providerName}
|
||||
if providerName == "github" {
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: tok.AccessToken},
|
||||
)
|
||||
tc := oauth2.NewClient(ctx, ts)
|
||||
client := github.NewClient(tc)
|
||||
u, _, err := client.Users.Get(ctx, "")
|
||||
if err != nil {
|
||||
log.Printf("Could not get user from github. Got: %v\n", err)
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
user.ProviderUserId = strconv.Itoa(u.GetID())
|
||||
user.Name = u.GetName()
|
||||
user.Avatar = u.GetAvatarURL()
|
||||
user.Email = u.GetEmail()
|
||||
} else if providerName == "facebook" {
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: tok.AccessToken},
|
||||
)
|
||||
tc := oauth2.NewClient(ctx, ts)
|
||||
session := &fb.Session{
|
||||
Version: "v2.10",
|
||||
HttpClient: tc,
|
||||
}
|
||||
p := fb.Params{}
|
||||
p["fields"] = "email,name,picture"
|
||||
res, err := session.Get("/me", p)
|
||||
if err != nil {
|
||||
log.Printf("Could not get user from facebook. Got: %v\n", err)
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
user.ProviderUserId = res.Get("id").(string)
|
||||
user.Name = res.Get("name").(string)
|
||||
user.Avatar = res.Get("picture.data.url").(string)
|
||||
user.Email = res.Get("email").(string)
|
||||
}
|
||||
|
||||
user, err = core.UserLogin(loginRequest, user)
|
||||
if err != nil {
|
||||
log.Printf("Could not login user. Got: %v\n", err)
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
cookieData := CookieID{Id: user.Id, UserName: user.Name, UserAvatar: user.Avatar}
|
||||
|
||||
if err := cookieData.SetCookie(rw); err != nil {
|
||||
log.Printf("Could not encode cookie. Got: %v\n", err)
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(rw, req, "/", http.StatusFound)
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
"github.com/play-with-docker/play-with-docker/config"
|
||||
"github.com/play-with-docker/play-with-docker/provisioner"
|
||||
"github.com/play-with-docker/play-with-docker/recaptcha"
|
||||
)
|
||||
|
||||
type NewSessionResponse struct {
|
||||
@@ -20,10 +19,16 @@ type NewSessionResponse struct {
|
||||
|
||||
func NewSession(rw http.ResponseWriter, req *http.Request) {
|
||||
req.ParseForm()
|
||||
if !recaptcha.IsHuman(req, rw) {
|
||||
// User it not a human
|
||||
rw.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
|
||||
userId := ""
|
||||
if len(config.Providers) > 0 {
|
||||
cookie, err := ReadCookie(req)
|
||||
if err != nil {
|
||||
// User it not a human
|
||||
rw.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
userId = cookie.Id
|
||||
}
|
||||
|
||||
reqDur := req.Form.Get("session-duration")
|
||||
@@ -45,7 +50,7 @@ func NewSession(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
}
|
||||
duration := config.GetDuration(reqDur)
|
||||
s, err := core.SessionNew(duration, stack, stackName, imageName)
|
||||
s, err := core.SessionNew(userId, duration, stack, stackName, imageName)
|
||||
if err != nil {
|
||||
if provisioner.OutOfCapacity(err) {
|
||||
http.Redirect(rw, req, "/ooc", http.StatusFound)
|
||||
|
||||
Reference in New Issue
Block a user