Giter Site home page Giter Site logo

pgstore's Introduction

pgstore

A session store backend for gorilla/sessions - src.

Installation

make get-deps

Documentation

Available on godoc.org.

See http://www.gorillatoolkit.org/pkg/sessions for full documentation on underlying interface.

Example

package examples

import (
	"log"
	"net/http"
	"time"

	"github.com/antonlindstrom/pgstore"
)

// ExampleHandler is an example that displays the usage of PGStore.
func ExampleHandler(w http.ResponseWriter, r *http.Request) {
	// Fetch new store.
	store, err := pgstore.NewPGStore("postgres://user:[email protected]:5432/database?sslmode=verify-full", []byte("secret-key"))
	if err != nil {
		log.Fatalf(err.Error())
	}
	defer store.Close()

	// Run a background goroutine to clean up expired sessions from the database.
	defer store.StopCleanup(store.Cleanup(time.Minute * 5))

	// Get a session.
	session, err := store.Get(r, "session-key")
	if err != nil {
		log.Fatalf(err.Error())
	}

	// Add a value.
	session.Values["foo"] = "bar"

	// Save.
	if err = session.Save(r, w); err != nil {
		log.Fatalf("Error saving session: %v", err)
	}

	// Delete session.
	session.Options.MaxAge = -1
	if err = session.Save(r, w); err != nil {
		log.Fatalf("Error saving session: %v", err)
	}
}

Breaking changes

  • 2016-07-19 - NewPGStore and NewPGStoreFromPool now returns (*PGStore, error)

Thanks

I've stolen, borrowed and gotten inspiration from the other backends available:

Thank you all for sharing your code!

What makes this backend different is that it's for PostgreSQL.

We've recently refactored this backend to use the standard database/sql driver instead of Gorp. This removes a dependency and makes this package very lightweight and makes database interactions very transparent. Lastly, from the standpoint of unit testing where you want to mock the database layer instead of requiring a real database, you can now easily use a package like go-SQLMock to do just that.

pgstore's People

Contributors

antonlindstrom avatar bbigras avatar caarlos0 avatar dhduvall avatar elithrar avatar hlawrenz avatar imjoshholloway avatar jcbwlkr avatar kmulvey avatar marinbek avatar mfzl avatar wchrisjohnson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

pgstore's Issues

index for performance?

Hi, I was wondering if you've had real-world experience with this package under load? In reviewing the code, I see that there are queries against expires_on and key, but neither have an index. Doesn't that make these queries sequential scans, and therefore pretty expensive?

Maybe there is something I'm not understanding?

don't error when failing to create http_sessions due to permissions

CREATE TABLE IF NOT EXISTS fails with an insufficient_privilege exception when the user executing the statement doesn't have the privileges to create the table in the current schema, even if the table exists. I don't know whether this is a bug in Postgres (9.6), even if it seems like it should succeed.

I've rewritten the statement that creates http_sessions to work around that:

stmt := `DO $$
    BEGIN
    CREATE TABLE IF NOT EXISTS http_sessions (
    id BIGSERIAL PRIMARY KEY,
    key BYTEA,
    data BYTEA,
    created_on TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    modified_on TIMESTAMPTZ,
    expires_on TIMESTAMPTZ);
    EXCEPTION WHEN insufficient_privilege THEN
      IF NOT EXISTS (SELECT FROM pg_catalog.pg_tables 
          WHERE schemaname = current_schema() AND tablename = 'http_sessions') THEN
        RAISE;
      END IF;
    WHEN others THEN RAISE;
    END;
    $$;`

which catches that exception and ignores it only if the table already exists. A bit more poking suggests that to_regclass(current_schema() || '.http_sessions') IS NULL would also work to check existence, though I've no idea which one would be preferred.

some concerns

Hi, first of all, great lib, thanks for the hard work!

I just have a few points:

  • the lib creates a connection pool with the database, assuming the app is already using a postgres database, that will leave us with 2 database pools running. That is a resource waste, since each connection to the DB uses some amount of memory and the session store and the "app store" are not sharing the pool, so, we might have more connections opened than needed
  • I see that you are using gorp to create the table and sequences... I think maybe it would be best to document that - or remove it entirely and provide a sql script instead.

I already did a PR fixing some minor stuff, I can try to work on this if you find it interesting...

Cheers!

Implicit initialization of lib/pq messes with other imports

Hi there, thanks for the handy library!

Since I was already using github.com/lib/pq in my app, when I added a dependency on github.com/antonlindstrom/pgstore I got the following runtime error:

panic: sql: Register called twice for driver postgres

goroutine 1 [running]:
panic(0x420ec0, 0xc820208e30)
    /usr/local/Cellar/go/1.6/libexec/src/runtime/panic.go:464 +0x3e6
database/sql.Register(0x5a78c0, 0x8, 0x9b2b58, 0x84dd30)
    /usr/local/Cellar/go/1.6/libexec/src/database/sql/sql.go:45 +0x181
github.com/bolt-data/boltapp/vendor/github.com/lib/pq.init.1()
    /Users/anaulin/src/github.com/bolt-data/boltapp/vendor/github.com/lib/pq/conn.go:44 +0x71
github.com/bolt-data/boltapp/vendor/github.com/lib/pq.init()
    /Users/anaulin/src/github.com/bolt-data/boltapp/vendor/github.com/lib/pq/user_posix.go:24 +0x4b0
github.com/bolt-data/boltapp/db.init()
    /Users/anaulin/src/github.com/bolt-data/boltapp/db/connection.go:27 +0x59
github.com/bolt-data/boltapp/handlers.init()
    /Users/anaulin/src/github.com/bolt-data/boltapp/handlers/template_util.go:28 +0x5b
main.init()

I can work around this issue by removing my own import of github.com/lib/pq but it feels a little messy, for a couple of reasons:

  1. It is no longer explicit in my code which Postgres driver is being used and where
  2. Some other dependency that also registers lib/pq will mess with things

Store is not creating

Hello I am trying to use this lib but there is a problem when it tries to create the http_sessions table it says the following error:
"Unable to create http_sessions table in the database".

I tried to find on google a possible solution that for some reason any of the people who had used this lib in the past and had the same issue, but unfortunately I could not find any solution at all.

My SQLUrl is the following:
"postgres://IGToolUserDB:[email protected]:x/IGToolDB?sslmode=verify-full"

Data race issues are often found in `pgstore.(*PGStore).MaxAge()`

Data race issues are often found in pgstore.(*PGStore).MaxAge(), in our application when run with the -race flag.

==================
WARNING: DATA RACE
Write at 0x00c000bb0950 by goroutine 64:
  github.com/gorilla/securecookie.(*SecureCookie).MaxAge()
      /Users/kotaro_abe/pkg/mod/github.com/gorilla/[email protected]/securecookie.go:203 +0x51c
  github.com/antonlindstrom/pgstore.(*PGStore).MaxAge()
      /Users/kotaro_abe/pkg/mod/github.com/antonlindstrom/[email protected]/pgstore.go:164 +0x52c
  github.com/antonlindstrom/pgstore.(*PGStore).New()
      /Users/kotaro_abe/pkg/mod/github.com/antonlindstrom/[email protected]/pgstore.go:105 +0x428
  github.com/gorilla/sessions.(*Registry).Get()
      /Users/kotaro_abe/pkg/mod/github.com/gorilla/[email protected]/sessions.go:139 +0xf8
  github.com/antonlindstrom/pgstore.(*PGStore).Get()
      /Users/kotaro_abe/pkg/mod/github.com/antonlindstrom/[email protected]/pgstore.go:78 +0x4c
  main.NewEcho.Sessions.func5.1()
...

Previous read at 0x00c000bb0950 by goroutine 130:
  github.com/gorilla/securecookie.(*SecureCookie).Decode()
      /Users/kotaro_abe/pkg/mod/github.com/gorilla/[email protected]/securecookie.go:339 +0x32c
  github.com/gorilla/securecookie.DecodeMulti()
      /Users/kotaro_abe/pkg/mod/github.com/gorilla/[email protected]/securecookie.go:594 +0x11c
  github.com/antonlindstrom/pgstore.(*PGStore).New()
      /Users/kotaro_abe/pkg/mod/github.com/antonlindstrom/[email protected]/pgstore.go:94 +0x374
  github.com/gorilla/sessions.(*Registry).Get()
      /Users/kotaro_abe/pkg/mod/github.com/gorilla/[email protected]/sessions.go:139 +0xf8
  github.com/antonlindstrom/pgstore.(*PGStore).Get()
      /Users/kotaro_abe/pkg/mod/github.com/antonlindstrom/[email protected]/pgstore.go:78 +0x4c
  main.NewEcho.Sessions.func5.1()
...

Goroutine 64 (running) created at:
  net/http.(*Server).Serve()
      /usr/local/go/src/net/http/server.go:3285 +0x674
  github.com/labstack/echo/v4.(*Echo).Start()
      /Users/kotaro_abe/pkg/mod/github.com/labstack/echo/[email protected]/echo.go:686 +0x11c
...

Goroutine 130 (running) created at:
  net/http.(*Server).Serve()
      /usr/local/go/src/net/http/server.go:3285 +0x674
  github.com/labstack/echo/v4.(*Echo).Start()
      /Users/kotaro_abe/pkg/mod/github.com/labstack/echo/[email protected]/echo.go:686 +0x11c
...

Fix the errors returned by the "createSessionsTable" function

There was already a problem number #35 , where there was a problem of creating a table in the database.
The problem may be that the function does not return driver data, but changes it to its own insufficiently informative.
For example, the http_session table is not created for me, but why it was not created, I found out only in debug mode, reaching the output of the driver error:

func (db *PGStore) createSessionsTable() error {
...
_, err := db.DbPool.Exec(stmt)
if err!=nil{
// My error was: "pq: SSL is not enabled on the server"
// but I won't know about it because "Unable to create http_sessions table in the database"
return errors.Wrapf(err, "Unable to create http_sessions table in the database")
}
}

Мaybe it is worth concatenating errors or outputting driver errors?

Use of gorp makes testing really difficult

Great package, thanks very much for your efforts!

We have an app that is using this pkg, and have found writing tests to be really difficult due to the dependence on gorp. Why was this approach chosen vs just interacting with Postgres directly? How do users of this pkg in their apps do unit testing?

Session Cleanup

From what I can determine, old sessions that have expired but have not been explicitly deleted will remain in the database.

Although this is not a huge problem unto itself, it's a little messy. The boltstore package has a reaper function that operates via a goroutine and a time.Ticker to check the database for old sessions -

e.g.

DELETE FROM http_sessions WHERE expires_on > current_time

I figured I'd raise this issue before submitting a PR (super busy right now!) in the event you can get to a patch before I can.

Set session cookie as HttpOnly by default to protect against cookie theft

I noticed that the session cookies written in the SetCookie headers are not set by default. If this field is not set, the end users of this library are vulnerable to cookie theft by potential XSS attacks. OWASP recommends HttpOnly to be set for session cookies, which is why in my opinion it would be good practice to set the cookie by default in the library code.

Of course, you can set the flag manually by calling session.Options.HttpOnly = true before session.Save(r, w), but I doubt most users remember or understand that the field should be set by them (I only discovered this after doing some digging).

If you think this sounds like something the library should do, I don't mind sending out a PR for it, should be a one-line fix.

PS: Thanks for all the work you've done on the library :)

Is it normal to call pgstore.NewPGStore for each handlers ?

Hi, in your example you are calling pgstore.NewPGStore inside your handler so:

  • Am I supposed to do the same for each handlers which need sessions? So putting this code in each handlers:
	store, err := pgstore.NewPGStore("...", []byte("secret-key"))
	if err != nil {
		log.Fatalf(err.Error())
	}
	defer store.Close()
	defer store.StopCleanup(store.Cleanup(time.Minute * 5))
  • If it's not the case I am wondering how are you achieving concurrency if a unique store is accessible by all handlers? I mean how you are ensuring there are no race conditions.

Thank you.

Create an initial release

Hi!
Great package, thank you for writing this!

Please create an initial release something like 1.0.0.
This way we can manage dependencies via godeps or glide.

Thank you!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.