package client
import (
"encoding/json"
"errors"
"sort"
"sync"
"time"
"github.com/jcmturner/gokrb5/v8/messages"
"github.com/jcmturner/gokrb5/v8/types"
)
type Cache struct {
Entries map [string ]CacheEntry
mux sync .RWMutex
}
type CacheEntry struct {
SPN string
Ticket messages .Ticket `json:"-"`
AuthTime time .Time
StartTime time .Time
EndTime time .Time
RenewTill time .Time
SessionKey types .EncryptionKey `json:"-"`
}
func NewCache () *Cache {
return &Cache {
Entries : map [string ]CacheEntry {},
}
}
func (c *Cache ) getEntry (spn string ) (CacheEntry , bool ) {
c .mux .RLock ()
defer c .mux .RUnlock ()
e , ok := (*c ).Entries [spn ]
return e , ok
}
func (c *Cache ) JSON () (string , error ) {
c .mux .RLock ()
defer c .mux .RUnlock ()
var es []CacheEntry
keys := make ([]string , 0 , len (c .Entries ))
for k := range c .Entries {
keys = append (keys , k )
}
sort .Strings (keys )
for _ , k := range keys {
es = append (es , c .Entries [k ])
}
b , err := json .MarshalIndent (&es , "" , " " )
if err != nil {
return "" , err
}
return string (b ), nil
}
func (c *Cache ) addEntry (tkt messages .Ticket , authTime , startTime , endTime , renewTill time .Time , sessionKey types .EncryptionKey ) CacheEntry {
spn := tkt .SName .PrincipalNameString ()
c .mux .Lock ()
defer c .mux .Unlock ()
(*c ).Entries [spn ] = CacheEntry {
SPN : spn ,
Ticket : tkt ,
AuthTime : authTime ,
StartTime : startTime ,
EndTime : endTime ,
RenewTill : renewTill ,
SessionKey : sessionKey ,
}
return c .Entries [spn ]
}
func (c *Cache ) clear () {
c .mux .Lock ()
defer c .mux .Unlock ()
for k := range c .Entries {
delete (c .Entries , k )
}
}
func (c *Cache ) RemoveEntry (spn string ) {
c .mux .Lock ()
defer c .mux .Unlock ()
delete (c .Entries , spn )
}
func (cl *Client ) GetCachedTicket (spn string ) (messages .Ticket , types .EncryptionKey , bool ) {
if e , ok := cl .cache .getEntry (spn ); ok {
if time .Now ().UTC ().After (e .StartTime ) && time .Now ().UTC ().Before (e .EndTime ) {
cl .Log ("ticket received from cache for %s" , spn )
return e .Ticket , e .SessionKey , true
} else if time .Now ().UTC ().Before (e .RenewTill ) {
e , err := cl .renewTicket (e )
if err != nil {
return e .Ticket , e .SessionKey , false
}
return e .Ticket , e .SessionKey , true
}
}
var tkt messages .Ticket
var key types .EncryptionKey
return tkt , key , false
}
func (cl *Client ) renewTicket (e CacheEntry ) (CacheEntry , error ) {
spn := e .Ticket .SName
_ , _ , err := cl .TGSREQGenerateAndExchange (spn , e .Ticket .Realm , e .Ticket , e .SessionKey , true )
if err != nil {
return e , err
}
e , ok := cl .cache .getEntry (e .Ticket .SName .PrincipalNameString ())
if !ok {
return e , errors .New ("ticket was not added to cache" )
}
cl .Log ("ticket renewed for %s (EndTime: %v)" , spn .PrincipalNameString (), e .EndTime )
return e , nil
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .