twitterchiver/viewer: swap openshiftauth for pomerium
This commit is contained in:
parent
e3b60baa72
commit
3a3acc4673
10 changed files with 95 additions and 361 deletions
|
@ -5,6 +5,5 @@
|
|||
args: {
|
||||
twitterchiver = import ./twitterchiver args;
|
||||
twitternuke = import ./twitternuke args;
|
||||
openshiftauth = import ./openshiftauth args;
|
||||
minotarproxy = import ./minotarproxy args;
|
||||
}
|
||||
|
|
|
@ -16,5 +16,6 @@ require (
|
|||
github.com/gorilla/sessions v1.2.1
|
||||
github.com/jackc/pgtype v1.4.2
|
||||
github.com/jackc/pgx/v4 v4.8.1
|
||||
github.com/pomerium/sdk-go v0.0.5 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
)
|
||||
|
|
11
go/go.sum
11
go/go.sum
|
@ -32,6 +32,7 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
|||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
|
@ -127,6 +128,8 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
|||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pomerium/sdk-go v0.0.5 h1:8iJg/uZ7AmhGfHvmHmpZOswzg9MDMcmpTMX0zpmDxxs=
|
||||
github.com/pomerium/sdk-go v0.0.5/go.mod h1:z4HXhKxVTIQqmffpjQP6Oi+oP9r37a0Kx/jq69jcNRo=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
|
@ -145,6 +148,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
|
@ -163,6 +167,8 @@ golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
|
@ -201,8 +207,10 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
|
@ -242,8 +250,11 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
|
|||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
{ depot, ... }: {
|
||||
openshiftauth = depot.third_party.buildGo.package {
|
||||
name = "hg.lukegb.com/lukegb/depot/go/openshiftauth";
|
||||
srcs = [ ./openshiftauth.go ];
|
||||
deps = with depot.third_party; [
|
||||
gopkgs."github.com".dghubble.gologin.v2
|
||||
gopkgs."github.com".dghubble.gologin.v2.oauth2
|
||||
gopkgs."github.com".dgrijalva.jwt-go
|
||||
gopkgs."github.com".gorilla.mux
|
||||
gopkgs."github.com".gorilla.securecookie
|
||||
gopkgs."github.com".gorilla.sessions
|
||||
gopkgs."golang.org".x.oauth2
|
||||
];
|
||||
};
|
||||
|
||||
example = depot.third_party.buildGo.program {
|
||||
name = "example";
|
||||
srcs = [ ./example/example.go ];
|
||||
deps = with depot.third_party; [
|
||||
depot.go.openshiftauth.openshiftauth
|
||||
gopkgs."github.com".gorilla.mux
|
||||
];
|
||||
};
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"hg.lukegb.com/lukegb/depot/go/openshiftauth"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := mux.NewRouter()
|
||||
authR, err := openshiftauth.NewRouter(r)
|
||||
if err != nil {
|
||||
log.Fatalf("openshiftauth.NewRouter: %v", err)
|
||||
}
|
||||
|
||||
authR.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
|
||||
u := openshiftauth.UserFromContext(r.Context())
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
enc := json.NewEncoder(rw)
|
||||
enc.SetIndent("", " ")
|
||||
enc.Encode(u)
|
||||
})
|
||||
r.HandleFunc("/healthz", func(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.Header().Set("Content-Type", "text/plain")
|
||||
fmt.Fprintf(rw, "ok")
|
||||
})
|
||||
|
||||
http.Handle("/", r)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
|
@ -1,285 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package openshiftauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/dghubble/gologin/v2"
|
||||
"github.com/dghubble/gologin/v2/oauth2"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/gorilla/sessions"
|
||||
xoauth2 "golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type SecretGetter func(ctx context.Context) (string, error)
|
||||
|
||||
func TokenDirSecretGetter(ctx context.Context) (string, error) {
|
||||
v, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("couldn't read serviceaccount secret: %w", err)
|
||||
}
|
||||
return string(v), nil
|
||||
}
|
||||
|
||||
type BaseHandler struct {
|
||||
Config xoauth2.Config
|
||||
SecretGetter SecretGetter
|
||||
}
|
||||
|
||||
func (h *BaseHandler) config(ctx context.Context) (*xoauth2.Config, error) {
|
||||
s, err := h.SecretGetter(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg := h.Config
|
||||
cfg.ClientSecret = s
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
func (h *BaseHandler) wrapWithConfig(f func(cfg *xoauth2.Config) http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
cfg, err := h.config(r.Context())
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
f(cfg).ServeHTTP(rw, r)
|
||||
})
|
||||
}
|
||||
|
||||
func (h *BaseHandler) CallbackHandler(success, failure http.Handler) http.Handler {
|
||||
return h.wrapWithConfig(func(cfg *xoauth2.Config) http.Handler {
|
||||
return oauth2.CallbackHandler(cfg, success, failure)
|
||||
})
|
||||
}
|
||||
|
||||
func (h *BaseHandler) LoginHandler(failure http.Handler) http.Handler {
|
||||
return h.wrapWithConfig(func(cfg *xoauth2.Config) http.Handler {
|
||||
return oauth2.LoginHandler(cfg, failure)
|
||||
})
|
||||
}
|
||||
|
||||
type contextKey string
|
||||
|
||||
const (
|
||||
sessionContextKey contextKey = "session"
|
||||
)
|
||||
|
||||
func AttachSessionMiddleware(sess sessions.Store) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
s, err := sess.Get(r, "openshiftauth")
|
||||
if err != nil {
|
||||
log.Printf("decoding session: %v", err)
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), sessionContextKey, s)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Session(ctx context.Context) *sessions.Session {
|
||||
return ctx.Value(sessionContextKey).(*sessions.Session)
|
||||
}
|
||||
|
||||
func UserFromContext(ctx context.Context) *User {
|
||||
s := Session(ctx)
|
||||
if s.Values["user"] != nil {
|
||||
return s.Values["user"].(*User)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func secretFromEnv(name string, length int) []byte {
|
||||
var bs []byte
|
||||
if bsHex, ok := os.LookupEnv(name); ok {
|
||||
newBS, err := hex.DecodeString(bsHex)
|
||||
if err != nil {
|
||||
log.Printf("failed to decode %v as hex: %v", name, err)
|
||||
} else {
|
||||
bs = newBS
|
||||
}
|
||||
}
|
||||
if bs == nil {
|
||||
log.Printf("generating a random %v: this is bad", name)
|
||||
bs = securecookie.GenerateRandomKey(length)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Metadata struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"metadata"`
|
||||
FullName string `json:"fullName"`
|
||||
Identities []string `json:"identities"`
|
||||
Groups []string `json:"groups"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
gob.Register(&User{})
|
||||
}
|
||||
|
||||
func fetchUserForToken(ctx context.Context, token *xoauth2.Token) (*User, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.k8s.lukegb.tech:6443/apis/user.openshift.io/v1/users/~", nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("http.NewRequestWithContext for users/~ failed")
|
||||
}
|
||||
token.SetAuthHeader(req)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("executing request for users/~ failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("openshift returned non-OK status %d %v", resp.StatusCode, resp.Status)
|
||||
}
|
||||
|
||||
var u User
|
||||
if err := json.NewDecoder(resp.Body).Decode(&u); err != nil {
|
||||
return nil, fmt.Errorf("decoding response from openshift: %v", err)
|
||||
}
|
||||
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
func RequireAuth(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
u := UserFromContext(r.Context())
|
||||
if u == nil {
|
||||
redirURL := r.URL.ResolveReference(r.URL)
|
||||
redirURL.Path = "/.login/start"
|
||||
redirURL.RawQuery = url.Values{
|
||||
"next": []string{r.URL.String()},
|
||||
}.Encode()
|
||||
http.Redirect(rw, r, redirURL.String(), http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(rw, r)
|
||||
})
|
||||
}
|
||||
|
||||
type serviceAccountDetails struct {
|
||||
Namespace string `json:"kubernetes.io/serviceaccount/namespace"`
|
||||
Name string `json:"kubernetes.io/serviceaccount/service-account.name"`
|
||||
FullName string `json:"sub"`
|
||||
}
|
||||
|
||||
func parseServiceAccount() (*serviceAccountDetails, error) {
|
||||
token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading service account JWT: %w", err)
|
||||
}
|
||||
|
||||
// ParseUnverified is safe here because we're just using it to extract information about ourselves.
|
||||
p := &jwt.Parser{}
|
||||
claims := make(jwt.MapClaims)
|
||||
if _, _, err := p.ParseUnverified(string(token), claims); err != nil {
|
||||
return nil, fmt.Errorf("parsing JWT: %w", err)
|
||||
}
|
||||
val := func(s string) string {
|
||||
v, ok := claims[s]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return v.(string)
|
||||
}
|
||||
sad := &serviceAccountDetails{
|
||||
Namespace: val("kubernetes.io/serviceaccount/namespace"),
|
||||
Name: val("kubernetes.io/serviceaccount/service-account.name"),
|
||||
FullName: val("sub"),
|
||||
}
|
||||
return sad, nil
|
||||
}
|
||||
|
||||
func NewRouter(r *mux.Router) (*mux.Router, error) {
|
||||
sess := sessions.NewCookieStore(
|
||||
secretFromEnv("OPENSHIFT_AUTH_SESSION_SIGN_SECRET", 32),
|
||||
secretFromEnv("OPENSHIFT_AUTH_SESSION_ENC_SECRET", 32))
|
||||
r.Use(AttachSessionMiddleware(sess))
|
||||
|
||||
sad, err := parseServiceAccount()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading service account information: %w", err)
|
||||
}
|
||||
log.Printf("using service account token for %v", sad.FullName)
|
||||
|
||||
bh := &BaseHandler{
|
||||
Config: xoauth2.Config{
|
||||
ClientID: sad.FullName,
|
||||
Endpoint: xoauth2.Endpoint{
|
||||
AuthURL: "https://oauth-openshift.apps.k8s.lukegb.tech/oauth/authorize",
|
||||
TokenURL: "https://oauth-openshift.apps.k8s.lukegb.tech/oauth/token",
|
||||
},
|
||||
RedirectURL: fmt.Sprintf("%s/.login/callback", os.Getenv("OPENSHIFT_AUTH_BASE")),
|
||||
Scopes: []string{"user:info"},
|
||||
},
|
||||
SecretGetter: TokenDirSecretGetter,
|
||||
}
|
||||
baseURL, err := url.Parse(os.Getenv("OPENSHIFT_AUTH_BASE"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse OPENSHIFT_AUTH_BASE as URL: %v", err)
|
||||
}
|
||||
stateConfig := gologin.DefaultCookieConfig
|
||||
r.HandleFunc("/.login/start", func(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if nextURL, err := url.Parse(r.URL.Query().Get("next")); err == nil {
|
||||
nextURL = baseURL.ResolveReference(nextURL)
|
||||
if nextURL.Scheme != baseURL.Scheme || nextURL.User != nil || nextURL.Host != baseURL.Host {
|
||||
nextURL = nil
|
||||
}
|
||||
if nextURL != nil {
|
||||
s := Session(ctx)
|
||||
s.AddFlash(nextURL.RequestURI(), "_openshiftauth_next")
|
||||
sessions.Save(r, rw)
|
||||
}
|
||||
}
|
||||
oauth2.StateHandler(stateConfig, bh.LoginHandler(nil)).ServeHTTP(rw, r)
|
||||
})
|
||||
r.Handle("/.login/callback", oauth2.StateHandler(stateConfig, bh.CallbackHandler(
|
||||
http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
token, err := oauth2.TokenFromContext(ctx)
|
||||
if err != nil {
|
||||
http.Error(rw, "oauth2 TokenFromContext failed", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
u, err := fetchUserForToken(ctx, token)
|
||||
if err != nil {
|
||||
http.Error(rw, fmt.Sprintf("fetchUserForToken: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
s := Session(ctx)
|
||||
s.Values["user"] = *u
|
||||
sessions.Save(r, rw)
|
||||
|
||||
next := "/"
|
||||
nexts := s.Flashes("_openshiftauth_next")
|
||||
if len(nexts) >= 1 {
|
||||
next = nexts[0].(string)
|
||||
}
|
||||
http.Redirect(rw, r, next, http.StatusFound)
|
||||
}),
|
||||
nil)))
|
||||
|
||||
authR := r.PathPrefix("/").Subrouter()
|
||||
authR.Use(RequireAuth)
|
||||
|
||||
return authR, nil
|
||||
}
|
|
@ -22,7 +22,7 @@
|
|||
depot.third_party.gopkgs."github.com".gorilla.mux
|
||||
depot.third_party.gopkgs."github.com".jackc.pgtype
|
||||
depot.third_party.gopkgs."github.com".jackc.pgx.v4.pgxpool
|
||||
depot.go.openshiftauth.openshiftauth
|
||||
depot.third_party.gopkgs."github.com".pomerium.sdk-go
|
||||
];
|
||||
dockerData = [ (
|
||||
depot.pkgs.runCommand "source" {} ''
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
"github.com/jackc/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"hg.lukegb.com/lukegb/depot/go/openshiftauth"
|
||||
pomerium "github.com/pomerium/sdk-go"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -100,6 +100,17 @@ func bestVariant(vs []interface{}) interface{} {
|
|||
return bestVariant
|
||||
}
|
||||
|
||||
type mapDatastore map[interface{}]interface{}
|
||||
|
||||
func (ds mapDatastore) Get(key interface{}) (value interface{}, ok bool) {
|
||||
value, ok = ds[key]
|
||||
return value, ok
|
||||
}
|
||||
|
||||
func (ds mapDatastore) Add(key, value interface{}) {
|
||||
ds[key] = value
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
ctx := context.Background()
|
||||
|
@ -117,20 +128,30 @@ func main() {
|
|||
})
|
||||
|
||||
var authR *mux.Router
|
||||
if *localDisableAuth != "" {
|
||||
authR = r
|
||||
} else {
|
||||
authR, err = openshiftauth.NewRouter(r)
|
||||
var v *pomerium.Verifier
|
||||
if *localDisableAuth == "" {
|
||||
authR = r.PathPrefix("/").Subrouter()
|
||||
var err error
|
||||
v, err = pomerium.New(&pomerium.Options{
|
||||
JWKSEndpoint: "https://auth.int.lukegb.com/.well-known/pomerium/jwks.json",
|
||||
Datastore: mapDatastore{},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("openshiftauth.NewRouter: %v", err)
|
||||
log.Fatalf("pomerium.New: %v", err)
|
||||
}
|
||||
authR.Use(pomerium.AddIdentityToRequest(v))
|
||||
}
|
||||
authR = r
|
||||
|
||||
userFromContext := func(ctx context.Context) string {
|
||||
userFromContext := func(ctx context.Context) (string, error) {
|
||||
if *localDisableAuth != "" {
|
||||
return *localDisableAuth
|
||||
return *localDisableAuth, nil
|
||||
}
|
||||
return openshiftauth.UserFromContext(ctx).Metadata.Name
|
||||
identity, err := pomerium.FromContext(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return identity.User, nil
|
||||
}
|
||||
|
||||
writeError := func(rw http.ResponseWriter, status int, wrap string, err error) {
|
||||
|
@ -143,7 +164,11 @@ func main() {
|
|||
authR.PathPrefix("/media/").Handler(http.StripPrefix("/media/", http.FileServer(http.Dir(*mediaDirectory))))
|
||||
authR.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
user := userFromContext(ctx)
|
||||
user, err := userFromContext(ctx)
|
||||
if err != nil {
|
||||
http.Error(rw, fmt.Errorf("userFromContext: %w", err).Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
twitterAccounts := userMapping[user]
|
||||
|
||||
var allTweets, timelineTweets, unfetchedMedia, unfetchedRelated int
|
||||
|
@ -214,7 +239,11 @@ ORDER BY 1
|
|||
})
|
||||
})
|
||||
isAllowedToSee := func(ctx context.Context, twitterUser string) bool {
|
||||
twitterAccounts := userMapping[userFromContext(ctx)]
|
||||
user, err := userFromContext(ctx)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
twitterAccounts := userMapping[user]
|
||||
for _, a := range twitterAccounts {
|
||||
if a == twitterUser {
|
||||
return true
|
||||
|
@ -241,8 +270,13 @@ ORDER BY 1
|
|||
ctx := r.Context()
|
||||
vars := mux.Vars(r)
|
||||
twitterUser := vars["twitterUser"]
|
||||
user, err := userFromContext(ctx)
|
||||
if err != nil {
|
||||
http.Error(rw, fmt.Errorf("userFromContext: %w", err).Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if !isAllowedToSee(ctx, twitterUser) {
|
||||
writeError(rw, http.StatusNotFound, "no such twitter user being archived", fmt.Errorf("user %q attempted to access %q", userFromContext(ctx), twitterUser))
|
||||
writeError(rw, http.StatusNotFound, "no such twitter user being archived", fmt.Errorf("user %q attempted to access %q", user, twitterUser))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
18
third_party/gopkgs/github.com/pomerium/sdk-go/default.nix
vendored
Normal file
18
third_party/gopkgs/github.com/pomerium/sdk-go/default.nix
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
{ depot, ... }:
|
||||
depot.third_party.buildGo.external {
|
||||
path = "github.com/pomerium/sdk-go";
|
||||
src = depot.third_party.nixpkgs.fetchFromGitHub {
|
||||
owner = "pomerium";
|
||||
repo = "sdk-go";
|
||||
rev = "v0.0.5";
|
||||
hash = "sha256:08sa6jzixw09w519lr69wb69z7hx1158jbji9kh6f9wvxp0ny9lw";
|
||||
};
|
||||
deps = with depot.third_party; [
|
||||
gopkgs."gopkg.in".square."go-jose.v2"
|
||||
gopkgs."gopkg.in".square."go-jose.v2".jwt
|
||||
];
|
||||
}
|
18
third_party/gopkgs/gopkg.in/square/go-jose.v2/default.nix
vendored
Normal file
18
third_party/gopkgs/gopkg.in/square/go-jose.v2/default.nix
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
{ depot, ... }:
|
||||
depot.third_party.buildGo.external {
|
||||
path = "gopkg.in/square/go-jose.v2";
|
||||
src = depot.third_party.nixpkgs.fetchFromGitHub {
|
||||
owner = "square";
|
||||
repo = "go-jose";
|
||||
rev = "v2.5.1";
|
||||
hash = "sha256:0z0hbmb5yyvnjkyiyn259wkbqbjxs2pzx87jz472shn2bgggxa4n";
|
||||
};
|
||||
deps = with depot.third_party; [
|
||||
gopkgs."golang.org".x.crypto.ed25519
|
||||
gopkgs."golang.org".x.crypto.pbkdf2
|
||||
];
|
||||
}
|
Loading…
Reference in a new issue