mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-30 17:37:25 +02:00
- all: prefer `FormValues` to `ParseForm` with subsequent `Form.Get`s - all: refactor authentication stack to be checked by middleware, and accessible via request context. - all: replace http.ServeMux with gorilla/mux’s router - all: replace custom CSRF checks with gorilla/csrf middleware - authenticate: extract callback path as constant. - internal/config: implement stringer interface for policy - internal/cryptutil: add helper func `NewBase64Key` - internal/cryptutil: rename `GenerateKey` to `NewKey` - internal/cryptutil: rename `GenerateRandomString` to `NewRandomStringN` - internal/middleware: removed alice in favor of gorilla/mux - internal/sessions: remove unused `ValidateRedirectURI` and `ValidateClientSecret` - internal/sessions: replace custom CSRF with gorilla/csrf fork that supports custom handler protection - internal/urlutil: add `SignedRedirectURL` to create hmac'd URLs - internal/urlutil: add `ValidateURL` helper to parse URL options - internal/urlutil: add `GetAbsoluteURL` which takes a request and returns its absolute URL. - proxy: remove holdover state verification checks; we no longer are setting sessions in any proxy routes so we don’t need them. - proxy: replace un-named http.ServeMux with named domain routes. Signed-off-by: Bobby DeSimone <bobbydesimone@gmail.com>
387 lines
15 KiB
Go
387 lines
15 KiB
Go
package templates // import "github.com/pomerium/pomerium/internal/templates"
|
|
|
|
import (
|
|
"html/template"
|
|
)
|
|
|
|
// New loads html and style resources directly. Panics on failure.
|
|
func New() *template.Template {
|
|
t := template.New("pomerium-templates")
|
|
template.Must(t.Parse(`
|
|
{{define "header.html"}}
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
|
<style>
|
|
* {
|
|
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;
|
|
// margin-right: 10px;
|
|
}
|
|
|
|
.icon {
|
|
display: inline-table;
|
|
margin-top: -72px;
|
|
background: #F8F8FF;
|
|
text-align: center;
|
|
width: 75px;
|
|
height: auto;
|
|
}
|
|
|
|
.logo {
|
|
width: 115px;
|
|
height: auto;
|
|
}
|
|
|
|
.ok{
|
|
fill: #6E43E8;
|
|
}
|
|
|
|
.error{
|
|
fill: #EB292F;
|
|
}
|
|
|
|
p.message {
|
|
margin-top: 10px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.field {
|
|
flex: 1;
|
|
padding: 0 15px;
|
|
background: transparent;
|
|
font-weight: 400;
|
|
color: #31325f;
|
|
outline: none;
|
|
cursor: text;
|
|
}
|
|
|
|
fieldset .select::after {
|
|
content: '';
|
|
position: absolute;
|
|
width: 9px;
|
|
height: 5px;
|
|
right: 20px;
|
|
top: 50%;
|
|
margin-top: -2px;
|
|
pointer-events: none;
|
|
background: #6E43E8 url("data:image/svg+xml;utf8,<svg viewBox='0 0 140 140' width='24' height='24' xmlns='http://www.w3.org/2000/svg'><g><path d='m121.3,34.6c-1.6-1.6-4.2-1.6-5.8,0l-51,51.1-51.1-51.1c-1.6-1.6-4.2-1.6-5.8,0-1.6,1.6-1.6,4.2 0,5.8l53.9,53.9c0.8,0.8 1.8,1.2 2.9,1.2 1,0 2.1-0.4 2.9-1.2l53.9-53.9c1.7-1.6 1.7-4.2 0.1-5.8z' fill='white'/></g></svg>") no-repeat;
|
|
|
|
}
|
|
|
|
input {
|
|
flex: 1;
|
|
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;
|
|
}
|
|
|
|
</style>
|
|
{{end}}`))
|
|
|
|
template.Must(t.Parse(`
|
|
{{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">
|
|
<svg class="icon error" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM4 12c0-4.42 3.58-8 8-8 1.85 0 3.55.63 4.9 1.69L5.69 16.9C4.63 15.55 4 13.85 4 12zm8 8c-1.85 0-3.55-.63-4.9-1.69L18.31 7.1C19.37 8.45 20 10.15 20 12c0 4.42-3.58 8-8 8z"/></svg>
|
|
<h1 class="title">{{.Title}}</h1>
|
|
<section>
|
|
<p class="message">
|
|
{{if .Message}}{{.Message}}</br>{{end}}
|
|
{{if .CanDebug}}Troubleshoot your <a href="/.pomerium">session</a>.</br>{{end}}
|
|
{{if .RequestID}} Request {{.RequestID}}</br>{{end}}
|
|
|
|
</p>
|
|
</section>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<footer>
|
|
<a href="https://www.pomerium.io" style="display: block;">
|
|
<svg class="logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 139 30"><defs><style>.a {fill: #6e43e8;}.a,.b {fill-rule: evenodd;}.b,.c {fill: #fff;}</style></defs><title>powered-by-pomerium</title><path class="a" d="M10.6,5.5H138.4c3.09,0,5.6,2,5.6,4.39V31.11c0,2.42-2.51,4.39-5.6,4.39H10.6c-3.09,0-5.6-2-5.6-4.39V9.89C5,7.47,7.51,5.5,10.6,5.5Z" transform="translate(-5 -5.5)" /><path class="b" d="M75.4,26.62H73.94l1.13-2.79-2.25-5.69h1.54L75.78,22l1.43-3.87h1.54Zm-5.61-2.44a2.42,2.42,0,0,1-1.5-.55V24H66.78V15.56h1.51v3a2.48,2.48,0,0,1,1.5-.55c1.58,0,2.66,1.28,2.66,3.09S71.37,24.18,69.79,24.18Zm-.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.8S70.32,19.3,69.47,19.3Zm-8.8,4.33a2.38,2.38,0,0,1-1.5.55c-1.57,0-2.66-1.27-2.66-3.09S57.6,18,59.17,18a2.44,2.44,0,0,1,1.5.55v-3h1.52V24H60.67Zm0-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,53.47,23a3.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.13A2.87,2.87,0,0,1,53.11,18,2.66,2.66,0,0,1,55.7,21a5.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,53.05,19.17Zm-5.34.93V24H46.2v-5.9h1.51v.59A2,2,0,0,1,49.16,18a1.65,1.65,0,0,1,.49.06v1.35a1.83,1.83,0,0,0-.53-.07A1.87,1.87,0,0,0,47.71,20.1ZM41,21.51A1.69,1.69,0,0,0,42.76,23a3.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.13A2.87,2.87,0,0,1,42.4,18,2.66,2.66,0,0,1,45,21a5.53,5.53,0,0,1,0,.56Zm1.37-2.34A1.38,1.38,0,0,0,41,20.53h2.57A1.28,1.28,0,0,0,42.34,19.17ZM35.7,24l-1.2-4-1.2,4H32l-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,0A2.94,2.94,0,0,1,26.47,24.18Zm0-4.92c-.88,0-1.49.75-1.49,1.83s.61,1.83,1.49,1.83S28,22.18,28,21.09,27.35,19.26,26.47,19.26Zm-6.62,1.87H18.49V24H17V15.93h2.87a2.61,2.61,0,1,1,0,5.2Zm-.22-4H18.49V19.9h1.14a1.38,1.38,0,1,0,0-2.75Z" transform="translate(-5 -5.5)" /><path class="c" d="M132.71,14.9A3.93,3.93,0,0,0,128.78,11H94.59a3.93,3.93,0,0,0-3.93,3.92V31.06h2.71V26.55h0a5.49,5.49,0,1,1,11,0h0v4.51h2V26.55h0a5.49,5.49,0,1,1,11,0h0v4.51h2V26.55h0a5.49,5.49,0,1,1,11,0h0v4.51h2.47ZM93.37,19a5.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" transform="translate(-5 -5.5)" /></svg>
|
|
</a>
|
|
</footer>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
|
|
{{end}}`))
|
|
|
|
t = template.Must(t.Parse(`
|
|
{{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">
|
|
<svg class="icon ok" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
<path fill="none" d="M0 0h24v24H0V0z" />
|
|
<path d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
|
|
</svg>
|
|
<form method="POST" action="{{.SignoutURL}}">
|
|
<section>
|
|
<h2>Current user</h2>
|
|
<p class="message">Your current session details.</p>
|
|
<fieldset>
|
|
<label>
|
|
<span>Email</span>
|
|
<input name="email" type="email" class="field" value="{{.Email}}" disabled>
|
|
</label>
|
|
<label>
|
|
<span>User</span>
|
|
<input name="user" type="text" class="field" value="{{.User}}" disabled>
|
|
</label>
|
|
<label class="select">
|
|
<span>Groups</span>
|
|
<div id="group" class="field">
|
|
<select name="group">
|
|
{{range .Groups}}
|
|
<option value="{{.}}">{{.}}</option>
|
|
{{end}}
|
|
</select>
|
|
</div>
|
|
</label>
|
|
<label>
|
|
<span>Expiry</span>
|
|
<input name="session expiration" type="text" class="field" value="{{.RefreshDeadline}}" disabled>
|
|
</label>
|
|
</fieldset>
|
|
</section>
|
|
<div class="flex">
|
|
{{ .csrfField }}
|
|
<button class="button full" type="submit">Sign Out</button>
|
|
</div>
|
|
</form>
|
|
<section>
|
|
<h2>Refresh Identity</h2>
|
|
<p class="message">Pomerium will automatically refresh your user session. However, if your group memberships have recently changed and haven't taken effect yet, you can refresh your session manually.</p>
|
|
|
|
<form method="POST" action="/.pomerium/refresh">
|
|
<div class="flex">
|
|
{{ .csrfField }}
|
|
<button class="button full" type="submit">Refresh</button>
|
|
</div>
|
|
</form>
|
|
</section>
|
|
|
|
{{if .IsAdmin}}
|
|
<form method="POST" action="/.pomerium/impersonate">
|
|
<section>
|
|
<h2>Sign-in-as</h2>
|
|
<p class="message">Administrators can temporarily impersonate another a user.</p>
|
|
<fieldset>
|
|
<label>
|
|
<span>Email</span>
|
|
<input name="email" type="email" class="field" value="{{.ImpersonateEmail}}" placeholder="user@example.com">
|
|
</label>
|
|
<label>
|
|
<span>Group</span>
|
|
<input name="group" type="text" class="field" value="{{.ImpersonateGroup}}" placeholder="engineering">
|
|
</label>
|
|
</fieldset>
|
|
</section>
|
|
<div class="flex">
|
|
{{ .csrfField }}
|
|
<button class="button full" type="submit">Impersonate session</button>
|
|
</div>
|
|
</form>
|
|
{{ end }}
|
|
</div>
|
|
</div>
|
|
<footer>
|
|
<a href="https://www.pomerium.io" style="display: block;">
|
|
<svg class="logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 139 30"><defs><style>.a {fill: #6e43e8;}.a,.b {fill-rule: evenodd;}.b,.c {fill: #fff;}</style></defs><title>powered-by-pomerium</title><path class="a" d="M10.6,5.5H138.4c3.09,0,5.6,2,5.6,4.39V31.11c0,2.42-2.51,4.39-5.6,4.39H10.6c-3.09,0-5.6-2-5.6-4.39V9.89C5,7.47,7.51,5.5,10.6,5.5Z" transform="translate(-5 -5.5)" /><path class="b" d="M75.4,26.62H73.94l1.13-2.79-2.25-5.69h1.54L75.78,22l1.43-3.87h1.54Zm-5.61-2.44a2.42,2.42,0,0,1-1.5-.55V24H66.78V15.56h1.51v3a2.48,2.48,0,0,1,1.5-.55c1.58,0,2.66,1.28,2.66,3.09S71.37,24.18,69.79,24.18Zm-.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.8S70.32,19.3,69.47,19.3Zm-8.8,4.33a2.38,2.38,0,0,1-1.5.55c-1.57,0-2.66-1.27-2.66-3.09S57.6,18,59.17,18a2.44,2.44,0,0,1,1.5.55v-3h1.52V24H60.67Zm0-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,53.47,23a3.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.13A2.87,2.87,0,0,1,53.11,18,2.66,2.66,0,0,1,55.7,21a5.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,53.05,19.17Zm-5.34.93V24H46.2v-5.9h1.51v.59A2,2,0,0,1,49.16,18a1.65,1.65,0,0,1,.49.06v1.35a1.83,1.83,0,0,0-.53-.07A1.87,1.87,0,0,0,47.71,20.1ZM41,21.51A1.69,1.69,0,0,0,42.76,23a3.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.13A2.87,2.87,0,0,1,42.4,18,2.66,2.66,0,0,1,45,21a5.53,5.53,0,0,1,0,.56Zm1.37-2.34A1.38,1.38,0,0,0,41,20.53h2.57A1.28,1.28,0,0,0,42.34,19.17ZM35.7,24l-1.2-4-1.2,4H32l-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,0A2.94,2.94,0,0,1,26.47,24.18Zm0-4.92c-.88,0-1.49.75-1.49,1.83s.61,1.83,1.49,1.83S28,22.18,28,21.09,27.35,19.26,26.47,19.26Zm-6.62,1.87H18.49V24H17V15.93h2.87a2.61,2.61,0,1,1,0,5.2Zm-.22-4H18.49V19.9h1.14a1.38,1.38,0,1,0,0-2.75Z" transform="translate(-5 -5.5)" /><path class="c" d="M132.71,14.9A3.93,3.93,0,0,0,128.78,11H94.59a3.93,3.93,0,0,0-3.93,3.92V31.06h2.71V26.55h0a5.49,5.49,0,1,1,11,0h0v4.51h2V26.55h0a5.49,5.49,0,1,1,11,0h0v4.51h2V26.55h0a5.49,5.49,0,1,1,11,0h0v4.51h2.47ZM93.37,19a5.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" transform="translate(-5 -5.5)" /></svg>
|
|
</a>
|
|
</footer>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
{{end}}`))
|
|
return t
|
|
}
|