internal/frontend : serve static assets (#392)

Signed-off-by: Bobby DeSimone <bobbydesimone@gmail.com>
This commit is contained in:
Bobby DeSimone 2019-11-22 17:46:01 -08:00 committed by GitHub
parent f20d913abe
commit ebee64b70b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 700 additions and 502 deletions

View file

@ -0,0 +1,237 @@
{{define "dashboard.html"}}
<!DOCTYPE html>
<html lang="en" charset="utf-8">
<head>
<title>Pomerium</title>
{{template "header.html"}}
</head>
<body>
<div id="main">
<div id="info-box">
<div class="card">
{{if .Session.Picture }}
<img class="icon" src="{{.Session.Picture}}" alt="user image" />
{{else}}
<img
class="icon"
src="/.pomerium/assets/img/account_circle-24px.svg"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
/>
{{end}}
<form method="POST" action="/.pomerium/sign_out">
<section>
<h2>Current user</h2>
<p class="message">Your current session details.</p>
<fieldset>
{{if .Session.Name}}
<label>
<span>Name</span>
<input
type="text"
class="field"
value="{{.Session.Name}}"
title="{{.Session.Name}}"
disabled
/>
</label>
{{else}} {{if .Session.GivenName}}
<label>
<span>Given Name</span>
<input
type="text"
class="field"
value="{{.Session.GivenName}}"
title="{{.Session.GivenName}}"
disabled
/>
</label>
{{end}} {{if .Session.FamilyName}}
<label>
<span>Family Name</span>
<input
type="text"
class="field"
value="{{.Session.FamilyName}}"
title="{{.Session.FamilyName}}"
disabled
/>
</label>
{{end}} {{end}} {{if .Session.Subject}}
<label>
<span>UserID</span>
<input
type="text"
class="field"
value="{{.Session.Subject}}"
title="{{.Session.Subject}}"
disabled
/>
</label>
{{end}} {{if .Session.Email}}
<label>
<span>Email</span>
<input
type="email"
class="field"
value="{{.Session.Email}}"
title="{{.Session.Email}}"
disabled
/>
</label>
{{end}} {{if .Session.User}}
<label>
<span>User</span>
<input
type="text"
class="field"
value="{{.Session.User}}"
title="{{.Session.User}}"
disabled
/>
</label>
{{end}} {{range $i,$_:= .Session.Groups}}
<label>
{{if eq $i 0}}
<span>Group</span>
{{else}}
<span></span>
{{end}}
<input
type="text"
class="field"
value="{{.}}"
title="{{.}}"
disabled
/>
</label>
{{end}} {{if .Session.Expiry}}
<label>
<span>Expiry</span>
<input
type="text"
class="field"
value="{{.Session.Expiry.Time}}"
title="{{.Session.Expiry.Time}}"
disabled
/>
</label>
{{end}} {{if .Session.IssuedAt}}
<label>
<span>Issued</span>
<input
type="text"
class="field"
value="{{.Session.IssuedAt.Time}}"
title="{{.Session.IssuedAt.Time}}"
disabled
/>
</label>
{{end}} {{if .Session.Issuer}}
<label>
<span>Issuer</span>
<input
type="text"
class="field"
value="{{ .Session.Issuer}}"
title="{{ .Session.Issuer}}"
disabled
/>
</label>
{{end}} {{range $i, $_:= .Session.Audience}}
<label>
{{if eq $i 0}}
<span>Audience</span>
{{else}}
<span></span>
{{end}}
<input
type="text"
class="field"
title="{{ . }}"
value="{{ . }}"
disabled
/>
</label>
{{end}} {{if .Session.ImpersonateEmail}}
<label>
<span>Impersonating Email</span>
<input
type="text"
class="field"
value="{{.Session.ImpersonateEmail}}"
disabled
/>
</label>
{{end}} {{range $i,$_:= .Session.ImpersonateGroups}}
<label>
{{if eq $i 0}}
<span>Impersonating Group</span>
{{else}}
<span></span>
{{end}}
<input
type="text"
class="field"
value="{{.}}"
title="{{.}}"
disabled
/>
</label>
{{end}}
</fieldset>
</section>
<div class="flex">
{{ .csrfField }}
<button class="button full" type="submit">Sign Out</button>
</div>
</form>
{{if .IsAdmin}}
<form method="POST" action="/.pomerium/impersonate">
<section>
<h2>Sign-in-as</h2>
<p class="message">
Administrators can temporarily impersonate another user.
</p>
<fieldset>
<label>
<span>Email</span>
<input
name="email"
type="email"
class="field"
value=""
placeholder="user@example.com"
/>
</label>
<label>
<span>Group</span>
<input
name="group"
type="text"
class="field"
value=""
placeholder="engineering"
/>
</label>
</fieldset>
</section>
<div class="flex">
{{ .csrfField }}
<button class="button full" type="submit">
Impersonate session
</button>
</div>
</form>
{{ end }}
</div>
</div>
{{template "footer.html"}}
</div>
</body>
</html>
{{end}}

View file

@ -0,0 +1,35 @@
{{define "error.html"}}
<!DOCTYPE html>
<html lang="en" charset="utf-8">
<head>
<title>{{.Code}} - {{.Title}}</title>
{{template "header.html"}}
</head>
<body>
<div id="main">
<div id="info-box">
<div class="card">
<img
class="icon"
src="/.pomerium/assets/img/error-24px.svg"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
/>
<h1 class="title">{{.Title}}</h1>
<section>
<p class="message">
{{if .Message}}{{.Message}}{{end}} {{if .CanDebug}}Troubleshoot
your
<a href="/.pomerium/">session</a>.{{end}} {{if .RequestID}}
Request {{.RequestID}}{{end}}
</p>
</section>
</div>
</div>
{{template "footer.html"}}
</div>
</body>
</html>
{{end}}

View file

@ -0,0 +1,12 @@
{{define "footer.html"}}
<footer>
<a href="https://www.pomerium.io">
<img
class="powered-by-pomerium"
src="/.pomerium/assets/img/pomerium.svg"
xmlns="http://www.w3.org/2000/svg"
height="25"
/>
</a>
</footer>
{{end}}

View file

@ -0,0 +1,12 @@
{{define "header.html"}}
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
/>
<link
rel="stylesheet"
type="text/css"
href="/.pomerium/assets/style/main.css"
/>
{{end}}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#6e43e8" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"/><path d="M0 0h24v24H0z" fill="none"/></svg>

After

Width:  |  Height:  |  Size: 380 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path fill="#6e43e8" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>

After

Width:  |  Height:  |  Size: 249 B

View file

@ -0,0 +1 @@
<svg viewBox="0 0 139 30" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-5 -5.5)"><path d="m10.6 5.5h127.8c3.09 0 5.6 2 5.6 4.39v21.22c0 2.42-2.51 4.39-5.6 4.39h-127.8c-3.09 0-5.6-2-5.6-4.39v-21.22c0-2.42 2.51-4.39 5.6-4.39z" fill="#6e43e8" fill-rule="evenodd"/><g fill="#fff"><path d="m75.4 26.62h-1.46l1.13-2.79-2.25-5.69h1.54l1.42 3.86 1.43-3.87h1.54zm-5.61-2.44a2.42 2.42 0 0 1 -1.5-.55v.37h-1.51v-8.44h1.51v3a2.48 2.48 0 0 1 1.5-.55c1.58 0 2.66 1.28 2.66 3.09s-1.08 3.08-2.66 3.08zm-.32-4.88a1.68 1.68 0 0 0 -1.18.53v2.52a1.65 1.65 0 0 0 1.18.54c.85 0 1.44-.73 1.44-1.8s-.59-1.79-1.44-1.79zm-8.8 4.33a2.38 2.38 0 0 1 -1.5.55c-1.57 0-2.66-1.27-2.66-3.09s1.09-3.09 2.66-3.09a2.44 2.44 0 0 1 1.5.55v-3h1.52v8.45h-1.52zm0-3.8a1.63 1.63 0 0 0 -1.17-.53c-.86 0-1.45.73-1.45 1.79s.59 1.8 1.45 1.8a1.6 1.6 0 0 0 1.17-.54zm-9 1.68a1.69 1.69 0 0 0 1.8 1.49 3.55 3.55 0 0 0 1.76-.56v1.26a4.73 4.73 0 0 1 -2 .46 3 3 0 0 1 -3-3.13 2.87 2.87 0 0 1 2.88-3.03 2.66 2.66 0 0 1 2.59 3 5.53 5.53 0 0 1 0 .56zm1.37-2.34a1.38 1.38 0 0 0 -1.37 1.36h2.57a1.28 1.28 0 0 0 -1.19-1.36zm-5.34.93v3.9h-1.5v-5.9h1.51v.59a2 2 0 0 1 1.45-.69 1.65 1.65 0 0 1 .49.06v1.35a1.83 1.83 0 0 0 -.53-.07 1.87 1.87 0 0 0 -1.41.76zm-6.7 1.41a1.69 1.69 0 0 0 1.76 1.49 3.55 3.55 0 0 0 1.76-.56v1.26a4.73 4.73 0 0 1 -2 .46 3 3 0 0 1 -3-3.13 2.87 2.87 0 0 1 2.88-3.03 2.66 2.66 0 0 1 2.6 3 5.53 5.53 0 0 1 0 .56zm1.37-2.34a1.38 1.38 0 0 0 -1.37 1.36h2.57a1.28 1.28 0 0 0 -1.23-1.36zm-6.67 4.83-1.2-4-1.2 4h-1.3l-2-5.9h1.51l1.19 4 1.19-4h1.37l1.19 4 1.19-4h1.51l-2 5.9zm-9.23.14a2.94 2.94 0 0 1 -3-3.09 3 3 0 1 1 6.07 0 2.94 2.94 0 0 1 -3.07 3.13zm0-4.92c-.88 0-1.49.75-1.49 1.83s.61 1.83 1.49 1.83 1.53-.7 1.53-1.79-.65-1.83-1.53-1.83zm-6.62 1.87h-1.36v2.91h-1.49v-8.07h2.87a2.61 2.61 0 1 1 0 5.2zm-.22-4h-1.14v2.81h1.14a1.38 1.38 0 1 0 0-2.75z" fill-rule="evenodd"/><path d="m132.71 14.9a3.93 3.93 0 0 0 -3.93-3.9h-34.19a3.93 3.93 0 0 0 -3.93 3.92v16.14h2.71v-4.51a5.49 5.49 0 1 1 11 0v4.51h2v-4.51a5.49 5.49 0 1 1 11 0v4.51h2v-4.51a5.49 5.49 0 1 1 11 0v4.51h2.47zm-39.34 4.1a5.49 5.49 0 1 1 11 0zm12.95 0a5.49 5.49 0 1 1 11 0zm12.94 0a5.49 5.49 0 1 1 11 0z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -0,0 +1,239 @@
* {
margin: 0;
padding: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: none;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", sans-serif;
font-size: 15px;
line-height: 1.4em;
}
body {
display: flex;
flex-direction: row;
align-items: center;
background: #f8f8ff;
}
#main {
width: 100%;
height: 100vh;
text-align: center;
display: flex;
flex-direction: column;
justify-content: space-between;
}
#info-box {
max-width: 480px;
width: 480px;
margin-top: 200px;
margin-right: auto;
margin-bottom: 0px;
margin-left: auto;
justify-content: center;
flex-grow: 1;
}
section {
display: flex;
flex-direction: column;
position: relative;
text-align: left;
}
h1 {
font-size: 36px;
font-weight: 400;
text-align: center;
letter-spacing: 0.3px;
text-transform: uppercase;
color: #32325d;
}
h1.title {
text-align: center;
background: #f8f8ff;
margin: 15px 0;
}
h2 {
margin: 15px 0;
color: #32325d;
text-transform: uppercase;
letter-spacing: 0.3px;
font-size: 18px;
font-weight: 650;
padding-top: 20px;
}
.card {
margin: 0 -30px;
padding: 20px 30px 30px;
border-radius: 4px;
border: 1px solid #e8e8fb;
background-color: #f8f8ff;
}
fieldset {
margin-bottom: 20px;
background: #fcfcff;
box-shadow: 0 1px 3px 0 rgba(50, 50, 93, 0.15),
0 4px 6px 0 rgba(112, 157, 199, 0.15);
border-radius: 4px;
border: none;
font-size: 0;
}
fieldset label {
position: relative;
display: flex;
flex-direction: row;
height: 42px;
padding: 10px 0;
align-items: center;
justify-content: center;
font-weight: 400;
}
fieldset label:not(:last-child) {
border-bottom: 1px solid #f0f5fa;
}
fieldset label span {
min-width: 125px;
padding: 0 15px;
text-align: right;
}
#group {
display: flex;
align-items: center;
}
#group::before {
display: inline-flex;
content: "";
height: 15px;
background-position: -1000px -1000px;
background-repeat: no-repeat;
}
.icon {
display: inline-table;
margin-top: -72px;
text-align: center;
width: 75px;
height: auto;
border-radius: 50%;
}
.icon svg {
fill: #6e43e8;
background: red;
}
.logo {
padding-bottom: 20px;
padding-top: 20px;
width: 115px;
height: auto;
}
p.message {
margin-top: 10px;
margin-bottom: 10px;
padding-bottom: 20px;
}
.field {
flex: 1;
padding: 0 15px;
background: transparent;
font-weight: 400;
color: #31325f;
outline: none;
cursor: text;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
fieldset .select::after {
content: "";
position: absolute;
width: 9px;
height: 5px;
right: 20px;
top: 50%;
margin-top: -2px;
pointer-events: none;
}
input {
border-style: none;
outline: none;
color: #313b3f;
}
select {
flex: 1;
border-style: none;
outline: none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
outline: none;
color: #313b3f;
cursor: pointer;
background: transparent;
}
.flex {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
}
.button {
color: #fcfcff;
background: #6e43e8;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
border-radius: 4px;
border: 0;
font-weight: 700;
width: 50%;
height: 40px;
outline: none;
cursor: pointer;
padding: 10px;
text-decoration: none;
}
.button.half {
flex-grow: 0;
flex-shrink: 0;
flex-basis: calc(50% - 10px);
}
.button.full {
flex-grow: 1;
}
.button:hover {
transform: translateY(-1px);
box-shadow: 0 7px 14px 0 rgba(50, 50, 93, 0.1),
0 3px 6px 0 rgba(0, 0, 0, 0.08);
}
.off-color {
background: #5735b5;
}
.powered-by-pomerium {
align-items: center;
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,55 @@
//go:generate statik -src=./assets -include=*.svg,*.html,*.css,*.js
package frontend // import "github.com/pomerium/pomerium/internal/frontend"
import (
"fmt"
"html/template"
"io/ioutil"
"net/http"
"os"
"github.com/rakyll/statik/fs"
_ "github.com/pomerium/pomerium/internal/frontend/statik" // load static assets
)
// NewTemplates loads pomerium's templates. Panics on failure.
func NewTemplates() (*template.Template, error) {
t := template.New("pomerium-templates")
statikFS, err := fs.New()
if err != nil {
return nil, fmt.Errorf("internal/frontend: error creating new file system: %w", err)
}
err = fs.Walk(statikFS, "/html", func(filePath string, fileInfo os.FileInfo, err error) error {
if !fileInfo.IsDir() {
file, err := statikFS.Open(filePath)
if err != nil {
return fmt.Errorf("internal/frontend: error opening %s: %w", filePath, err)
}
buf, err := ioutil.ReadAll(file)
if err != nil {
return fmt.Errorf("internal/frontend: error reading %s: %w", filePath, err)
}
t.Parse(string(buf))
}
return nil
})
if err != nil {
return nil, err
}
return t, nil
}
// MustAssetHandler wraps a call to the embedded static file system and panics
// if the error is non-nil. It is intended for use in variable initializations
func MustAssetHandler() http.Handler {
statikFS, err := fs.New()
if err != nil {
panic(err)
}
return http.FileServer(statikFS)
}

View file

@ -0,0 +1,63 @@
package frontend
import (
"html/template"
"reflect"
"testing"
_ "github.com/pomerium/pomerium/internal/frontend/statik"
"github.com/rakyll/statik/fs"
)
func TestTemplatesCompile(t *testing.T) {
templates := template.Must(NewTemplates())
if templates == nil {
t.Errorf("unexpected nil value %#v", templates)
}
}
func TestNewTemplates(t *testing.T) {
tests := []struct {
name string
testData string
want *template.Template
wantErr bool
}{
{"empty statik fs", "", nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fs.Register(tt.testData)
got, err := NewTemplates()
if (err != nil) != tt.wantErr {
t.Errorf("NewTemplates() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewTemplates() = %v, want %v", got, tt.want)
}
})
}
}
func TestMustAssetHandler(t *testing.T) {
tests := []struct {
name string
testData string
wantPanic bool
}{
{"empty statik fs", "", true},
{"empty statik fs", "", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Errorf("The code did not panic")
}
}()
MustAssetHandler()
})
}
}