Initial commit
This commit is contained in:
commit
2422e3108f
37 changed files with 12691 additions and 0 deletions
169
services/clientstore.go
Normal file
169
services/clientstore.go
Normal file
|
@ -0,0 +1,169 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"git.klink.asia/paul/certman/models"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNilCertificate = errors.New("Trying to store nil certificate")
|
||||
ErrDuplicate = errors.New("Client with that name already exists")
|
||||
ErrUserNotExists = errors.New("User does not exist")
|
||||
ErrClientNotExists = errors.New("Client does not exist")
|
||||
)
|
||||
|
||||
type ClientCollection struct {
|
||||
sync.RWMutex
|
||||
path string
|
||||
|
||||
Clients map[uint]*models.Client
|
||||
UserIndex map[string]map[string]uint
|
||||
LastID uint
|
||||
}
|
||||
|
||||
func NewClientCollection(path string) *ClientCollection {
|
||||
// empty collection
|
||||
var clientCollection = ClientCollection{
|
||||
path: path,
|
||||
Clients: make(map[uint]*models.Client),
|
||||
UserIndex: make(map[string]map[string]uint),
|
||||
LastID: 0,
|
||||
}
|
||||
|
||||
raw, err := ioutil.ReadFile(path)
|
||||
if os.IsNotExist(err) {
|
||||
return &clientCollection
|
||||
} else if err != nil {
|
||||
log.Println(err)
|
||||
return &clientCollection
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(raw, &clientCollection); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
return &clientCollection
|
||||
}
|
||||
|
||||
// CreateClient inserts a client into the datastore
|
||||
func (db *ClientCollection) CreateClient(client *models.Client) error {
|
||||
db.Lock()
|
||||
defer db.Unlock()
|
||||
|
||||
if client == nil {
|
||||
return ErrNilCertificate
|
||||
}
|
||||
|
||||
db.LastID++ // increment Id
|
||||
client.ID = db.LastID
|
||||
|
||||
userIndex, exists := db.UserIndex[client.User]
|
||||
if !exists {
|
||||
// create user index if not exists
|
||||
db.UserIndex[client.User] = make(map[string]uint)
|
||||
userIndex = db.UserIndex[client.User]
|
||||
}
|
||||
|
||||
if _, exists = userIndex[client.Name]; exists {
|
||||
return ErrDuplicate
|
||||
}
|
||||
|
||||
// if all went well, add client and set the index
|
||||
db.Clients[client.ID] = client
|
||||
userIndex[client.Name] = client.ID
|
||||
db.UserIndex[client.User] = userIndex
|
||||
|
||||
return db.save()
|
||||
}
|
||||
|
||||
// ListClientsForUser returns a slice of 'count' client for user 'user', starting at 'offset'
|
||||
func (db *ClientCollection) ListClientsForUser(user string) ([]*models.Client, error) {
|
||||
db.RLock()
|
||||
defer db.RUnlock()
|
||||
|
||||
var clients = make([]*models.Client, 0)
|
||||
|
||||
userIndex, exists := db.UserIndex[user]
|
||||
if !exists {
|
||||
return nil, errors.New("user does not exist")
|
||||
}
|
||||
|
||||
for _, clientID := range userIndex {
|
||||
clients = append(clients, db.Clients[clientID])
|
||||
}
|
||||
|
||||
return clients, nil
|
||||
}
|
||||
|
||||
// GetClientByID returns a single client by ID
|
||||
func (db *ClientCollection) GetClientByID(id uint) (*models.Client, error) {
|
||||
|
||||
client, exists := db.Clients[id]
|
||||
if !exists {
|
||||
return nil, ErrClientNotExists
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// GetClientByNameUser returns a single client by ID
|
||||
func (db *ClientCollection) GetClientByNameUser(name, user string) (*models.Client, error) {
|
||||
db.RLock()
|
||||
defer db.RUnlock()
|
||||
|
||||
userIndex, exists := db.UserIndex[user]
|
||||
if !exists {
|
||||
return nil, ErrUserNotExists
|
||||
}
|
||||
|
||||
clientID, exists := userIndex[name]
|
||||
if !exists {
|
||||
return nil, ErrClientNotExists
|
||||
}
|
||||
|
||||
client, exists := db.Clients[clientID]
|
||||
if !exists {
|
||||
return nil, ErrClientNotExists
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// DeleteClient removes a client from the datastore
|
||||
func (db *ClientCollection) DeleteClient(id uint) error {
|
||||
db.Lock()
|
||||
defer db.Unlock()
|
||||
|
||||
client, exists := db.Clients[id]
|
||||
if !exists {
|
||||
return nil // nothing to delete
|
||||
}
|
||||
|
||||
userIndex, exists := db.UserIndex[client.User]
|
||||
if !exists {
|
||||
return ErrUserNotExists
|
||||
}
|
||||
|
||||
delete(userIndex, client.Name) // delete client index
|
||||
|
||||
// if index is now empty, delete the user entry
|
||||
if len(userIndex) == 0 {
|
||||
delete(db.UserIndex, client.User)
|
||||
}
|
||||
|
||||
// finally delete the client
|
||||
delete(db.Clients, id)
|
||||
|
||||
return db.save()
|
||||
}
|
||||
|
||||
func (c *ClientCollection) save() error {
|
||||
collectionJSON, _ := json.Marshal(c)
|
||||
return ioutil.WriteFile(c.path, collectionJSON, 0600)
|
||||
}
|
21
services/provider.go
Normal file
21
services/provider.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package services
|
||||
|
||||
type Config struct {
|
||||
CollectionPath string
|
||||
Sessions *SessionsConfig
|
||||
}
|
||||
|
||||
type Provider struct {
|
||||
ClientCollection *ClientCollection
|
||||
Sessions *Sessions
|
||||
}
|
||||
|
||||
// NewProvider returns the ServiceProvider
|
||||
func NewProvider(conf *Config) *Provider {
|
||||
var provider = &Provider{}
|
||||
|
||||
provider.ClientCollection = NewClientCollection(conf.CollectionPath)
|
||||
provider.Sessions = NewSessions(conf.Sessions)
|
||||
|
||||
return provider
|
||||
}
|
123
services/sessions.go
Normal file
123
services/sessions.go
Normal file
|
@ -0,0 +1,123 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/alexedwards/scs"
|
||||
)
|
||||
|
||||
var (
|
||||
// FlashesKey is the key used for the flashes in the cookie
|
||||
FlashesKey = "_flashes"
|
||||
// UserEmailKey is the key used to reference usernames
|
||||
UserEmailKey = "_user_email"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Register the Flash message type, so gob can serialize it
|
||||
gob.Register(Flash{})
|
||||
}
|
||||
|
||||
type SessionsConfig struct {
|
||||
SessionName string
|
||||
CookieKey string
|
||||
HttpOnly bool
|
||||
Secure bool
|
||||
Lifetime time.Duration
|
||||
}
|
||||
|
||||
// Sessions is a wrapped scs.Store in order to implement custom logic
|
||||
type Sessions struct {
|
||||
*scs.Manager
|
||||
}
|
||||
|
||||
// NewSessions populates the default sessions Store
|
||||
func NewSessions(conf *SessionsConfig) *Sessions {
|
||||
store := scs.NewCookieManager(
|
||||
conf.CookieKey,
|
||||
)
|
||||
store.Name(conf.SessionName)
|
||||
store.HttpOnly(true)
|
||||
store.Lifetime(conf.Lifetime)
|
||||
store.Secure(conf.Secure)
|
||||
|
||||
return &Sessions{store}
|
||||
}
|
||||
|
||||
func (store *Sessions) GetUsername(req *http.Request) string {
|
||||
if store == nil {
|
||||
// if store was not initialized, all requests fail
|
||||
log.Println("Nil pointer when checking session for username")
|
||||
return ""
|
||||
}
|
||||
|
||||
sess := store.Load(req)
|
||||
|
||||
email, err := sess.GetString(UserEmailKey)
|
||||
if err != nil {
|
||||
// Username found
|
||||
return ""
|
||||
|
||||
}
|
||||
|
||||
// User is logged in
|
||||
return email
|
||||
}
|
||||
|
||||
func (store *Sessions) SetUsername(w http.ResponseWriter, req *http.Request, username string) {
|
||||
if store == nil {
|
||||
// if store was not initialized, do nothing
|
||||
return
|
||||
}
|
||||
|
||||
sess := store.Load(req)
|
||||
|
||||
// renew token to avoid session pinning/fixation attack
|
||||
sess.RenewToken(w)
|
||||
|
||||
sess.PutString(w, UserEmailKey, username)
|
||||
|
||||
}
|
||||
|
||||
type Flash struct {
|
||||
Message template.HTML
|
||||
Type string
|
||||
}
|
||||
|
||||
// Render renders the flash message as a notification box
|
||||
func (flash Flash) Render() template.HTML {
|
||||
return template.HTML(
|
||||
fmt.Sprintf(
|
||||
"<div class=\"notification is-radiusless is-%s\"><div class=\"container has-text-centered\">%s</div></div>",
|
||||
flash.Type, flash.Message,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Flash add flash message to session data
|
||||
func (store *Sessions) Flash(w http.ResponseWriter, req *http.Request, flash Flash) error {
|
||||
var flashes []Flash
|
||||
|
||||
sess := store.Load(req)
|
||||
|
||||
if err := sess.GetObject(FlashesKey, &flashes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flashes = append(flashes, flash)
|
||||
|
||||
return sess.PutObject(w, FlashesKey, flashes)
|
||||
}
|
||||
|
||||
// Flashes returns a slice of flash messages from session data
|
||||
func (store *Sessions) Flashes(w http.ResponseWriter, req *http.Request) []Flash {
|
||||
var flashes []Flash
|
||||
sess := store.Load(req)
|
||||
sess.PopObject(w, FlashesKey, &flashes)
|
||||
return flashes
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue