Initial commit

This commit is contained in:
paul 2018-04-22 16:55:50 +02:00
commit 2422e3108f
37 changed files with 12691 additions and 0 deletions

169
services/clientstore.go Normal file
View 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
View 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
View 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
}