github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/examples/sample-application/secure/auth/auth.go (about) 1 // Copyright 2020 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build go1.16 16 // +build go1.16 17 18 package auth 19 20 import ( 21 "github.com/google/go-safeweb/safehttp" 22 "github.com/google/safehtml/template" 23 24 "github.com/google/go-safeweb/examples/sample-application/secure/responses" 25 "github.com/google/go-safeweb/examples/sample-application/storage" 26 ) 27 28 const sessionCookie = "SESSION" 29 30 var unauthMsg = template.MustParseAndExecuteToHTML(`Please <a href="/">login</a> before visiting this page.`) 31 32 // Interceptor is an auth (access control) interceptor. 33 // 34 // It showcases how safehttp.Interceptor could be implement to provide custom 35 // security features. See https://pkg.go.dev/github.com/google/go-safeweb/safehttp#hdr-Interceptors. 36 // 37 // In order to interact with the interceptor, use functions from this package. 38 // E.g. to clear a user session call ClearSession. 39 type Interceptor struct { 40 DB *storage.DB 41 } 42 43 // Before runs before the request is passed to the handler. 44 // 45 // Implementation details: this interceptor uses IncomingRequest's context to 46 // store user information that's read from a cookie. 47 func (ip Interceptor) Before(w safehttp.ResponseWriter, r *safehttp.IncomingRequest, cfg safehttp.InterceptorConfig) safehttp.Result { 48 // Identify the user. 49 user := ip.userFromCookie(r) 50 if user != "" { 51 putUser(r.Context(), user) 52 } 53 54 if _, ok := cfg.(Skip); ok { 55 // If the config says we should not perform auth, let's stop executing here. 56 return safehttp.NotWritten() 57 } 58 59 if user == "" { 60 // We have to perform auth, and the user was not identified, bail out. 61 return w.WriteError(responses.Error{ 62 StatusCode: safehttp.StatusUnauthorized, 63 Message: unauthMsg, 64 }) 65 } 66 return safehttp.NotWritten() 67 } 68 69 // Commit runs after the handler commited to a response. 70 // 71 // Implementation details: the interceptor reads IncomingRequest's context to 72 // retrieve information about the user and to do what the handler asked it to 73 // (through ClearSession or CreateSession). 74 func (ip Interceptor) Commit(w safehttp.ResponseHeadersWriter, r *safehttp.IncomingRequest, resp safehttp.Response, cfg safehttp.InterceptorConfig) { 75 user := User(r) 76 77 switch ctxSessionAction(r.Context()) { 78 case clearSess: 79 ip.DB.DelSession(user) 80 w.AddCookie(safehttp.NewCookie(sessionCookie, "")) 81 case setSess: 82 token := ip.DB.GetToken(user) 83 w.AddCookie(safehttp.NewCookie(sessionCookie, token)) 84 default: 85 // do nothing 86 } 87 } 88 89 func (Interceptor) Match(cfg safehttp.InterceptorConfig) bool { 90 _, ok := cfg.(Skip) 91 return ok 92 } 93 94 // User retrieves the user. 95 func User(r *safehttp.IncomingRequest) string { 96 return ctxUser(r.Context()) 97 } 98 99 func (ip Interceptor) userFromCookie(r *safehttp.IncomingRequest) string { 100 sess, err := r.Cookie(sessionCookie) 101 if err != nil || sess.Value() == "" { 102 return "" 103 } 104 user, ok := ip.DB.GetUser(sess.Value()) 105 if !ok { 106 return "" 107 } 108 return user 109 } 110 111 // ClearSession clears the session. 112 // 113 // Implementation details: to interact with the interceptor, passes data through 114 // the IncomingRequest's context. 115 func ClearSession(r *safehttp.IncomingRequest) { 116 putSessionAction(r.Context(), clearSess) 117 } 118 119 // CreateSession creates a session. 120 // 121 // Implementation details: to interact with the interceptor, passes data through 122 // the IncomingRequest's context. 123 func CreateSession(r *safehttp.IncomingRequest, user string) { 124 putSessionAction(r.Context(), setSess) 125 putUser(r.Context(), user) 126 } 127 128 // Skip allows to mark an endpoint to skip auth checks. 129 // 130 // Its uses would normally be gated by a security review. You can use the 131 // https://github.com/google/go-safeweb/blob/master/cmd/bancheck tool to enforce 132 // this. 133 type Skip struct{}