Skip to content

Commit

Permalink
Refactor the lib and break to v3 (#35)
Browse files Browse the repository at this point in the history
* Refactor the session `AutoCreate` behaviour. This is a breaking change.
* feat: refactor tests
* fix: update code comments
* refactor: remove Commit() pattern and introduce setMulti to store interface
- remove Commit() pattern, Set and SetMulti should immediately set the
  values to backend.
- rename LoadValues() to CacheAll() and update cache on Set, SetMulti,
  Delete and Clear calls.
* fix: update goredis package to implement latest store interface
* feat: v3 refactor
- Update store interface to take session ID instead of returning on
  Create()
- Introduce hooks to generate and validate session ID in manager. By
  default use alpha-numeric 32 length ID.
- Rename get and set cookie register method to `SetCookieHooks`.
- Ditch ErrFieldNotFound error and get/set supposed to return Nil
  instead.
* refactor: conv package to v3 spec
* feat: migrate memory store to v3 spec
* refactor: redis package to v3 spec
* refactor: securecookie package to v3 spec
* refactor: postgres package to v3 spec
* fix: Clear() should only clear the values and not session
* feat: add Destroy() which deletes the dession from backend
* chore: update README and lib comments
* fix: incorrect store Delete() method implementation
* fix: update store version to v3
* fix: update examples to v3
* fix: refactor examples module structure

---------

Co-authored-by: Kailash Nadh <kailash@nadh.in>
  • Loading branch information
vividvilla and knadh committed Jun 1, 2024
1 parent 205c1e0 commit a3b0aa7
Show file tree
Hide file tree
Showing 41 changed files with 2,808 additions and 3,963 deletions.
107 changes: 76 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,41 @@ Most session libraries are highly opinionated and hard-wired to work with `net/h
## Features
1. Framework/network library agnostic.
2. Simple API and with support for primitive data types. Complex types can be stored using own encoding/decoding.
3. Pre-built redis/postgres/in-memory stores that can be separately installed.
3. Pre-built redis/postgres/in-memory/securecookie stores that can be separately installed.
4. Multiple session instances with custom handlers and different backend stores.

## Installation
Install `simplesessions` and all [available stores](/stores).

```shell
go get -u github.com/vividvilla/simplesessions
go get -u github.com/vividvilla/simplesessions/v3

# Install the requrired store: memory|goredis|redis|postgres
go get -u github.com/vividvilla/simplesessions/stores/goredis
# Install the requrired store: memory|redis|postgres|securecookie
go get -u github.com/vividvilla/simplesessions/v3/stores/redis
go get -u github.com/vividvilla/simplesessions/v3/stores/postgres
```

# Stores
Sessions can be stored to any backend by implementing the [store](/store.go) interface. The following stores are bundled.

* [in-memory](/stores/memory)
* [redis](/stores/redis)
* [postgres](/stores/postgres)
* [in-memory](/stores/memory)
* [secure cookie](/stores/securecookie)

# Usage
Check the [examples](/examples) directory for complete examples.

## Connecting a store
Stores can be registered to a session instance by using `Use` method.
Stores can be registered to a session instance by using `Use` method. Check individual [Stores](#stores) docs for more details.

```go
sess := simplesessions.New(simplesessions.Options{})
sess.UseStore(memory.New())
sess.UseStore(store.New())
```

## Connecting an HTTP handler
Any HTTP library can be connected to simplesessions by registering the `RegisterGetCookie()` and `RegisterSetCookie()` callbacks. The below example shows a simple `net/http` usecase. Another example showing `fasthttp` can be found [here](/examples).
Any HTTP library can be connected to simplesessions by registering the get and set cookie hooks using `SetCookieHooks()`. The below example shows a simple `net/http` usecase. Another example showing `fasthttp` can be found [here](/examples).

```go
var sessMan *simplesessions.Manager
Expand Down Expand Up @@ -81,61 +83,104 @@ func setCookie(cookie *http.Cookie, w interface{}) error {
func handler(w http.ResponseWriter, r *http.Request) {
// Use method `Acquire` to acquire a session before you access the session.
// Acquire takes read, write interface and context respectively.
// Read interface sent to callback registered with `RegisterGetCookie`
// and write interface is sent to callback registered with `RegisterWriteCookie`
// Read interface sent to callback registered with get cookie hook
// and write interface is sent to callback registered with write cookie hook
// set using `SetCookieHooks()` method.
//
// Optionally `context` can be sent which is usually request context where acquire
// session will get previously loaded session. This is useful if you have multiple
// middlewares accessing sessions. New sessions will be created in first middleware which
// does `Acquire` and will be reused in other places.
sess, err := sessMan.Acquire(r, w, nil)

// Use 'Set` and `Commit` to set a field for session.
// 'Set` ideally doesn't persist the value to store unless method `Commit` is called.
// But note that its up to the store you are using to decide to
// persist data only on `commit` or persist on `Set` itself.
// Stores like redis, db etc should persist on `Commit` while in-memory does on `Set`.
// No matter what store you use its better to explicitly
// call `Commit` method when you set all the values.
//
// If `Options.EnableAutoCreate` is set to True then if session doesn't exist it will
// be immediately created and returned. Bydefault its set to False so if session doesn't
// exist then `ErrInvalidSession` error is returned.
sess, err := sessMan.Acquire(nil, r, w)

// If session doesn't exist then create new session.
// In a traditional login flow you can create a new session once user completes the login flow.
if err == simplesessions.ErrInvalidSession {
sess, err = sessMan.NewSession(r, w)
}

// Use 'Set` or `SetMulti` to set a field for session.
err = sess.Set("somekey", "somevalue")
err = sess.Set("someotherkey", 10)
err = sess.Commit()
err = sess.SetMulti(map[string]interface{}{
"k1": "v1",
"k2": "v2",
})

// Use `Get` method to get a field from current session. The result will be an interface
// so you can use helper methods like
// `String', `Int`, `Int64`, `UInt64`, `Float64`, `Bytes`, `Bool`.
val, err := sess.String(sess.Get("somekey"))
fmt.Println("val=", val)

// Use `GetAll` to get map of all fields from session.
// The result is map of string and interface you can use helper methods to type cast it.
val, err := sess.GetAll()
all, err := sess.GetAll()
fmt.Println("all=", all)

// Use `GetMulti` to get values for given fields from session.
// The result is map of string and interface you can use helper methods to type cast it.
// If key is not there then store should ideally send `nil` value for given key.
val, err := sess.GetMulti("somekey", "someotherkey")
vals, err := sess.GetMulti("somekey", "someotherkey")
fmt.Println("vals=", vals)

// Use `Delete` to delete a field from session.
err := sess.Delete("somekey")
err = sess.Delete("somekey")

// Use `Clear` to empty the session but to keep the session alive.
err = sess.Clear()

// Use `Clear` to clear session from store.
err := sess.Clear()
// Use `Destroy` to clear session from store and cookie.
err = sess.Destroy()

fmt.Fprintf(w, "success")
}

func main() {
// Create a session manager with custom options like cookie name,
// cookie domain, is secure cookie etc. Check `Options` struct for more options.
sessMan := simplesessions.New(simplesessions.Options{})
sessMan := simplesessions.New(simplesessions.Options{
// If set to true then `Acquire()` method will create new session instead of throwing
// `ErrInvalidSession` when the session doesn't exist. By default its set to false.
EnableAutoCreate: false,
Cookie: simplesessions.CookieOptions{
// Name sets http cookie name. This is also sent as cookie name in `GetCookie` callback.
Name: "session",
// Domain sets hostname for the cookie. Domain specifies allowed hosts to receive the cookie.
Domain: "example.com",
// Path sets path for the cookie. Path indicates a URL path that must exist in the requested URL in order to send the cookie header.
Path: "/",
// IsSecure marks the cookie as secure cookie (only sent in HTTPS).
IsSecure: true,
// IsHTTPOnly marks the cookie as http only cookie. JS won't be able to access the cookie so prevents XSS attacks.
IsHTTPOnly: true,
// SameSite sets allows you to declare if your cookie should be restricted to a first-party or same-site context.
SameSite: http.SameSiteDefaultMode,
// Expires sets absolute expiration date and time for the cookie.
// If both Expires and MaxAge are sent then MaxAge takes precedence over Expires.
// Cookies without a Max-age or Expires attribute – are deleted when the current session ends
// and some browsers use session restoring when restarting. This can cause session cookies to last indefinitely.
Expires: time.Now().Add(time.Hour * 24),
// Sets the cookie's expiration in seconds from the current time, internally its rounder off to nearest seconds.
// If both Expires and MaxAge are sent then MaxAge takes precedence over Expires.
// Cookies without a Max-age or Expires attribute – are deleted when the current session ends
// and some browsers use session restoring when restarting. This can cause session cookies to last indefinitely.
MaxAge: time.Hour * 24,
},
})

// Create a new store instance and attach to session manager
sessMan.UseStore(memory.New())
// Register callbacks for read and write cookie
// Register callbacks for read and write cookie.
// Get cookie callback should get cookie based on cookie name and
// sent back in net/http cookie format.
sessMan.RegisterGetCookie(getCookie)
// Set cookie callback should set cookie it received for received cookie name.
sessMan.RegisterSetCookie(setCookie)
sessMan.SetCookieHooks(getCookie, setCookie)

http.HandleFunc("/set", handler)
// Initialize the handler.
http.HandleFunc("/", handler)
}
```
14 changes: 0 additions & 14 deletions TODO

This file was deleted.

187 changes: 0 additions & 187 deletions conv/conv.go

This file was deleted.

Loading

0 comments on commit a3b0aa7

Please sign in to comment.