http.Handler is not called in golang - go

I have the following middleware, first sets currentUser in gorilla/context to current user acquired from database, and the second checks if currentUser exists and redirects otherwise:
package main
import (
"database/sql"
"github.com/gorilla/context"
"log"
"net/http"
"server/helpers"
)
func withCurrentUser(db *sql.DB, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
userId := helpers.GetCurrentUserId(db, r)
if userId == nil {
next.ServeHTTP(w, r)
return
}
row := db.QueryRow("SELECT username FROM User WHERE id=?", userId)
var username string
switch err := row.Scan(&username); err {
case sql.ErrNoRows:
next.ServeHTTP(w, r)
return
case nil:
user := helpers.User{UserId: *userId, LoggedIn: true, Username: username}
context.Set(r, "currentUser", user)
default:
log.Fatal(err)
}
next.ServeHTTP(w, r)
})
}
func loginRequired(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user, ok := context.Get(r, "currentUser").(helpers.User)
log.Println(user, ok)
if !ok {
http.Redirect(w, r, "/login", 301)
return
}
next.ServeHTTP(w, r)
})
}
Then when I register a route that requires authenticated user, I do the following:
router.Handle("/create_post",
withCurrentUser(db, loginRequired(http.HandlerFunc(createPostGet))),
).Methods(http.MethodGet)
Where createPostGet:
func createPostGet(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.ParseFiles("templates/createPost.html"))
user := context.Get(r, "currentUser").(helpers.User)
_ = tmpl.Execute(w, helpers.FormStruct{CurrentUser: user})
}
My problem is: even if user is authenticated and context is populated correctly, this route always redirects to login. I tried setting breakpoints inside loginRequired and adding log.Println (as you can see in code), and that function doesn't seem to be even called (no breakpoints stop, no log output, too).
What's happening and how to make sure loginRequired is called every time and checks context properly?
UPD: It doesn't seem to persist, I recompiled the app a few times and now it's working. Anyway, what might be the reason for such behavior (I'm positive I saved everything the first time).
UPD 2: I found out that the problem is connected to browser caching, but I still have no idea why it happens. When I disable browser caching, everything works and function is called every time, but while browser cache is enabled, the function isn't called at all. Any ideas?

OMG, that was stupid. I was using 301 redirect code in loginRequired, so redirect was permanent and browser didn't even make requests.

Related

Redirect to home page after saml authentification (login)

I'm trying to integrate saml using crewjam library with an open-source app in go.
After authentication test using samltest.id, I want to be redirected to the home page.
I have tried several ways, but nothing works well, i'm using gorilla/mux router:
func login(w http.ResponseWriter, r *http.Request) {
s := samlsp.SessionFromContext(r.Context())
if s == nil {
return
}
sa, ok := s.(samlsp.SessionWithAttributes)
if !ok {
return
}
fmt.Fprintf(w, "Token contents, %+v!", sa.GetAttributes())
w.Header().Add("Location", "http://localhost:8080/")
w.WriteHeader(http.StatusFound)
}
I have also tested :
http.Redirect(w, r, "http://localhost:8080/", http.StatusFound)
Can someone help me please?
Thanks :)
Calling w.Write or writing into it using Fmt.Fprintf requires HTTP status code to be set before, otherwise it sets default StatusOK
Server.go
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
Setting the status code multiple times throws superfluous log.
Therefore, Your code is setting the HTTP status code to 200 (http.StatusOk), so the redirect after that is simply impossible.
Solution:
func login(w http.ResponseWriter, r *http.Request) {
s := samlsp.SessionFromContext(r.Context())
if s == nil {
return
}
sa, ok := s.(samlsp.SessionWithAttributes)
if !ok {
return
}
// this line is removed
// fmt.Fprintf(w, "Token contents, %+v!", sa.GetAttributes())
w.Header().Add("Location", "http://localhost:8080/")
w.WriteHeader(http.StatusFound)
// Or Simply
// http.Redirect(w, r, "http://localhost:8080/", http.StatusFound)
}
Try to send your headers before writing content.
And optionally use a relative Location
w.Header().Add("Location", "/")
w.WriteHeader(http.StatusFound)
fmt.Fprintf(w, "Token contents, %+v!", sa.GetAttributes())

How to avoid globals in Go with julien schmidt's router?

I am developing a RESTful API with Go but owing to app configuration, authentication etc I have quite a few global variables.
I am using Julien Schmidt's httprouter because of popular recommendation and am looking for a feasible way to avoid global variables.
Here is some of the code.
I am using a middleware for authenticating a user using gorrila/securecookie.
func AuthMiddleware(handler httprouter.Handle, isLoggedIn bool) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {if isLoggedIn {
currUser, err := GetCurrentUser(r)
if currUser.Username != "" {
handler(w, r, ps)
return
}
responseWriter(w, false, "User not logged in", nil)
return
}
handler(w, r, ps)
}
}
After this, I want to be able to use the currUser object inside the handler that the request is forwarded to such as this one instead of calling GetCurrentUser once again
func foobar(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
var currQuestion CurrQuestions
err = Db.db.Raw("SELECT * FROM users WHERE U.user_id = ?", currUser.UserID).Find(&currQuestion).Error
if err != nil {
responseWriter(w, false, "Internal Server Error", "")
return
}
responseWriter(w, true, "", GameData{})
}
You can use the context package. Values stored in context are only accessible in the current controller. In your middleware (at the end) do the following:
ctx := context.WithValue(r.Context(), "User", user)
r = r.WithContext(ctx)
handler(w, r, ps)
Now you can fetch this value in you handler:
user, ok := r.Context().Value("User").(<type of your user struct>)
The string "User" is the key that is used to save the value. You can change it any way you like, but a const is a good idea.
if ok is true, the user has been fetched and is the correct type. If ok is false, the user was not set or it has the wrong type.

Way to pass data up to parent middleware?

I've got a pretty solid grip on how to pass data from a handler to the handler it wraps, but is there a go idiomatic way to get something back from the wrapped handler? Here's a motivating example: I have an accessLogHandler and an authHandler. accessLogHandler logs every http request, with timings and other request info such as the currently logged in user's ID (if there is one). authHandler is for routes that need a logged in user, it 403's when a user isn't logged in. I want to wrap some (but perhaps not all) of my routes with the authHandler, and wrap all of my routes with the accessLogHandler. If a user is logged in, I would like my accessLogHandler to log the user info along with the access log.
Now, I have a solution I've come up with that I don't like. I'll add the code and then explain some of my issues with it.
// Log the timings of each request optionally including user data
// if there is a logged in user
func accessLogHandler(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
accessLog := newAccessLog()
ctx := context.WithValue(r.Context(), accessLogKey, accessLog)
fn.ServeHTTP(w, r.WithContext(ctx))
// Logs the http access, ommit user info if not set
accessLog.Log()
}
}
// pull some junk off the request/cookies/whatever and check if somebody is logged in
func authHandler(fn http.HandlerFunc) http.HandlerFunc {
return func (w http.ResponseWriter, r *http.Request) {
//Do some authorization
user, err := auth(r)
if err != nil{
//No userId, don't set anything on the accesslogger
w.WriteHeader(http.StatusForbiddend)
return
}
//Success a user is logged in, let's make sure the access logger knows
acessLog := r.Context().Value(accessLogKey).(*AccessLog)
accessLog.Set("userID", user.ID)
fn.ServeHTTP(w, r)
}
}
Basically, what I'm doing here is attaching an accessLog struct to my context inside the accessLogHandler and inside the authHandler I'm reading accessLog from the context and calling accessLog.Set to inform the logger that a userID is present.
Some things I don't like about this approach:
context is immutable, but I'm sticking a mutable struct on it and mutating said struct elsewhere downstream. Feels like a hack.
My authHandler now has a package level dependency on the accessLog package, since I'm type asserting to *AccessLog.
Ideally my authHandler would have some way of informing any part of the request stack about user data without tightly coupling itself to said parts.
Context itself is an interface, so you could create a new logger context in the logger middleware that has the methods you would need to get the behavior you are after.
Something like this:
type Logger struct{}
func (l *Logger) SetLogField(key string, value interface{}) {// set log field }
func (l *Logger) Log(){// log request}
type LoggerCtx struct {
context.Context
*Logger
}
func newAccessLog() *Logger {
return &Logger{}
}
func accessLogHandler(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// create new logger context
ctx := &LoggerCtx{}
ctx.Context = r.Context()
ctx.Logger = newAccessLog()
fn.ServeHTTP(w, r.WithContext(ctx))
// Logs the http access, ommit user info if not set
ctx.Log()
}
}
// pull some junk off the request/cookies/whatever and check if somebody is logged in
func authHandler(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
//Do some authorization
user, err := auth(r)
if err != nil {
//No userId, don't set anything on the accesslogger
w.WriteHeader(http.StatusForbiddend)
return
}
//Success a user is logged in, let's make sure the access logger knows
ctx := r.Context()
// this could be moved - here for clarity
type setLog interface {
SetLogField(string, interface{})
}
if lctx, ok := ctx.(setLog); ok {
lctx.SetLogField("userID", user.ID)
}
fn.ServeHTTP(w, r.WithContext(ctx))
}
}

go - Handling AJAX POST request

I setup a middleware and router to handle an AJAX request on path /api/on like this
http.HandleFunc("/",route.IndexHandler)
http.HandleFunc("/login",route.GoogleLoginHandler)
http.HandleFunc("/auth/google",route.GoogleCallbackHandler)
http.Handle("/dashboard",negroni.New(
negroni.HandlerFunc(route.IsAuthenticated),
negroni.Wrap(http.HandlerFunc(route.DashboardHandler)),
))
http.Handle("/api/on",negroni.New(
negroni.HandlerFunc(route.IsAuthenticated),
negroni.Wrap(http.HandlerFunc(route.TurnOn)),
))
Middleware IsAuthenticated allows to jump to the next router if there is a user session in my web, otherwise it will redirect
func IsAuthenticated(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
session, _ := util.GlobalSessions.SessionStart(w, r)
defer session.SessionRelease(w)
if session.Get("profile") == nil {
http.Redirect(w, r, "/", http.StatusMovedPermanently)
} else {
next(w, r)
}
}
And this is TurnOn handler for later process
func TurnOn(w http.ResponseWriter, r *http.Request) {
session, _ := util.GlobalSessions.SessionStart(w, r)
defer session.SessionRelease(w)
w.Header().Set("Access-Control-Allow-Origin","*")
w.Header().Set("Access-Control-Allow-Methods","POST")
w.Header().Set("Access-Control-Allow-Headers","Content-Type")
fmt.Println(r.URL)
}
After I turned on server, I conducted a POST request with JavaScript's client-side code using AJAX, but the console logged nothing. It seems that the TurnOn function was not reached even when AJAX request was sent successfully. Did I miss something obviously?

Golang: redirect twice and cause http: multiple response.WriteHeader calls

I have a very weird output ... let me post my code first then I will explain:
Under main function I declared
manageMux.HandleFunc("/info", info)
first I log in and redirect from "/login" to page "/":
func login(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
t, err := template.ParseFiles("manage/login.html")
checkError(err)
t.Execute(w, nil)
} else { //POST
r.ParseForm()
//do some authentications here
http.Redirect(w, r, "/", http.StatusFound)
}
}
Then I redirect to another page "/info" from current page "/" (which has only buttons):
func manage(w http.ResponseWriter, r *http.Request) {
t, err := template.ParseFiles("manage/manage.html")
checkError(err)
t.Execute(w, nil)
r.ParseForm()
if r.Form["Button"] != nil { //to get only POST actions from buttons
if r.Form["Button"][0] == "Log" {
http.Redirect(w, r, "/info", http.StatusFound)
}
}
}
At last, I made a template and would like to show on client side:
const tpl=`stuff inside`
type InfoDefault struct {
//stuff inside
}
func info(w http.ResponseWriter, r *http.Request) {
info := InfoDefault{
//stuff inside
}
t, err := template.New("info").Parse(tpl)
checkError(err)
err = t.Execute(os.Stdout, info)
checkError(err)
}
Now, the weird thing is, when I click the button on page "/", I got the error "http: multiple response.WriteHeader calls". At the same time a link called "found" shows up on the bottom of my page (weird!), and when I click the link "found", I got all my parsed template printed on the server side instead of webpage.
Does anyone know why...? And how to fix the error and print stuff on client webpage? Thank you!!!
As JimB already pointed out: your server gets confused because there are different status codes associated with both writing to http.ResponseWriter and the redirect. You can't do both at the same time.
I would actually like to expand more on how you can carry data over to the next page (assuming you are redirecting).
Headers
You can write some information to the request object and receive it on the destination page. Example:
func myHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("My-Awesome-Header", "Rocks")
...
}
Session:
You are talking about access control as far as I can see, and I think persisting user data is better done through a session. Example: you can use a database or a session handler like https://github.com/gorilla/sessions. Check out this thread: Best practice with sessions (gorilla/sessions).
Cookies:
I'm not sure what kind of front-end you are using, but storing non-sensitive data on the cookie could be an option? Nothing beats this one (it has real choc-chip cookies in the examples ;-) ): https://astaxie.gitbooks.io/build-web-application-with-golang/content/en/06.1.html.
In your manage handler, you're executing the template which will write to the http.ResponseWriter and trigger an http.StatusOK (200) status code. You can't redirect after that, since that requires sending a different response code.
If you need to redirect, do it before executing the template.

Resources