git.iko.soy/typeable/oauth2-proxy
on master help sign in
Add option for custom logos on the sign in page
commit23e545a63933130db70cd2e8c96546ae10013caf authorJoel Speed <joel.speed@hotmail.co.uk> dateThu 18 Feb 2021, 19:13 (6 years ago) parentad2d7b1
10 files changed+220−38
CHANGELOG.md+1 docs/docs/configuration/overview.md+1 oauthproxy.go+1 pkg/apis/options/app.go+7 pkg/app/pagewriter/default_logo.svg+1 pkg/app/pagewriter/pagewriter.go+10 pkg/app/pagewriter/sign_in.html+9 pkg/app/pagewriter/sign_in_page.go+57 pkg/app/pagewriter/sign_in_page_test.go+131−38 pkg/app/pagewriter/templates_test.go+2
CHANGELOG.md+1
@@ -8,6 +8,7 @@
88
99## Changes since v7.0.1
1010
11+- [#1056](https://github.com/oauth2-proxy/oauth2-proxy/pull/1056) Add option for custom logos on the sign in page (@JoelSpeed)
1112- [#1054](https://github.com/oauth2-proxy/oauth2-proxy/pull/1054) Update to Go 1.16 (@JoelSpeed)
1213- [#1052](https://github.com/oauth2-proxy/oauth2-proxy/pull/1052) Update golangci-lint to latest version (v1.36.0) (@JoelSpeed)
1314- [#1043](https://github.com/oauth2-proxy/oauth2-proxy/pull/1043) Refactor Sign In Page rendering and capture all page rendering code in pagewriter package (@JoelSpeed)
docs/docs/configuration/overview.md+1
@@ -40,6 +40,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/
4040| `--cookie-secure` | bool | set [secure (HTTPS only) cookie flag](https://owasp.org/www-community/controls/SecureFlag) | true |
4141| `--cookie-samesite` | string | set SameSite cookie attribute (`"lax"`, `"strict"`, `"none"`, or `""`). | `""` |
4242| `--custom-templates-dir` | string | path to custom html templates | |
43+| `--custom-sign-in-logo` | string | path to an custom image for the sign_in page logo. Use \"-\" to disable default logo. |
4344| `--display-htpasswd-form` | bool | display username / password login form if an htpasswd file is provided | true |
4445| `--email-domain` | string \| list | authenticate emails with the specified domain (may be given multiple times). Use `*` to authenticate any email | |
4546| `--errors-to-info-log` | bool | redirects error-level logging to default log channel instead of stderr | |
oauthproxy.go+1
@@ -123,6 +123,7 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
123123
124124 pageWriter, err := pagewriter.NewWriter(pagewriter.Opts{
125125 TemplatesPath: opts.Templates.Path,
126+ CustomLogo: opts.Templates.CustomLogo,
126127 ProxyPrefix: opts.ProxyPrefix,
127128 Footer: opts.Templates.Footer,
128129 Version: VERSION,
pkg/apis/options/app.go+7
@@ -11,6 +11,12 @@ type Templates struct {
1111 // If either file is missing, the default will be used instead.
1212 Path string `flag:"custom-templates-dir" cfg:"custom_templates_dir"`
1313
14+ // CustomLogo is the path to a logo that should replace the default logo
15+ // on the sign_in page template.
16+ // Supported formats are .svg, .png, .jpg and .jpeg.
17+ // To disable the default logo, set this value to "-".
18+ CustomLogo string `flag:"custom-sign-in-logo" cfg:"custom_sign_in_logo"`
19+
1420 // Banner overides the default sign_in page banner text. If unspecified,
1521 // the message will give users a list of allowed email domains.
1622 Banner string `flag:"banner" cfg:"banner"`
@@ -34,6 +40,7 @@ func templatesFlagSet() *pflag.FlagSet {
3440 flagSet := pflag.NewFlagSet("templates", pflag.ExitOnError)
3541
3642 flagSet.String("custom-templates-dir", "", "path to custom html templates")
43+ flagSet.String("custom-sign-in-logo", "", "path to an custom image for the sign_in page logo. Use \"-\" to disable default logo.")
3744 flagSet.String("banner", "", "custom banner string. Use \"-\" to disable default banner.")
3845 flagSet.String("footer", "", "custom footer string. Use \"-\" to disable default footer.")
3946 flagSet.Bool("display-htpasswd-form", true, "display username / password login form if an htpasswd file is provided")
pkg/app/pagewriter/default_logo.svgnew file+1
@@ -0,0 +1 @@
1+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 840.55645 412.45904"><defs><style>.cls-1{fill:#333;}.cls-2{fill:#7c7c82;}.cls-3{fill:#06ed94;}.cls-4{fill:#059b61;}.cls-5{fill:#1957ba;}.cls-6{fill:#6ca8ff;}</style></defs><title>OAuth2_Proxy_logo_v3</title><path class="cls-1" d="M474.20933,196.63116a41.46679,41.46679,0,0,1-16.33506-3.18964,39.80757,39.80757,0,0,1-21.67092-21.83518,42.99409,42.99409,0,0,1,0-32.23,39.80765,39.80765,0,0,1,21.67092-21.83518,43.41665,43.41665,0,0,1,32.67012,0,39.8101,39.8101,0,0,1,21.66989,21.83518,42.994,42.994,0,0,1,0,32.23,39.81,39.81,0,0,1-21.66989,21.83518A41.485,41.485,0,0,1,474.20933,196.63116Zm0-14.07985a26.63714,26.63714,0,0,0,10.28431-1.9799,25.89889,25.89889,0,0,0,8.36072-5.50012,25.04229,25.04229,0,0,0,5.60963-8.525,31.04294,31.04294,0,0,0,0-22.11,25.07963,25.07963,0,0,0-5.60963-8.52549,25.9145,25.9145,0,0,0-8.36072-5.49961,27.6962,27.6962,0,0,0-20.56965,0,25.89233,25.89233,0,0,0-8.36072,5.49961,25.03434,25.03434,0,0,0-5.60962,8.52549,31.04279,31.04279,0,0,0,0,22.11,24.99714,24.99714,0,0,0,5.60962,8.525,25.87675,25.87675,0,0,0,8.36072,5.50012A26.63112,26.63112,0,0,0,474.20933,182.55131Z"/><path class="cls-1" d="M548.12931,116.11083h16.94044l29.59051,78.76048H578.26942L571.77961,176.061H541.41945l-6.48981,18.81033H518.53983Zm18.81032,46.3104-7.03941-19.69-2.8606-9.56994h-.88018l-2.85957,9.56994-7.04044,19.69Z"/><path class="cls-1" d="M636.67873,187.83138h-.88019a18.26469,18.26469,0,0,1-6.82039,6.43506,19.94541,19.94541,0,0,1-9.78947,2.36472q-10.23059,0-15.23483-6.26977-5.00682-6.27052-5.00527-16.7204V140.97087H613.359v30.91027q0,5.832,2.58477,8.635,2.58321,2.80482,7.53425,2.80533a10.50006,10.50006,0,0,0,5.22532-1.265,12.21407,12.21407,0,0,0,3.85029-3.41019,15.44293,15.44293,0,0,0,2.41947-5.06,22.69746,22.69746,0,0,0,.82543-6.215V140.97087H650.209v53.90044H636.67873Z"/><path class="cls-1" d="M669.01723,153.29138h-9.461V140.97087h9.461V124.471h14.4094v16.49984h13.2007v12.32051h-13.2007V174.829a15.54561,15.54561,0,0,0,.38534,3.57135,5.7936,5.7936,0,0,0,1.48454,2.77744,5.44914,5.44914,0,0,0,4.39988,1.81357,8.00547,8.00547,0,0,0,5.06-1.4303l4.07034,12.65007a23.83924,23.83924,0,0,1-5.44434,1.81512,31.8029,31.8029,0,0,1-6.43505.60487,19.66528,19.66528,0,0,1-7.31524-1.29084,15.54151,15.54151,0,0,1-5.44537-3.53675q-5.17107-5.05331-5.16953-14.37272Z"/><path class="cls-1" d="M707.735,116.11083h14.4094v22.1105l-.87915,9.79h.87915a17.85447,17.85447,0,0,1,6.65511-6.325,19.31492,19.31492,0,0,1,9.73574-2.47526q10.44909,0,15.45488,6.16026,5.00371,6.16128,5.00527,16.8299v32.67013H744.585V163.961q0-5.60962-2.64055-8.525a9.44018,9.44018,0,0,0-7.37-2.91484,10.82482,10.82482,0,0,0-5.28007,1.265,11.76092,11.76092,0,0,0-3.904,3.465,16.09429,16.09429,0,0,0-2.4205,5.11478,22.748,22.748,0,0,0-.82544,6.215v26.29034H707.735Z"/><path class="cls-1" d="M769.77353,194.87131V181.78115q6.48979-6.37824,11.98992-11.87991,2.31048-2.3097,4.73048-4.73,2.4174-2.41818,4.50939-4.50991,2.08888-2.08889,3.74078-3.795,1.6488-1.70458,2.53-2.69531,1.75726-2.08811,3.13436-3.73975a18.16413,18.16413,0,0,0,2.20046-3.24542,16.47472,16.47472,0,0,0,1.26449-3.18963,13.02445,13.02445,0,0,0,.44009-3.465,8.4276,8.4276,0,0,0-2.85956-6.325,10.93308,10.93308,0,0,0-7.81009-2.6953q-4.951,0-7.70058,2.80533a14.59773,14.59773,0,0,0-3.73975,6.325l-13.09015-5.3901a24.55547,24.55547,0,0,1,2.8606-6.105,22.76637,22.76637,0,0,1,5.06-5.61014,26.44875,26.44875,0,0,1,7.31421-4.07034,26.86184,26.86184,0,0,1,9.5157-1.59456,29.71878,29.71878,0,0,1,10.3401,1.70458,23.00883,23.00883,0,0,1,7.81008,4.67521,20.92923,20.92923,0,0,1,4.94949,6.98517,21.28551,21.28551,0,0,1,1.76037,8.635,25.00612,25.00612,0,0,1-3.465,12.92486,56.87664,56.87664,0,0,1-8.41546,10.945l-9.45992,9.46043q-3.74077,3.74077-8.47022,8.35968l.44009.66014h30.4707v12.65007Z"/><path class="cls-2" d="M447.03623,265.05824v31.9h-10.12V218.19774h26.84045a24.25664,24.25664,0,0,1,17.38054,6.82039,22.75761,22.75761,0,0,1,0,33.32974,24.22463,24.22463,0,0,1-17.38054,6.71037Zm0-37.18055v27.50059h16.94045a13.378,13.378,0,0,0,10.01054-4.07033,13.80577,13.80577,0,0,0-10.01054-23.43026Z"/><path class="cls-2" d="M507.31645,296.95821h-10.12V243.05777h9.68v8.80029h.44009a14.60805,14.60805,0,0,1,6.32451-7.31523,17.56035,17.56035,0,0,1,9.40516-3.02486,18.97706,18.97706,0,0,1,7.48054,1.31976l-3.08065,9.7905a16.79182,16.79182,0,0,0-5.94021-.77017A13.09674,13.09674,0,0,0,511.551,256.478a15.45278,15.45278,0,0,0-4.23459,10.78019Z"/><path class="cls-2" d="M530.74567,270.00825q0-12.42951,7.81009-20.57018a26.6222,26.6222,0,0,1,19.91055-8.14015q11.98785,0,19.8,8.14015,7.92011,8.14172,7.91959,20.57018,0,12.53952-7.91959,20.57017-7.81164,8.1417-19.8,8.13964a26.616,26.616,0,0,1-19.91055-8.13964Q530.74412,282.439,530.74567,270.00825Zm10.12,0q0,8.691,5.06,14.07984a17.28222,17.28222,0,0,0,25.08008,0q5.05952-5.388,5.06-14.07984,0-8.58025-5.06-13.97034a17.04544,17.04544,0,0,0-25.08008,0Q540.86469,261.429,540.86572,270.00825Z"/><path class="cls-2" d="M619.95626,269.56816l19.25041,27.39005H627.32625l-13.3102-19.91-13.09016,19.91H589.04651l19.03036-27.39005L589.4866,243.05777h11.43929l13.31021,19.03037,12.65006-19.03037h11.88042Z"/><path class="cls-2" d="M694.4248,243.05777l-33.77035,77.66076H650.20484L662.74437,293.548l-22.21949-50.49024h11.00024l16.06026,38.72036h.219l15.62017-38.72036Z"/><path class="cls-3" d="M211.931,392.63536c-101.38027,0-183.85932-82.47852-183.85932-183.8588S110.55071,24.91776,211.931,24.91776,395.7903,107.39629,395.7903,208.77656,313.31125,392.63536,211.931,392.63536Zm0-354.70389c-94.2045,0-170.84457,76.64111-170.84457,170.84509S117.72648,379.62165,211.931,379.62165s170.84457-76.6411,170.84457-170.84509S306.13548,37.93147,211.931,37.93147Z"/><polygon class="cls-4" points="244.602 112.007 239.295 131.843 281.959 166.856 322.055 161.912 244.602 112.007"/><polygon class="cls-3" points="348.449 195.562 348.449 158.734 290.209 109.179 244.602 112.007 300.209 156.67 76.642 156.67 76.642 198.05 348.449 195.562"/><polygon class="cls-5" points="180.489 297.175 185.796 277.34 141.87 240.558 103.037 247.271 180.489 297.175"/><polygon class="cls-6" points="76.642 213.62 76.642 250.448 134.882 300.004 180.489 297.175 124.882 252.512 348.449 252.512 348.449 211.132 76.642 213.62"/></svg>
pkg/app/pagewriter/pagewriter.go+10
@@ -49,6 +49,10 @@ type Opts struct {
4949
5050 // SignInMessage is the messge displayed above the login button.
5151 SignInMessage string
52+
53+ // CustomLogo is the path to a logo to be displayed on the sign in page.
54+ // The logo can be either PNG, JPG/JPEG or SVG.
55+ CustomLogo string
5256}
5357
5458// NewWriter constructs a Writer from the options given to allow
@@ -59,6 +63,11 @@ func NewWriter(opts Opts) (Writer, error) {
5963 return nil, fmt.Errorf("error loading templates: %v", err)
6064 }
6165
66+ logoData, err := loadCustomLogo(opts.CustomLogo)
67+ if err != nil {
68+ return nil, fmt.Errorf("error loading logo: %v", err)
69+ }
70+
6271 errorPage := &errorPageWriter{
6372 template: templates.Lookup("error.html"),
6473 proxyPrefix: opts.ProxyPrefix,
@@ -76,6 +85,7 @@ func NewWriter(opts Opts) (Writer, error) {
7685 footer: opts.Footer,
7786 version: opts.Version,
7887 displayLoginForm: opts.DisplayLoginForm,
88+ logoData: logoData,
7989 }
8090
8191 return &pageWriter{
pkg/app/pagewriter/sign_in.html+9
@@ -15,6 +15,9 @@
1515 max-width: 400px;
1616 margin: 1.25rem auto;
1717 }
18+ .logo-box {
19+ margin: 1.5rem 3rem;
20+ }
1821 footer a {
1922 text-decoration: underline;
2023 }
@@ -40,6 +43,12 @@
4043 <body class="has-background-light">
4144 <section class="section">
4245 <div class="box block sign-in-box has-text-centered">
46+ {{ if .LogoData }}
47+ <div class="block logo-box">
48+ {{.LogoData}}
49+ </div>
50+ {{ end }}
51+
4352 <form method="GET" action="{{.ProxyPrefix}}/start">
4453 <input type="hidden" name="rd" value="{{.Redirect}}">
4554 {{ if .SignInMessage }}
pkg/app/pagewriter/sign_in_page.go+57
@@ -1,12 +1,24 @@
11package pagewriter
22
33import (
4+ // Import embed to allow importing default logo
5+ _ "embed"
6+
7+ "encoding/base64"
8+ "fmt"
9+ "os"
10+ "path/filepath"
11+ "strings"
12+
413 "html/template"
514 "net/http"
615
716 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
817)
918
19+//go:embed default_logo.svg
20+var defaultLogoData string
21+
1022// signInPageWriter is used to render sign-in pages.
1123type signInPageWriter struct {
1224 // Template is the sign-in page HTML template.
@@ -33,6 +45,10 @@ type signInPageWriter struct {
3345
3446 // DisplayLoginForm determines whether or not the basic auth password form is displayed on the sign-in page.
3547 displayLoginForm bool
48+
49+ // LogoData is the logo to render in the template.
50+ // This should contain valid html.
51+ logoData string
3652}
3753
3854// WriteSignInPage writes the sign-in page to the given response writer.
@@ -48,6 +64,7 @@ func (s *signInPageWriter) WriteSignInPage(rw http.ResponseWriter, redirectURL s
4864 Version string
4965 ProxyPrefix string
5066 Footer template.HTML
67+ LogoData template.HTML
5168 }{
5269 ProviderName: s.providerName,
5370 SignInMessage: template.HTML(s.signInMessage),
@@ -56,6 +73,7 @@ func (s *signInPageWriter) WriteSignInPage(rw http.ResponseWriter, redirectURL s
5673 Version: s.version,
5774 ProxyPrefix: s.proxyPrefix,
5875 Footer: template.HTML(s.footer),
76+ LogoData: template.HTML(s.logoData),
5977 }
6078
6179 err := s.template.Execute(rw, t)
@@ -64,3 +82,42 @@ func (s *signInPageWriter) WriteSignInPage(rw http.ResponseWriter, redirectURL s
6482 s.errorPageWriter.WriteErrorPage(rw, http.StatusInternalServerError, redirectURL, err.Error())
6583 }
6684}
85+
86+// loadCustomLogo loads the logo file from the path and encodes it to an HTML
87+// entity. If no custom logo is provided, the OAuth2 Proxy Icon is used instead.
88+func loadCustomLogo(logoPath string) (string, error) {
89+ if logoPath == "" {
90+ // The default logo is an SVG so this will be valid to just return.
91+ return defaultLogoData, nil
92+ }
93+
94+ if logoPath == "-" {
95+ // Return no logo when the custom logo is set to `-`.
96+ // This disables the logo rendering.
97+ return "", nil
98+ }
99+
100+ logoData, err := os.ReadFile(logoPath)
101+ if err != nil {
102+ return "", fmt.Errorf("could not read logo file: %v", err)
103+ }
104+
105+ extension := strings.ToLower(filepath.Ext(logoPath))
106+ switch extension {
107+ case ".svg":
108+ return string(logoData), nil
109+ case ".jpg", ".jpeg":
110+ return encodeImg(logoData, "jpeg"), nil
111+ case ".png":
112+ return encodeImg(logoData, "png"), nil
113+ default:
114+ return "", fmt.Errorf("unknown extension: %q, supported extensions are .svg, .jpg, .jpeg and .png", extension)
115+ }
116+}
117+
118+// encodeImg takes the raw image data and converts it to an HTML Img tag with
119+// a base64 data source.
120+func encodeImg(data []byte, format string) string {
121+ b64Data := base64.StdEncoding.EncodeToString(data)
122+ return fmt.Sprintf("<img src=\"data:image/%s;base64,%s\" alt=\"Logo\" />", format, b64Data)
123+}
pkg/app/pagewriter/sign_in_page_test.go+131−38
@@ -1,61 +1,154 @@
11package pagewriter
22
33import (
4+ "errors"
5+ "fmt"
46 "html/template"
57 "io/ioutil"
68 "net/http/httptest"
9+ "os"
10+ "path/filepath"
11+ "strings"
712
813 . "github.com/onsi/ginkgo"
14+ . "github.com/onsi/ginkgo/extensions/table"
915 . "github.com/onsi/gomega"
1016)
1117
12−var _ = Describe("SignIn Page Writer", func() {
13− var signInPage *signInPageWriter
18+var _ = Describe("SignIn Page", func() {
1419
15− BeforeEach(func() {
16− errorTmpl, err := template.New("").Parse("{{.Title}}")
17− Expect(err).ToNot(HaveOccurred())
18− errorPage := &errorPageWriter{
19− template: errorTmpl,
20− }
21−
22− tmpl, err := template.New("").Parse("{{.ProxyPrefix}} {{.ProviderName}} {{.SignInMessage}} {{.Footer}} {{.Version}} {{.Redirect}} {{.CustomLogin}}")
23− Expect(err).ToNot(HaveOccurred())
24−
25− signInPage = &signInPageWriter{
26− template: tmpl,
27− errorPageWriter: errorPage,
28− proxyPrefix: "/prefix/",
29− providerName: "My Provider",
30− signInMessage: "Sign In Here",
31− footer: "Custom Footer Text",
32− version: "v0.0.0-test",
33− displayLoginForm: true,
34− }
35− })
20+ Context("SignIn Page Writer", func() {
21+ var signInPage *signInPageWriter
3622
37− Context("WriteSignInPage", func() {
38− It("Writes the template to the response writer", func() {
39− recorder := httptest.NewRecorder()
40− signInPage.WriteSignInPage(recorder, "/redirect")
23+ BeforeEach(func() {
24+ errorTmpl, err := template.New("").Parse("{{.Title}}")
25+ Expect(err).ToNot(HaveOccurred())
26+ errorPage := &errorPageWriter{
27+ template: errorTmpl,
28+ }
4129
42− body, err := ioutil.ReadAll(recorder.Result().Body)
30+ tmpl, err := template.New("").Parse("{{.ProxyPrefix}} {{.ProviderName}} {{.SignInMessage}} {{.Footer}} {{.Version}} {{.Redirect}} {{.CustomLogin}} {{.LogoData}}")
4331 Expect(err).ToNot(HaveOccurred())
44− Expect(string(body)).To(Equal("/prefix/ My Provider Sign In Here Custom Footer Text v0.0.0-test /redirect true"))
32+
33+ signInPage = &signInPageWriter{
34+ template: tmpl,
35+ errorPageWriter: errorPage,
36+ proxyPrefix: "/prefix/",
37+ providerName: "My Provider",
38+ signInMessage: "Sign In Here",
39+ footer: "Custom Footer Text",
40+ version: "v0.0.0-test",
41+ displayLoginForm: true,
42+ logoData: "Logo Data",
43+ }
4544 })
4645
47− It("Writes an error if the template can't be rendered", func() {
48− // Overwrite the template with something bad
49− tmpl, err := template.New("").Parse("{{.Unknown}}")
50− Expect(err).ToNot(HaveOccurred())
51− signInPage.template = tmpl
46+ Context("WriteSignInPage", func() {
47+ It("Writes the template to the response writer", func() {
48+ recorder := httptest.NewRecorder()
49+ signInPage.WriteSignInPage(recorder, "/redirect")
50+
51+ body, err := ioutil.ReadAll(recorder.Result().Body)
52+ Expect(err).ToNot(HaveOccurred())
53+ Expect(string(body)).To(Equal("/prefix/ My Provider Sign In Here Custom Footer Text v0.0.0-test /redirect true Logo Data"))
54+ })
55+
56+ It("Writes an error if the template can't be rendered", func() {
57+ // Overwrite the template with something bad
58+ tmpl, err := template.New("").Parse("{{.Unknown}}")
59+ Expect(err).ToNot(HaveOccurred())
60+ signInPage.template = tmpl
5261
53− recorder := httptest.NewRecorder()
54− signInPage.WriteSignInPage(recorder, "/redirect")
62+ recorder := httptest.NewRecorder()
63+ signInPage.WriteSignInPage(recorder, "/redirect")
5564
56− body, err := ioutil.ReadAll(recorder.Result().Body)
65+ body, err := ioutil.ReadAll(recorder.Result().Body)
66+ Expect(err).ToNot(HaveOccurred())
67+ Expect(string(body)).To(Equal("Internal Server Error"))
68+ })
69+ })
70+ })
71+
72+ Context("loadCustomLogo", func() {
73+ var customDir string
74+
75+ const fakeImageData = "Fake Image Data"
76+
77+ BeforeEach(func() {
78+ var err error
79+ customDir, err = ioutil.TempDir("", "oauth2-proxy-sign-in-page-test")
5780 Expect(err).ToNot(HaveOccurred())
58− Expect(string(body)).To(Equal("Internal Server Error"))
81+
82+ for _, ext := range []string{".svg", ".png", ".jpg", ".jpeg", ".gif"} {
83+ fileName := filepath.Join(customDir, fmt.Sprintf("logo%s", ext))
84+ Expect(ioutil.WriteFile(fileName, []byte(fakeImageData), 0600)).To(Succeed())
85+ }
86+ })
87+
88+ AfterEach(func() {
89+ Expect(os.RemoveAll(customDir)).To(Succeed())
5990 })
91+
92+ type loadCustomLogoTableInput struct {
93+ logoPath string
94+ expectedErr error
95+ expectedData string
96+ }
97+
98+ DescribeTable("should load the logo based on configuration", func(in loadCustomLogoTableInput) {
99+ logoPath := in.logoPath
100+ if strings.HasPrefix(logoPath, "customDir/") {
101+ logoPath = filepath.Join(customDir, strings.TrimLeft(logoPath, "customDir/"))
102+ }
103+
104+ data, err := loadCustomLogo(logoPath)
105+ if in.expectedErr != nil {
106+ Expect(err).To(MatchError(in.expectedErr.Error()))
107+ } else {
108+ Expect(err).ToNot(HaveOccurred())
109+ }
110+ Expect(data).To(Equal(in.expectedData))
111+ },
112+ Entry("with no custom logo path", loadCustomLogoTableInput{
113+ logoPath: "",
114+ expectedErr: nil,
115+ expectedData: defaultLogoData,
116+ }),
117+ Entry("when disabling the logo display", loadCustomLogoTableInput{
118+ logoPath: "-",
119+ expectedErr: nil,
120+ expectedData: "",
121+ }),
122+ Entry("with an svg custom logo", loadCustomLogoTableInput{
123+ logoPath: "customDir/logo.svg",
124+ expectedErr: nil,
125+ expectedData: fakeImageData,
126+ }),
127+ Entry("with a png custom logo", loadCustomLogoTableInput{
128+ logoPath: "customDir/logo.png",
129+ expectedErr: nil,
130+ expectedData: "<img src=\"data:image/png;base64,RmFrZSBJbWFnZSBEYXRh\" alt=\"Logo\" />",
131+ }),
132+ Entry("with a jpg custom logo", loadCustomLogoTableInput{
133+ logoPath: "customDir/logo.jpg",
134+ expectedErr: nil,
135+ expectedData: "<img src=\"data:image/jpeg;base64,RmFrZSBJbWFnZSBEYXRh\" alt=\"Logo\" />",
136+ }),
137+ Entry("with a jpeg custom logo", loadCustomLogoTableInput{
138+ logoPath: "customDir/logo.jpeg",
139+ expectedErr: nil,
140+ expectedData: "<img src=\"data:image/jpeg;base64,RmFrZSBJbWFnZSBEYXRh\" alt=\"Logo\" />",
141+ }),
142+ Entry("with a gif custom logo", loadCustomLogoTableInput{
143+ logoPath: "customDir/logo.gif",
144+ expectedErr: errors.New("unknown extension: \".gif\", supported extensions are .svg, .jpg, .jpeg and .png"),
145+ expectedData: "",
146+ }),
147+ Entry("when the logo does not exist", loadCustomLogoTableInput{
148+ logoPath: "unknown.svg",
149+ expectedErr: errors.New("could not read logo file: open unknown.svg: no such file or directory"),
150+ expectedData: "",
151+ }),
152+ )
60153 })
61154})
pkg/app/pagewriter/templates_test.go+2
@@ -45,6 +45,7 @@ var _ = Describe("Templates", func() {
4545 SignInMessage string
4646 ProviderName string
4747 CustomLogin bool
48+ LogoData string
4849
4950 // For default error template
5051 StatusCode int
@@ -61,6 +62,7 @@ var _ = Describe("Templates", func() {
6162 SignInMessage: "<sign-in-message>",
6263 ProviderName: "<provider-name>",
6364 CustomLogin: false,
65+ LogoData: "<logo>",
6466
6567 StatusCode: 404,
6668 Title: "<title>",