how to set the expiry of the redis keys in golang - go

I am using golang as my backend.I am storing some token values in redis.I m setting the values HSET and getting the values in HGETALL.I would like to know if there is any function to set the expiry for the keys that i m storing in the redis database.i want the token and its data to be deleted after 1hour. I m using Redigo package for redis. Thanks.Appreciate any help.
I use this to set the struct with has token as key
redisCon.Do("HMSET", redis.Args{}.Add(hashToken).AddFlat(&dataStruct)...)

For those who use go-redis library, you can set expiration by calling
_, err = redisClient.Expire("my:redis:key", 1 * time.Hour).Result()
Alternatively, you can do that upon insertion
_, err = redisClient.Set("my:redis:key", "value", 1 * time.Hour).Result()

Redis documentation does not support a command like "HMSETEX".
"HMSET" modifies the hashkeys and not the root key. TTL is supported at root key level and not at the hash key level. Hence, in your case you must be doing something like this in a separate call:
redisCon.Do("EXPIRE", key, ttl)
Which client are you using to connect to redis?
For redigo you can use this - https://github.com/yadvendar/redigo-wrapper
In that use call
func Expire(RConn *redigo.Conn, key string, ttl int)
For goredis - https://godoc.org/gopkg.in/redis.v5#Client.TTL
In this use:
func (c *Client) TTL(key string) *DurationCmd

Related

How to turn *rsa.PublicKey into a recognisable key string

I have this function:
func GetSigningKey() *rsa.PublicKey {
set, _ := jwk.ParseString(GetWellKnown())
publicKey, _ := set.Keys[0].Materialize()
return publicKey.(*rsa.PublicKey)
}
.Materialize() returns interface{}, so I use this function to cast it to (what I think is) the expected type.
I can then use that token with:
publicKey := GetSigningKey()
token, _ := jwt.Parse(tokenString, func(*jwt.Token) (interface{}, error) {
return publicKey, nil
})
fmt.Println(token.Valid)
A few questions:
The purpose of the GetSigningKey function is to obtain the signing key from a well known file and cast the resulting key to the correct type.
1) This requires an http request which lives within GetWellKnown(). In that function, I do the http request and cache the response in a variable so following requests will pull from that variable. Is this a good practice? (follow up, should I do the same thing to the parsed public key in GetSigningKey())
2) Am I right in assuming that this is the best way to cast the interface{} to the correct type?
3) if I try fmt.Println(GetSigningKey()) I get a long, unrecognisable sequence of numbers
&{2568409364095596940117712027335... %!s(int=65537)}635 <nil>
What exactly am I seeing here?
There's no error checking in your code. Especially if the JWKS is empty or contains something that's not an RSA key, your program will just crash, and that tends to not be the behavior encouraged in Go. I'd add some checking around the whole thing; for instance,
func GetSigningKey() *rsa.PublicKey, error {
...
if rsaKey, ok := publicKey.(*rsa.PublicKey); ok {
return rsaKey, nil
}
return nil, errors.New("not an RSA key")
}
Remember that other signing methods exist and in principle you can get back an ECDSA key or (in theory) a raw key for symmetric signing, so checking here is valuable.
The other high-level thing you should try is to take the raw JSON Web token and decode it; https://jwt.io/ has a debugger I use. If the header contains a claim named "kid", that is a key ID, and you should be able to pass that key ID as an input to the JWKS library. That should be more robust than blindly using the first key in the JWKS.
Online JWKS documents can change, but seem to generally do so infrequently (on a scale of months). (If they do change, it's likely that a JWKS will include both old and new keys.) Caching them in-process is extremely reasonable. If you see a "kid" you don't recognize that can be a signal to try refetching the document.
Your fmt.Printf output looks like it's probably the raw RSA public key. The public key itself consists of two numbers, one being the product of two large prime numbers and the second frequently being exactly 65537. There is some more discussion of this in the Wikipedia description of RSA.

go-redis: TTL returns a negative duration when the key does not exist

I am using go-redis to interact with a REDIS server (version 3.2.100).
According to the Redis documentation, if a key does not exist, then the command TTL should return the value -2.
However, if the key does not exist, the method TTL returns a value which represents some duration (-2s), instead of an integer.
The code below illustrates this behaviour.
package main
import (
"github.com/go-redis/redis"
"fmt"
)
func main() {
fmt.Print("Create a REDIS client now.\n")
client := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
ttl, _ := client.TTL("MyKey").Result()
fmt.Printf("%v\n", ttl)
if ttl < 0 {
if -1 == ttl.Seconds() {
fmt.Print("The key will not expire.\n")
} else if -2 == ttl.Seconds() {
fmt.Print("The key does not exist.\n")
} else {
fmt.Printf("Unexpected error %d.\n", ttl.Seconds())
}
}
}
Output:
Create a REDIS client now.
-2s
The key does not exist.
Is it OK ? I think that the GO method TTL should return an integer, instead of a negative duration.
It is more useful to get TTL of the existing key from redis as time.Duration. -1 and -2 are the exceptions, asserted to the primary type. Maybe it could be more convenient if TTL returned (*DurationCmd, error) but I didn't dive deep into go-redis logic. I don't see a problem here. Just consider your always get time.Duration as a result.
In this implementation of Redis client TTL is returning *DurationCmd which itself doesn't interpret -2 and -1 in any special way, so after calling Result the value is represented as time.Duration.
Then your code is correct; please also note time.Duration is just a wrapped int with a special Stringer implementation, thus you have this misleading -2s output.
It would be more elegant for your code to simply print the duration after checking for special negative cases. By the way, you don't need to call duration.Seconds() With regard to comparison - you can directly compare time.Duration to raw int, but then the returned value would be in nanoseconds - even more mimsleading (thanks #Peter for pointing that); so, you're doing the right thing with ttl.Seconds().
Please also note it'd be better to not ignore error where you call Result, so the line would become:
ttl, err := client.TTL("MyKey").Result()
If you feel that it would be more valid and elegant to treat -2 and -1 as special cases requiring dedicated errors, then there's a space to open an issue on GitHub.
Hope this helps,

Redigo multi requests

I have previously been using this:
data, err := redis.Bytes(c.Do("GET", key))
to make sure that data returned is a slice of bytes.
However, I now need to add an extra command to the Redis request so I have something like this:
c.Send("MULTI")
c.Send("GET", key)
c.Send("EXPIRE", key)
r, err := c.Do("EXEC")
but now I can't seem to make the GET command return a slice of bytes. I've tried adding redis.Bytes like below but no luck.
c.Send("MULTI")
redis.Bytes(c.Send("GET", key))
c.Send("EXPIRE", key)
r, err := c.Do("EXEC")
In redis, the EXEC command returns an array containing the results of all the commands in the transaction.
redigo provides a Values function, which converts an array command reply to a []interface{}.
c.Send("MULTI")
c.Send("GET", key)
c.Send("EXPIRE", key)
r, err := redis.Values(c.Do("EXEC"))
r[0] now has the reply from your GET command as a interface{}, so you'll need to do a type assertion to get the slice of bytes you're expecting:
data := r[0].([]byte)
References
func Values: https://godoc.org/github.com/garyburd/redigo/redis#Values
Type assertions: https://golang.org/ref/spec#Type_assertions
MULTI is used to send several commands in an atomic way to Redis, by creating a transaction. This is not a pipeline at all.
None of the commands will be actually executed before the EXEC call so it is impossible to obtain the value when you call GET from within a transaction.
From the docs:
When a Redis connection is in the context of a MULTI request, all commands will reply with the string QUEUED (sent as a Status Reply from the point of view of the Redis protocol). A queued command is simply scheduled for execution when EXEC is called.
In redigo pipelining is done in a different way:
http://godoc.org/github.com/garyburd/redigo/redis#hdr-Pipelining
What you want to do is something like this (untested):
c.Send("GET", key)
c.Send("EXPIRE", key)
c.Flush()
v := redis.Bytes(c.Receive()) // reply from GET
_, err = c.Receive() // reply from EXPIRE

Go Gorilla Mux Session Name

I am having a hard time understanding Gorilla mux's session name.
http://www.gorillatoolkit.org/pkg/sessions#CookieStore.Get
var store = sessions.NewCookieStore([]byte("something-very-secret"))
func MyHandler(w http.ResponseWriter, r *http.Request) {
// Get a session. We're ignoring the error resulted from decoding an
// existing session: Get() always returns a session, even if empty.
session, _ := store.Get(r, "session-name")
// Set some session values.
session.Values["foo"] = "bar"
session.Values[42] = 43
// Save it.
session.Save(r, w)
}
I want to use session to avoid using global variables between two handlers. So I save the key-value in the shared session and retrieve the value from the session.
And I wonder if I want each user to have its own unique session and its Values, do I need to assign unique session name(session id)? Or the gorilla session handles by itself that each user gets its own session and values?
I wonder if I need to generate session names with unique identifiers.
Thanks
The session data is stored in the client's cookies. So the session you retrieve with store.Get(r, "session-name") is reading that particular client't (request) cookies. You do not need unique names. The name in this case is the name of the cookie so it will be unique to the request.

Timeout structure in Go

I'm looking for a storage with the ability of storing expiring data. I mean, a structure where you could specify the data to be stored and a timeout, and where that value would be removed after some time.
If you need this for caching, consider using cache2go:
cache := cache2go.Cache("c")
val := struct{x string}{"This is a test!"}
cache.Add("valA", 5*time.Second, &val)
As cache2go is for caching it operates on memory alone but you can specify a data loading routine to lazily load a missing value for a given key. The data loader can be utilized to
load the value from disk:
cache.SetDataLoader(func(key interface{}) *cache2go.CacheItem {
val := loadFromDisk()
item := cache2go.CreateCacheItem(key, 0, val)
return &item
})
go-cache supports this as well and supports loading cached items from disk:
func (c *Cache) Set(k string, x interface{}, d time.Duration)
Adds an item to the cache, replacing any existing item. If the duration is 0,
the cache's default expiration time is used. If it is -1, the item never
expires.
func (c *Cache) Save(w io.Writer) error
Writes the cache's items (using Gob) to an io.Writer. Returns an error if
the serialization fails, e.g. because there are unserializable objects like
channels in the cache.
func (c *Cache) Load(r io.Reader) error
Adds (Gob-serialized) cache items from an io.Reader, excluding any items that
already exist in the current cache.

Resources