Initial commit

This commit is contained in:
2020-05-20 10:43:12 +02:00
commit 8ba7b5d492
27 changed files with 1326 additions and 0 deletions

83
internal/config/config.go Normal file
View File

@ -0,0 +1,83 @@
package config
import (
"io"
"io/ioutil"
"time"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
)
type Config struct {
HTTP HTTPConfig `yaml:"http"`
OIDC OIDCConfig `yaml:"oidc"`
}
// NewFromFile retrieves the configuration from the given file
func NewFromFile(filepath string) (*Config, error) {
config := NewDefault()
data, err := ioutil.ReadFile(filepath)
if err != nil {
return nil, errors.Wrapf(err, "could not read file '%s'", filepath)
}
if err := yaml.Unmarshal(data, config); err != nil {
return nil, errors.Wrapf(err, "could not unmarshal configuration")
}
return config, nil
}
type HTTPConfig struct {
Address string `yaml:"address"`
CookieAuthenticationKey string `yaml:"cookieAuthenticationKey"`
CookieEncryptionKey string `yaml:"cookieEncryptionKey"`
CookieMaxAge int `yaml:"cookieMaxAge"`
TemplateDir string `yaml:"templateDir"`
PublicDir string `yaml:"publicDir"`
}
type OIDCConfig struct {
ClientID string `yaml:"clientId"`
ClientSecret string `yaml:"clientSecret"`
IssuerURL string `ymal:"issuerUrl"`
RedirectURL string `yaml:"redirectUrl"`
}
func NewDumpDefault() *Config {
config := NewDefault()
return config
}
func NewDefault() *Config {
return &Config{
HTTP: HTTPConfig{
Address: ":3002",
CookieAuthenticationKey: "",
CookieEncryptionKey: "",
CookieMaxAge: int((time.Hour * 1).Seconds()), // 1 hour
TemplateDir: "template",
PublicDir: "public",
},
OIDC: OIDCConfig{
IssuerURL: "http://localhost:4444/",
RedirectURL: "http://localhost:3002/oauth2/callback",
},
}
}
func Dump(config *Config, w io.Writer) error {
data, err := yaml.Marshal(config)
if err != nil {
return errors.Wrap(err, "could not dump config")
}
if _, err := w.Write(data); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,9 @@
package config
import "gitlab.com/wpetit/goweb/service"
func ServiceProvider(config *Config) service.Provider {
return func(ctn *service.Container) (interface{}, error) {
return config, nil
}
}

View File

@ -0,0 +1,33 @@
package config
import (
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/service"
)
const ServiceName service.Name = "config"
// From retrieves the config service in the given container
func From(container *service.Container) (*Config, error) {
service, err := container.Service(ServiceName)
if err != nil {
return nil, errors.Wrapf(err, "error while retrieving '%s' service", ServiceName)
}
srv, ok := service.(*Config)
if !ok {
return nil, errors.Errorf("retrieved service is not a valid '%s' service", ServiceName)
}
return srv, nil
}
// Must retrieves the config service in the given container or panic otherwise
func Must(container *service.Container) *Config {
srv, err := From(container)
if err != nil {
panic(err)
}
return srv
}

24
internal/route/helper.go Normal file
View File

@ -0,0 +1,24 @@
package route
import (
"net/http"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/middleware/container"
"gitlab.com/wpetit/goweb/service/template"
"gitlab.com/wpetit/goweb/template/html"
)
func extendTemplateData(w http.ResponseWriter, r *http.Request, data template.Data) template.Data {
ctn := container.Must(r.Context())
data, err := template.Extend(data,
html.WithFlashes(w, r, ctn),
template.WithBuildInfo(w, r, ctn),
)
if err != nil {
panic(errors.Wrap(err, "could not extend template data"))
}
return data
}

53
internal/route/login.go Normal file
View File

@ -0,0 +1,53 @@
package route
import (
"encoding/json"
"net/http"
oidc "forge.cadoles.com/wpetit/goweb-oidc"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
"gitlab.com/wpetit/goweb/middleware/container"
"gitlab.com/wpetit/goweb/service/template"
)
func serveHomePage(w http.ResponseWriter, r *http.Request) {
ctn := container.Must(r.Context())
tmpl := template.Must(ctn)
idToken, err := oidc.IDToken(w, r)
if err != nil {
panic(errors.Wrap(err, "could not retrieve idToken"))
}
jsonIDToken, err := json.MarshalIndent(idToken, "", " ")
if err != nil {
panic(errors.Wrap(err, "could not encode idToken"))
}
data := extendTemplateData(w, r, template.Data{
"IDToken": idToken,
"JSONIDToken": string(jsonIDToken),
})
if err := tmpl.RenderPage(w, "home.html.tmpl", data); err != nil {
panic(errors.Wrapf(err, "could not render '%s' page", r.URL.Path))
}
}
func handleLogin(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
idToken, err := oidc.IDToken(w, r)
if err != nil {
logger.Error(ctx, "could not retrieve idToken", logger.E(err))
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
logger.Info(ctx, "user logged in", logger.F("sub", idToken.Subject))
http.Redirect(w, r, "/", http.StatusSeeOther)
}

27
internal/route/logout.go Normal file
View File

@ -0,0 +1,27 @@
package route
import (
"net/http"
oidc "forge.cadoles.com/wpetit/goweb-oidc"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/middleware/container"
"gitlab.com/wpetit/goweb/service/session"
)
func handleLogout(w http.ResponseWriter, r *http.Request) {
ctn := container.Must(r.Context())
sess, err := session.Must(ctn).Get(w, r)
if err != nil {
panic(errors.Wrap(err, "could not retrieve session"))
}
if err := sess.Delete(w, r); err != nil {
panic(errors.Wrap(err, "could not delete session"))
}
client := oidc.Must(ctn)
client.Logout(w, r)
}

25
internal/route/mount.go Normal file
View File

@ -0,0 +1,25 @@
package route
import (
oidc "forge.cadoles.com/wpetit/goweb-oidc"
"forge.cadoles.com/wpetit/goweb-oidc/internal/config"
"github.com/go-chi/chi"
"gitlab.com/wpetit/goweb/static"
)
func Mount(r *chi.Mux, config *config.Config) error {
r.Group(func(r chi.Router) {
r.Use(oidc.Middleware)
r.Get("/", serveHomePage)
})
r.With(oidc.HandleCallback).Get("/oauth2/callback", handleLogin)
r.Get("/logout", handleLogout)
notFoundHandler := r.NotFoundHandler()
r.Get("/*", static.Dir(config.HTTP.PublicDir, "", notFoundHandler))
return nil
}