Compare commits
8 Commits
123071ab1b
...
31484dd44a
Author | SHA1 | Date | |
---|---|---|---|
31484dd44a | |||
3b4e1e01d2 | |||
03425fc2c6 | |||
0b9bae3bf8 | |||
8ed0676e51 | |||
d7cbb34814 | |||
23f0c6f4a4 | |||
cb322c57f9 |
@ -4,19 +4,19 @@ tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = [
|
||||
"-aes tmp/cpolis.aes",
|
||||
"-articles tmp/articles",
|
||||
"-config tmp/config.toml",
|
||||
"-desc 'Freiheit, Gleichheit, Brüderlichkeit, Toleranz und Humanität'",
|
||||
"-domain localhost",
|
||||
"-feed tmp/cpolis.atom",
|
||||
"-firebase tmp/firebase.json",
|
||||
"-key tmp/key.gob",
|
||||
"-gob tmp/cpolis.gob",
|
||||
"-link https://distrikt-ni-st.de",
|
||||
"-log tmp/cpolis.log",
|
||||
"-pdfs tmp/pdfs",
|
||||
"-pics tmp/pics",
|
||||
"-port 8080",
|
||||
"-rss tmp/orientexpress_alle.rss",
|
||||
"-title 'Freimaurer Distrikt Niedersachsen und Sachsen-Anhalt'",
|
||||
"-web web",
|
||||
"-width 1024",
|
||||
|
@ -18,7 +18,7 @@ func GenerateAtomFeed(c *Config, db *DB) (*string, error) {
|
||||
|
||||
feed.Generator = atom.NewGenerator("cpolis")
|
||||
feed.Generator.URI = "https://git.streifling.com/jason/cpolis"
|
||||
feed.Generator.Version = "0.13"
|
||||
feed.Generator.Version = c.Version
|
||||
|
||||
articles, err := db.GetCertainArticles("published", true)
|
||||
if err != nil {
|
||||
@ -39,7 +39,7 @@ func GenerateAtomFeed(c *Config, db *DB) (*string, error) {
|
||||
entry.Links[linkID].Rel = "enclosure"
|
||||
entry.Links[linkID].Type = "image/webp"
|
||||
|
||||
user, err := db.GetUser(article.AuthorID)
|
||||
user, err := db.GetUser(c, article.AuthorID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting user user info for RSS feed: %v", err)
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
AESKeyFile string
|
||||
ArticleDir string
|
||||
ConfigFile string
|
||||
DBName string
|
||||
@ -19,14 +20,14 @@ type Config struct {
|
||||
Domain string
|
||||
AtomFeed string
|
||||
FirebaseKey string
|
||||
KeyFile string
|
||||
GOBKeyFile string
|
||||
Link string
|
||||
LogFile string
|
||||
PDFDir string
|
||||
PicsDir string
|
||||
Port string
|
||||
RSSFile string
|
||||
Title string
|
||||
Version string
|
||||
WebDir string
|
||||
MaxImgHeight int
|
||||
MaxImgWidth int
|
||||
@ -34,19 +35,20 @@ type Config struct {
|
||||
|
||||
func newConfig() *Config {
|
||||
return &Config{
|
||||
AESKeyFile: "/var/www/cpolis/aes.key",
|
||||
ArticleDir: "/var/www/cpolis/articles",
|
||||
ConfigFile: "/etc/cpolis/config.toml",
|
||||
DBName: "cpolis",
|
||||
AtomFeed: "/var/www/cpolis/cpolis.atom",
|
||||
FirebaseKey: "/var/www/cpolis/serviceAccountKey.json",
|
||||
KeyFile: "/var/www/cpolis/cpolis.key",
|
||||
GOBKeyFile: "/var/www/cpolis/gob.key",
|
||||
LogFile: "/var/log/cpolis.log",
|
||||
MaxImgHeight: 1080,
|
||||
MaxImgWidth: 1920,
|
||||
PDFDir: "/var/www/cpolis/pdfs",
|
||||
PicsDir: "/var/www/cpolis/pics",
|
||||
Port: ":8080",
|
||||
RSSFile: "/var/www/cpolis/cpolis.rss",
|
||||
Version: "v0.13.0",
|
||||
WebDir: "/var/www/cpolis/web",
|
||||
}
|
||||
}
|
||||
@ -102,6 +104,7 @@ func (c *Config) handleCliArgs() error {
|
||||
var port int
|
||||
var err error
|
||||
|
||||
flag.StringVar(&c.AESKeyFile, "aes", c.AESKeyFile, "aes key file")
|
||||
flag.StringVar(&c.ArticleDir, "articles", c.ArticleDir, "articles directory")
|
||||
flag.StringVar(&c.AtomFeed, "feed", c.AtomFeed, "atom feed file")
|
||||
flag.StringVar(&c.ConfigFile, "config", c.ConfigFile, "config file")
|
||||
@ -109,12 +112,11 @@ func (c *Config) handleCliArgs() error {
|
||||
flag.StringVar(&c.Description, "desc", c.Description, "channel description")
|
||||
flag.StringVar(&c.Domain, "domain", c.Domain, "domain name")
|
||||
flag.StringVar(&c.FirebaseKey, "firebase", c.FirebaseKey, "Firebase service account key file")
|
||||
flag.StringVar(&c.KeyFile, "key", c.KeyFile, "key file")
|
||||
flag.StringVar(&c.GOBKeyFile, "gob", c.GOBKeyFile, "gob key file")
|
||||
flag.StringVar(&c.Link, "link", c.Link, "channel Link")
|
||||
flag.StringVar(&c.LogFile, "log", c.LogFile, "log file")
|
||||
flag.StringVar(&c.PDFDir, "pdfs", c.PDFDir, "pdf directory")
|
||||
flag.StringVar(&c.PicsDir, "pics", c.PicsDir, "pictures directory")
|
||||
flag.StringVar(&c.RSSFile, "rss", c.RSSFile, "RSS file")
|
||||
flag.StringVar(&c.Title, "title", c.Title, "channel title")
|
||||
flag.StringVar(&c.WebDir, "web", c.WebDir, "web directory")
|
||||
flag.IntVar(&c.MaxImgHeight, "height", c.MaxImgHeight, "maximum image height")
|
||||
@ -147,6 +149,14 @@ func (c *Config) setupConfig(cliConfig *Config) error {
|
||||
var err error
|
||||
defaultConfig := newConfig()
|
||||
|
||||
if cliConfig.AESKeyFile != defaultConfig.AESKeyFile {
|
||||
c.AESKeyFile = cliConfig.AESKeyFile
|
||||
}
|
||||
c.AESKeyFile, err = mkFile(c.AESKeyFile, 0600, 0700)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting up file: %v", err)
|
||||
}
|
||||
|
||||
if cliConfig.ArticleDir != defaultConfig.ArticleDir {
|
||||
c.ArticleDir = cliConfig.ArticleDir
|
||||
}
|
||||
@ -187,10 +197,10 @@ func (c *Config) setupConfig(cliConfig *Config) error {
|
||||
return fmt.Errorf("error setting up file: %v", err)
|
||||
}
|
||||
|
||||
if cliConfig.KeyFile != defaultConfig.KeyFile {
|
||||
c.KeyFile = cliConfig.KeyFile
|
||||
if cliConfig.GOBKeyFile != defaultConfig.GOBKeyFile {
|
||||
c.GOBKeyFile = cliConfig.GOBKeyFile
|
||||
}
|
||||
c.KeyFile, err = mkFile(c.KeyFile, 0600, 0700)
|
||||
c.GOBKeyFile, err = mkFile(c.GOBKeyFile, 0600, 0700)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting up file: %v", err)
|
||||
}
|
||||
@ -235,14 +245,6 @@ func (c *Config) setupConfig(cliConfig *Config) error {
|
||||
c.Port = cliConfig.Port
|
||||
}
|
||||
|
||||
if cliConfig.RSSFile != defaultConfig.RSSFile {
|
||||
c.RSSFile = cliConfig.RSSFile
|
||||
}
|
||||
c.RSSFile, err = mkFile(c.RSSFile, 0600, 0700)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting up file: %v", err)
|
||||
}
|
||||
|
||||
if cliConfig.Title != defaultConfig.Title {
|
||||
c.Title = cliConfig.Title
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ func GenerateRSS(c *Config, db *DB) (*string, error) {
|
||||
tagNames = append(tagNames, "autogenerated")
|
||||
}
|
||||
|
||||
user, err := db.GetUser(article.AuthorID)
|
||||
user, err := db.GetUser(c, article.AuthorID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting user user info for RSS feed: %v", err)
|
||||
}
|
||||
|
@ -2,9 +2,16 @@ package backend
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
@ -18,24 +25,135 @@ const (
|
||||
)
|
||||
|
||||
type User struct {
|
||||
UserName string
|
||||
FirstName string
|
||||
LastName string
|
||||
ID int64
|
||||
Role int
|
||||
UserName string
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string
|
||||
ProfilePicLink string
|
||||
ID int64
|
||||
Role int
|
||||
}
|
||||
|
||||
func (db *DB) AddUser(u *User, pass string) (int64, error) {
|
||||
func readKey(filename string) ([]byte, error) {
|
||||
key, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading from aes key file: %v", err)
|
||||
}
|
||||
|
||||
if len(key) != 44 {
|
||||
return nil, errors.New("key is not 32 bytes long")
|
||||
}
|
||||
|
||||
key, err = base64.StdEncoding.DecodeString(string(key))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error base64 decoding key: %v", err)
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func key(c *Config) ([]byte, error) {
|
||||
key, err := readKey(c.AESKeyFile)
|
||||
if err != nil {
|
||||
key = make([]byte, 32)
|
||||
if _, err := rand.Read(key); err != nil {
|
||||
return nil, fmt.Errorf("error generating random key: %v", err)
|
||||
}
|
||||
|
||||
fileKey := make([]byte, 44)
|
||||
base64.StdEncoding.Encode(fileKey, key)
|
||||
if err = os.WriteFile(c.AESKeyFile, fileKey, 0600); err != nil {
|
||||
return nil, fmt.Errorf("error writing key to file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func aesEncrypt(c *Config, plaintext string) (string, error) {
|
||||
key, err := key(c)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error retrieving key: %v", err)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating cipher block: %v", err)
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating new gcm: %v", err)
|
||||
}
|
||||
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return "", fmt.Errorf("error creating nonce: %v", err)
|
||||
}
|
||||
|
||||
cipherText := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
|
||||
return base64.StdEncoding.EncodeToString(cipherText), nil
|
||||
}
|
||||
|
||||
func aesDecrypt(c *Config, ciphertext string) (string, error) {
|
||||
key, err := key(c)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error retrieving key: %v", err)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating cipher block: %v", err)
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating new gcm: %v", err)
|
||||
}
|
||||
|
||||
data, err := base64.StdEncoding.DecodeString(ciphertext)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error base64 decoding ciphertext: %v", err)
|
||||
}
|
||||
|
||||
nonceSize := gcm.NonceSize()
|
||||
nonce, cipherText := data[:nonceSize], data[nonceSize:]
|
||||
|
||||
plaintext, err := gcm.Open(nil, nonce, cipherText, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error aes decoding ciphertext: %v", err)
|
||||
}
|
||||
|
||||
return string(plaintext), nil
|
||||
}
|
||||
|
||||
func (db *DB) AddUser(c *Config, u *User, pass string) (int64, error) {
|
||||
hashedPass, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error creating password hash: %v", err)
|
||||
}
|
||||
|
||||
aesFirstName, err := aesEncrypt(c, u.FirstName)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error encrypting first name: %v", err)
|
||||
}
|
||||
|
||||
aesLastName, err := aesEncrypt(c, u.LastName)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error encrypting last name: %v", err)
|
||||
}
|
||||
|
||||
aesEmail, err := aesEncrypt(c, u.Email)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error encrypting email: %v", err)
|
||||
}
|
||||
|
||||
query := `
|
||||
INSERT INTO users (username, password, first_name, last_name, role)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
INSERT INTO users (username, password, first_name, last_name, email, role)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`
|
||||
result, err := db.Exec(query, u.UserName, string(hashedPass), u.FirstName, u.LastName, u.Role)
|
||||
|
||||
result, err := db.Exec(query, u.UserName, string(hashedPass), aesFirstName, aesLastName, aesEmail, u.Role)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error inserting new user %v into DB: %v", u.UserName, err)
|
||||
}
|
||||
@ -129,34 +247,44 @@ func (tx *Tx) ChangePassword(id int64, oldPass, newPass string) error {
|
||||
}
|
||||
|
||||
// TODO: No need for ID field in general
|
||||
func (db *DB) GetUser(id int64) (*User, error) {
|
||||
func (db *DB) GetUser(c *Config, id int64) (*User, error) {
|
||||
var aesFirstName, aesLastName, aesEmail string
|
||||
var err error
|
||||
|
||||
user := new(User)
|
||||
query := `
|
||||
SELECT id, username, first_name, last_name, role
|
||||
SELECT id, username, first_name, last_name, email, role
|
||||
FROM users
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
row := db.QueryRow(query, id)
|
||||
if err := row.Scan(&user.ID, &user.UserName, &user.FirstName,
|
||||
&user.LastName, &user.Role); err != nil {
|
||||
if err := row.Scan(&user.ID, &user.UserName, &aesFirstName, &aesLastName, &aesEmail, &user.Role); err != nil {
|
||||
return nil, fmt.Errorf("error reading user information: %v", err)
|
||||
}
|
||||
|
||||
user.FirstName, err = aesDecrypt(c, aesFirstName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decrypting first name: %v", err)
|
||||
}
|
||||
|
||||
user.LastName, err = aesDecrypt(c, aesLastName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decrypting last name: %v", err)
|
||||
}
|
||||
|
||||
user.Email, err = aesDecrypt(c, aesEmail)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decrypting email: %v", err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (db *DB) UpdateOwnUserAttributes(id int64, user, first, last, oldPass, newPass, newPass2 string) error {
|
||||
passwordEmpty := true
|
||||
if len(newPass) > 0 || len(newPass2) > 0 {
|
||||
if newPass != newPass2 {
|
||||
return fmt.Errorf("error: passwords do not match")
|
||||
}
|
||||
passwordEmpty = false
|
||||
}
|
||||
|
||||
tx := new(Tx)
|
||||
func (db *DB) UpdateOwnUserAttributes(c *Config, id int64, userName, firstName, lastName, email, oldPass, newPass string) error {
|
||||
var err error
|
||||
tx := new(Tx)
|
||||
passwordEmpty := len(newPass) > 0
|
||||
|
||||
for i := 0; i < TxMaxRetries; i++ {
|
||||
err := func() error {
|
||||
@ -174,10 +302,35 @@ func (db *DB) UpdateOwnUserAttributes(id int64, user, first, last, oldPass, newP
|
||||
}
|
||||
}
|
||||
|
||||
aesFirstName, err := aesEncrypt(c, firstName)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return fmt.Errorf("error encrypting first name: %v", err)
|
||||
}
|
||||
|
||||
aesLastName, err := aesEncrypt(c, lastName)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return fmt.Errorf("error encrypting last name: %v", err)
|
||||
}
|
||||
|
||||
aesEmail, err := aesEncrypt(c, email)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return fmt.Errorf("error encrypting email: %v", err)
|
||||
}
|
||||
|
||||
if err = tx.UpdateAttributes(
|
||||
&Attribute{Table: "users", ID: id, AttName: "username", Value: user},
|
||||
&Attribute{Table: "users", ID: id, AttName: "first_name", Value: first},
|
||||
&Attribute{Table: "users", ID: id, AttName: "last_name", Value: last},
|
||||
&Attribute{Table: "users", ID: id, AttName: "username", Value: userName},
|
||||
&Attribute{Table: "users", ID: id, AttName: "first_name", Value: aesFirstName},
|
||||
&Attribute{Table: "users", ID: id, AttName: "last_name", Value: aesLastName},
|
||||
&Attribute{Table: "users", ID: id, AttName: "email", Value: aesEmail},
|
||||
); err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
@ -202,13 +355,13 @@ func (db *DB) UpdateOwnUserAttributes(id int64, user, first, last, oldPass, newP
|
||||
return fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
|
||||
}
|
||||
|
||||
func (db *DB) AddFirstUser(u *User, pass string) (int64, error) {
|
||||
func (db *DB) AddFirstUser(c *Config, u *User, pass string) (int64, error) {
|
||||
var numUsers int64
|
||||
txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable}
|
||||
selectQuery := "SELECT COUNT(*) FROM users"
|
||||
insertQuery := `
|
||||
INSERT INTO users (username, password, first_name, last_name, role)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
INSERT INTO users (username, password, first_name, last_name, email, role)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`
|
||||
|
||||
for i := 0; i < TxMaxRetries; i++ {
|
||||
@ -239,7 +392,31 @@ func (db *DB) AddFirstUser(u *User, pass string) (int64, error) {
|
||||
return 0, fmt.Errorf("error creating password hash: %v", err)
|
||||
}
|
||||
|
||||
result, err := tx.Exec(insertQuery, u.UserName, string(hashedPass), u.FirstName, u.LastName, u.Role)
|
||||
aesFirstName, err := aesEncrypt(c, u.FirstName)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return 0, fmt.Errorf("error encrypting first name: %v", err)
|
||||
}
|
||||
|
||||
aesLastName, err := aesEncrypt(c, u.LastName)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return 0, fmt.Errorf("error encrypting last name: %v", err)
|
||||
}
|
||||
|
||||
aesEmail, err := aesEncrypt(c, u.Email)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return 0, fmt.Errorf("error encrypting email: %v", err)
|
||||
}
|
||||
|
||||
result, err := tx.Exec(insertQuery, u.UserName, string(hashedPass), aesFirstName, aesLastName, aesEmail, u.Role)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
@ -270,8 +447,11 @@ func (db *DB) AddFirstUser(u *User, pass string) (int64, error) {
|
||||
return 0, fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
|
||||
}
|
||||
|
||||
func (db *DB) GetAllUsers() (map[int64]*User, error) {
|
||||
query := "SELECT id, username, first_name, last_name, role FROM users"
|
||||
func (db *DB) GetAllUsers(c *Config) (map[int64]*User, error) {
|
||||
var aesFirstName, aesLastName, aesEmail string
|
||||
var err error
|
||||
|
||||
query := "SELECT id, username, first_name, last_name, email, role FROM users"
|
||||
|
||||
rows, err := db.Query(query)
|
||||
if err != nil {
|
||||
@ -281,10 +461,25 @@ func (db *DB) GetAllUsers() (map[int64]*User, error) {
|
||||
users := make(map[int64]*User, 0)
|
||||
for rows.Next() {
|
||||
user := new(User)
|
||||
if err = rows.Scan(&user.ID, &user.UserName, &user.FirstName,
|
||||
&user.LastName, &user.Role); err != nil {
|
||||
if err = rows.Scan(&user.ID, &user.UserName, &aesFirstName, &aesLastName, &aesEmail, &user.Role); err != nil {
|
||||
return nil, fmt.Errorf("error getting user info: %v", err)
|
||||
}
|
||||
|
||||
user.FirstName, err = aesDecrypt(c, aesFirstName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decrypting first name: %v", err)
|
||||
}
|
||||
|
||||
user.LastName, err = aesDecrypt(c, aesLastName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decrypting last name: %v", err)
|
||||
}
|
||||
|
||||
user.Email, err = aesDecrypt(c, aesEmail)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decrypting email: %v", err)
|
||||
}
|
||||
|
||||
users[user.ID] = user
|
||||
}
|
||||
|
||||
@ -311,17 +506,10 @@ func (tx *Tx) SetPassword(id int64, newPass string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) UpdateUserAttributes(id int64, user, first, last, newPass, newPass2 string, role int) error {
|
||||
passwordEmpty := true
|
||||
if len(newPass) > 0 || len(newPass2) > 0 {
|
||||
if newPass != newPass2 {
|
||||
return fmt.Errorf("error: passwords do not match")
|
||||
}
|
||||
passwordEmpty = false
|
||||
}
|
||||
|
||||
tx := new(Tx)
|
||||
func (db *DB) UpdateUserAttributes(c *Config, id int64, userName, firstName, lastName, email, newPass string, role int) error {
|
||||
var err error
|
||||
tx := new(Tx)
|
||||
passwordEmpty := len(newPass) > 0
|
||||
|
||||
for i := 0; i < TxMaxRetries; i++ {
|
||||
err := func() error {
|
||||
@ -339,10 +527,35 @@ func (db *DB) UpdateUserAttributes(id int64, user, first, last, newPass, newPass
|
||||
}
|
||||
}
|
||||
|
||||
aesFirstName, err := aesEncrypt(c, firstName)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return fmt.Errorf("error encrypting first name: %v", err)
|
||||
}
|
||||
|
||||
aesLastName, err := aesEncrypt(c, lastName)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return fmt.Errorf("error encrypting last name: %v", err)
|
||||
}
|
||||
|
||||
aesEmail, err := aesEncrypt(c, email)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return fmt.Errorf("error encrypting email: %v", err)
|
||||
}
|
||||
|
||||
if err = tx.UpdateAttributes(
|
||||
&Attribute{Table: "users", ID: id, AttName: "username", Value: user},
|
||||
&Attribute{Table: "users", ID: id, AttName: "first_name", Value: first},
|
||||
&Attribute{Table: "users", ID: id, AttName: "last_name", Value: last},
|
||||
&Attribute{Table: "users", ID: id, AttName: "username", Value: userName},
|
||||
&Attribute{Table: "users", ID: id, AttName: "first_name", Value: aesFirstName},
|
||||
&Attribute{Table: "users", ID: id, AttName: "last_name", Value: aesLastName},
|
||||
&Attribute{Table: "users", ID: id, AttName: "email", Value: aesEmail},
|
||||
&Attribute{Table: "users", ID: id, AttName: "role", Value: role},
|
||||
); err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
|
@ -1,17 +0,0 @@
|
||||
package calls
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
b "streifling.com/jason/cpolis/cmd/backend"
|
||||
)
|
||||
|
||||
func ServeRSS(c *b.Config) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if !tokenIsVerified(w, r, c) {
|
||||
return
|
||||
}
|
||||
|
||||
http.ServeFile(w, r, c.RSSFile)
|
||||
}
|
||||
}
|
@ -146,9 +146,12 @@ func SubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -225,9 +228,12 @@ func ResubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -469,9 +475,12 @@ func PublishArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -501,9 +510,12 @@ func RejectArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -698,9 +710,12 @@ func DeleteArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -755,8 +770,11 @@ func AllowEditArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl := template.Must(template.ParseFiles(c.WebDir + "/templates/hub.html"))
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -110,9 +110,12 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -56,12 +56,18 @@ func HomePage(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
data := new(struct {
|
||||
Version string
|
||||
Role int
|
||||
})
|
||||
data.Version = c.Version
|
||||
|
||||
files := make([]string, 2)
|
||||
files[0] = c.WebDir + "/templates/index.html"
|
||||
if numRows == 0 {
|
||||
files[1] = c.WebDir + "/templates/first-user.html"
|
||||
tmpl, err := template.ParseFiles(files...)
|
||||
if err = template.Must(tmpl, err).Execute(w, nil); err != nil {
|
||||
if err = template.Must(tmpl, err).Execute(w, data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -74,9 +80,10 @@ func HomePage(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
if auth, ok := session.Values["authenticated"].(bool); auth && ok {
|
||||
data.Role = session.Values["role"].(int)
|
||||
files[1] = c.WebDir + "/templates/hub.html"
|
||||
tmpl, err := template.ParseFiles(files...)
|
||||
if err = template.Must(tmpl, err).Execute(w, session.Values["role"]); err != nil {
|
||||
if err = template.Must(tmpl, err).Execute(w, data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -84,7 +91,7 @@ func HomePage(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
} else {
|
||||
files[1] = c.WebDir + "/templates/login.html"
|
||||
tmpl, err := template.ParseFiles(files...)
|
||||
if err = template.Must(tmpl, err).Execute(w, nil); err != nil {
|
||||
if err = template.Must(tmpl, err).Execute(w, data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -111,7 +118,7 @@ func Login(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := db.GetUser(id)
|
||||
user, err := db.GetUser(c, id)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@ -125,7 +132,7 @@ func Login(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
}
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", user.Role); err != nil {
|
||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", user); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -174,8 +181,11 @@ func ShowHub(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -41,9 +41,12 @@ func AddTag(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
}
|
||||
db.AddTag(tag)
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -11,8 +11,8 @@ import (
|
||||
)
|
||||
|
||||
func checkUserStrings(user *b.User) (string, int, bool) {
|
||||
userLen := 15
|
||||
nameLen := 50
|
||||
userLen := 255
|
||||
nameLen := 56 // max value when aes encrypting up to 255 bytes
|
||||
|
||||
if len(user.UserName) > userLen {
|
||||
return "Benutzername", userLen, false
|
||||
@ -55,12 +55,13 @@ func AddUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
UserName: r.PostFormValue("username"),
|
||||
FirstName: r.PostFormValue("first-name"),
|
||||
LastName: r.PostFormValue("last-name"),
|
||||
Email: r.PostFormValue("email"),
|
||||
}
|
||||
pass := r.PostFormValue("password")
|
||||
pass2 := r.PostFormValue("password2")
|
||||
email2 := r.PostFormValue("email2")
|
||||
|
||||
if len(user.UserName) == 0 || len(user.FirstName) == 0 ||
|
||||
len(user.LastName) == 0 || len(pass) == 0 || len(pass2) == 0 {
|
||||
if len(user.UserName) == 0 || len(user.FirstName) == 0 || len(user.LastName) == 0 || len(user.Email) == 0 || len(email2) == 0 || len(pass) == 0 || len(pass2) == 0 {
|
||||
http.Error(w, "Bitte alle Felder ausfüllen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@ -76,6 +77,11 @@ func AddUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if user.Email != email2 {
|
||||
http.Error(w, "Die Emailadressen stimmen nicht überein.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if pass != pass2 {
|
||||
http.Error(w, "Die Passwörter stimmen nicht überein.", http.StatusBadRequest)
|
||||
return
|
||||
@ -94,16 +100,19 @@ func AddUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = db.AddUser(user, pass)
|
||||
_, err = db.AddUser(c, user, pass)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -120,7 +129,7 @@ func EditSelf(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := db.GetUser(session.Values["id"].(int64))
|
||||
user, err := db.GetUser(c, session.Values["id"].(int64))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@ -150,11 +159,13 @@ func UpdateSelf(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
UserName: r.PostFormValue("username"),
|
||||
FirstName: r.PostFormValue("first-name"),
|
||||
LastName: r.PostFormValue("last-name"),
|
||||
Email: r.PostFormValue("email"),
|
||||
}
|
||||
|
||||
oldPass := r.PostFormValue("old-password")
|
||||
newPass := r.PostFormValue("password")
|
||||
newPass2 := r.PostFormValue("password2")
|
||||
email2 := r.PostFormValue("email2")
|
||||
|
||||
if len(user.UserName) == 0 {
|
||||
http.Error(w, "Bitte den Benutzernamen ausfüllen.", http.StatusBadRequest)
|
||||
@ -166,6 +177,20 @@ func UpdateSelf(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if len(user.Email) == 0 || len(email2) == 0 {
|
||||
http.Error(w, "Bitte die Emailadresse ausfüllen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if user.Email != email2 {
|
||||
http.Error(w, "Die Emailadressen stimmen nicht überein", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if newPass != newPass2 {
|
||||
http.Error(w, "Die Passwörter stimmen nicht überein", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
userString, stringLen, ok := checkUserStrings(user)
|
||||
if !ok {
|
||||
http.Error(w, fmt.Sprint(userString, " ist zu lang. Maximal ", stringLen, " Zeichen erlaubt."), http.StatusBadRequest)
|
||||
@ -177,15 +202,18 @@ func UpdateSelf(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if err = db.UpdateOwnUserAttributes(user.ID, user.UserName, user.FirstName, user.LastName, oldPass, newPass, newPass2); err != nil {
|
||||
if err = db.UpdateOwnUserAttributes(c, user.ID, user.UserName, user.FirstName, user.LastName, user.Email, oldPass, newPass); err != nil {
|
||||
log.Println("error: user:", user.ID, err)
|
||||
http.Error(w, "Benutzerdaten konnten nicht aktualisiert werden.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -200,13 +228,14 @@ func AddFirstUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
UserName: r.PostFormValue("username"),
|
||||
FirstName: r.PostFormValue("first-name"),
|
||||
LastName: r.PostFormValue("last-name"),
|
||||
Email: r.PostFormValue("email"),
|
||||
Role: b.Admin,
|
||||
}
|
||||
pass := r.PostFormValue("password")
|
||||
pass2 := r.PostFormValue("password2")
|
||||
email2 := r.PostFormValue("email2")
|
||||
|
||||
if len(user.UserName) == 0 || len(user.FirstName) == 0 ||
|
||||
len(user.LastName) == 0 || len(pass) == 0 || len(pass2) == 0 {
|
||||
if len(user.UserName) == 0 || len(user.FirstName) == 0 || len(user.LastName) == 0 || len(user.Email) == 0 || len(email2) == 0 || len(pass) == 0 || len(pass2) == 0 {
|
||||
http.Error(w, "Bitte alle Felder ausfüllen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@ -217,12 +246,17 @@ func AddFirstUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if user.Email != email2 {
|
||||
http.Error(w, "Die Emailadressen stimmen nicht überein.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if pass != pass2 {
|
||||
http.Error(w, "Die Passwörter stimmen nicht überein.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
user.ID, err = db.AddFirstUser(user, pass)
|
||||
user.ID, err = db.AddFirstUser(c, user, pass)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@ -245,8 +279,11 @@ func AddFirstUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = 0
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", 0); err != nil {
|
||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -269,7 +306,7 @@ func ShowAllUsers(c *b.Config, db *b.DB, s *b.CookieStore, action string) http.H
|
||||
})
|
||||
|
||||
data.Action = action
|
||||
data.Users, err = db.GetAllUsers()
|
||||
data.Users, err = db.GetAllUsers(c)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@ -301,7 +338,7 @@ func EditUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := db.GetUser(id)
|
||||
user, err := db.GetUser(c, id)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@ -343,19 +380,34 @@ func UpdateUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
|
||||
user.UserName = r.PostFormValue("username")
|
||||
if len(user.UserName) == 0 {
|
||||
http.Error(w, "Bitte den Benutzernamen ausfüllen.", http.StatusInternalServerError)
|
||||
http.Error(w, "Bitte den Benutzernamen ausfüllen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
user.FirstName = r.PostFormValue("first-name")
|
||||
user.LastName = r.PostFormValue("last-name")
|
||||
if len(user.FirstName) == 0 || len(user.LastName) == 0 {
|
||||
http.Error(w, "Bitte den vollständigen Namen ausfüllen.", http.StatusInternalServerError)
|
||||
http.Error(w, "Bitte den vollständigen Namen ausfüllen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
user.Email = r.PostFormValue("email")
|
||||
email2 := r.PostFormValue("email2")
|
||||
if len(user.Email) == 0 || len(email2) == 0 {
|
||||
http.Error(w, "Bitte die Emailadresse ausfüllen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if user.Email != email2 {
|
||||
http.Error(w, "Die Emailadressen stimmen nicht überein.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
newPass := r.PostFormValue("password")
|
||||
newPass2 := r.PostFormValue("password2")
|
||||
if newPass != newPass2 {
|
||||
http.Error(w, "Die Passwörter stimmen nicht überein.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
userString, stringLen, ok := checkUserStrings(user)
|
||||
if !ok {
|
||||
@ -368,14 +420,17 @@ func UpdateUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if err = db.UpdateUserAttributes(user.ID, user.UserName, user.FirstName, user.LastName, newPass, newPass2, user.Role); err != nil {
|
||||
if err = db.UpdateUserAttributes(c, user.ID, user.UserName, user.FirstName, user.LastName, user.Email, newPass, user.Role); err != nil {
|
||||
log.Println("error: user:", user.ID, err)
|
||||
http.Error(w, "Benutzerdaten konnten nicht aktualisiert werden.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl := template.Must(template.ParseFiles(c.WebDir + "/templates/hub.html"))
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -405,9 +460,12 @@ func DeleteUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -34,13 +34,13 @@ func main() {
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
key, err := b.LoadKey(config.KeyFile)
|
||||
key, err := b.LoadKey(config.GOBKeyFile)
|
||||
if err != nil {
|
||||
key, err = b.NewKey()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if err = b.SaveKey(key, config.KeyFile); err != nil {
|
||||
if err = b.SaveKey(key, config.GOBKeyFile); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
@ -72,7 +72,6 @@ func main() {
|
||||
mux.HandleFunc("GET /logout", f.Logout(config, store))
|
||||
mux.HandleFunc("GET /pdf/get-list", c.ServePDFList(config))
|
||||
mux.HandleFunc("GET /pdf/serve/{id}", c.ServePDF(config))
|
||||
mux.HandleFunc("GET /rss/serve", c.ServeRSS(config))
|
||||
mux.HandleFunc("GET /tag/create", f.CreateTag(config, store))
|
||||
mux.HandleFunc("GET /user/create", f.CreateUser(config, store))
|
||||
mux.HandleFunc("GET /user/delete/{id}", f.DeleteUser(config, db, store))
|
||||
|
@ -5,12 +5,14 @@ DROP TABLE IF EXISTS issues;
|
||||
DROP TABLE IF EXISTS users;
|
||||
|
||||
CREATE TABLE users (
|
||||
id INT AUTO_INCREMENT,
|
||||
username VARCHAR(15) NOT NULL UNIQUE,
|
||||
password VARCHAR(60) NOT NULL,
|
||||
first_name VARCHAR(50) NOT NULL,
|
||||
last_name VARCHAR(50) NOT NULL,
|
||||
role INT NOT NULL,
|
||||
id INT AUTO_INCREMENT,
|
||||
username VARCHAR(255) NOT NULL UNIQUE,
|
||||
password VARCHAR(60) NOT NULL,
|
||||
first_name VARCHAR(255) NOT NULL,
|
||||
last_name VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
-- profile_pic_link VARCHAR(255) NOT NULL,
|
||||
role INT NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
@ -24,12 +26,9 @@ CREATE TABLE articles (
|
||||
id INT AUTO_INCREMENT,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
banner_link VARCHAR(255),
|
||||
banner_link VARCHAR(255) NOT NULL,
|
||||
summary TEXT NOT NULL,
|
||||
content_link VARCHAR(255),
|
||||
enc_url VARCHAR(255),
|
||||
enc_length INT,
|
||||
enc_type VARCHAR(255),
|
||||
content_link VARCHAR(255) NOT NULL,
|
||||
published BOOL NOT NULL,
|
||||
rejected BOOL NOT NULL,
|
||||
author_id INT NOT NULL,
|
||||
|
@ -32,11 +32,11 @@ textarea {
|
||||
}
|
||||
|
||||
.btn {
|
||||
@apply bg-slate-200 dark:bg-slate-800 hover:bg-slate-100 dark:hover:bg-slate-900 border border-slate-200 dark:border-slate-800 my-2 px-3 py-2 rounded-md w-full;
|
||||
@apply bg-slate-200 dark:bg-slate-800 hover:bg-slate-100 dark:hover:bg-slate-900 border border-slate-200 dark:border-slate-800 cursor-pointer my-2 px-3 py-2 rounded-md w-full;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
@apply bg-slate-800 dark:bg-slate-200 hover:bg-slate-700 dark:hover:bg-slate-300 my-2 px-3 py-2 rounded-md text-slate-50 dark:text-slate-950 w-full;
|
||||
@apply bg-slate-800 dark:bg-slate-200 hover:bg-slate-700 dark:hover:bg-slate-300 cursor-pointer my-2 px-3 py-2 rounded-md text-slate-50 dark:text-slate-950 w-full;
|
||||
}
|
||||
|
||||
.EasyMDEContainer .CodeMirror {
|
||||
|
@ -7,22 +7,36 @@
|
||||
<label for="username">Benutzername</label>
|
||||
<input class="w-full" required name="username" type="text" value="{{.UserName}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password">Passwort</label>
|
||||
<input class="w-full" required name="password" placeholder="***" type="password" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password2">Passwort wiederholen</label>
|
||||
<input class="w-full" required name="password2" placeholder="***" type="password" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="first-name">Vorname</label>
|
||||
<input class="w-full" required name="first-name" type="text" value="{{.FirstName}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="last-name">Nachname</label>
|
||||
<input class="w-full" required name="last-name" type="text" value="{{.LastName}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email">Emailadresse</label>
|
||||
<input class="w-full" required name="email" type="text" value="{{.Email}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email2">Emailadresse wiederholen</label>
|
||||
<input class="w-full" required name="email2" type="text" value="{{.Email}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-4">
|
||||
|
@ -13,7 +13,7 @@
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 items-center">
|
||||
<label class="btn cursor-pointer text-center" for="issue-banner-upload">Titelbild</label>
|
||||
<label class="btn text-center" for="issue-banner-upload">Titelbild</label>
|
||||
<input class="hidden" id="issue-banner-upload" name="issue-banner" type="file" required
|
||||
hx-post="/issue/upload-banner" hx-target="#issue-banner-container" />
|
||||
</div>
|
||||
|
@ -32,6 +32,16 @@
|
||||
<label for="password2">Passwort wiederholen</label>
|
||||
<input class="w-full" name="password2" placeholder="***" type="password" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email">Emailadresse</label>
|
||||
<input class="w-full" required name="email" type="text" value="{{.Email}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email2">Emailadresse wiederholen</label>
|
||||
<input class="w-full" required name="email2" type="text" value="{{.Email}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-area">
|
||||
|
@ -27,6 +27,16 @@
|
||||
<label for="password2">Passwort wiederholen</label>
|
||||
<input class="w-full" name="password2" placeholder="***" type="password" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email">Emailadresse</label>
|
||||
<input class="w-full" required name="email" type="text" value="{{.Email}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email2">Emailadresse wiederholen</label>
|
||||
<input class="w-full" required name="email2" type="text" value="{{.Email}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-4">
|
||||
|
@ -15,7 +15,7 @@
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 items-center">
|
||||
<label class="btn cursor-pointer text-center" for="article-banner">Titelbild</label>
|
||||
<label class="btn text-center" for="article-banner">Titelbild</label>
|
||||
<input class="hidden" id="article-banner" name="article-banner" type="file" required
|
||||
hx-post="/article/upload-banner" hx-target="#article-banner-container" />
|
||||
</div>
|
||||
|
@ -5,23 +5,37 @@
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label for="username">Benutzername</label>
|
||||
<input class="w-full" required name="username" type="text" value="{{.UserName}}" />
|
||||
<input class="w-full" required name="username" type="text" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password">Passwort</label>
|
||||
<input class="w-full" required name="password" placeholder="***" type="password" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password2">Passwort wiederholen</label>
|
||||
<input class="w-full" required name="password2" placeholder="***" type="password" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="first-name">Vorname</label>
|
||||
<input class="w-full" required name="first-name" type="text" value="{{.FirstName}}" />
|
||||
<input class="w-full" required name="first-name" type="text" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="last-name">Nachname</label>
|
||||
<input class="w-full" required name="last-name" type="text" value="{{.LastName}}" />
|
||||
<input class="w-full" required name="last-name" type="text" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email">Emailadresse</label>
|
||||
<input class="w-full" required name="email" type="text" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email2">Emailadresse wiederholen</label>
|
||||
<input class="w-full" required name="email2" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -2,30 +2,31 @@
|
||||
<div class="flex flex-col gap-4">
|
||||
<button class="btn" hx-get="/logout" hx-target="#page-content">Abmelden</button>
|
||||
|
||||
{{if lt . 4}}
|
||||
{{if lt .Role 4}}
|
||||
<div class="mb-3">
|
||||
<h2>Artikel</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2">
|
||||
<button class="btn" hx-get="/article/write" hx-target="#page-content">Artikel schreiben</button>
|
||||
<button class="btn" hx-get="/article/all-rejected" hx-target="#page-content">Artikel bearbeiten</button>
|
||||
{{if lt . 3}}<button class="btn" hx-get="/article/all-unpublished-unrejected-and-published-rejected"
|
||||
{{if lt .Role 3}}<button class="btn" hx-get="/article/all-unpublished-unrejected-and-published-rejected"
|
||||
hx-target="#page-content">Artikel veröffentlichen</button>{{end}}
|
||||
{{if lt . 2}}<button class="btn" hx-get="/article/all-published/delete" hx-target="#page-content">Artikel
|
||||
löschen</button>{{end}}
|
||||
{{if lt . 2}}<button class="btn" hx-get="/article/all-published/review-edit"
|
||||
{{if lt .Role 2}}<button class="btn" hx-get="/article/all-published/delete"
|
||||
hx-target="#page-content">Artikel löschen</button>{{end}}
|
||||
{{if lt .Role 2}}<button class="btn" hx-get="/article/all-published/review-edit"
|
||||
hx-target="#page-content">Artikel bearbeiten lassen</button>{{end}}
|
||||
{{if lt . 3}}<button class="btn" hx-get="/tag/create" hx-target="#page-content">Neuer Tag</button>{{end}}
|
||||
{{if lt .Role 3}}<button class="btn" hx-get="/tag/create" hx-target="#page-content">Neuer
|
||||
Tag</button>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if lt . 2}}
|
||||
{{if lt .Role 2}}
|
||||
<div class="mb-3">
|
||||
<h2>Ausgabe</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2">
|
||||
<button class="btn" hx-get="/issue/this" hx-target="#page-content">Diese Ausgabe</button>
|
||||
<form class="flex" hx-encoding="multipart/form-data">
|
||||
<label class="btn cursor-pointer text-center" for="pdf-upload">PDF hochladen</label>
|
||||
<label class="btn text-center" for="pdf-upload">PDF hochladen</label>
|
||||
<input accept=".pdf" class="hidden" id="pdf-upload" name="pdf-upload" type="file"
|
||||
hx-post="/pdf/upload" />
|
||||
</form>
|
||||
@ -33,16 +34,16 @@
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if lt . 4}}
|
||||
{{if lt .Role 4}}
|
||||
<div class="mb-3">
|
||||
<h2>Benutzer</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2">
|
||||
<button class="btn" hx-get="/user/edit/self" hx-target="#page-content">Mein Profil bearbeiten</button>
|
||||
{{if eq . 0}}<button class="btn" hx-get="/user/create" hx-target="#page-content">Benutzer
|
||||
{{if eq .Role 0}}<button class="btn" hx-get="/user/create" hx-target="#page-content">Benutzer
|
||||
hinzufügen</button>{{end}}
|
||||
{{if eq . 0}}<button class="btn" hx-get="/user/show-all/edit" hx-target="#page-content">Benutzer
|
||||
{{if eq .Role 0}}<button class="btn" hx-get="/user/show-all/edit" hx-target="#page-content">Benutzer
|
||||
bearbeiten</button>{{end}}
|
||||
{{if eq . 0}}<button class="btn" hx-get="/user/show-all/delete" hx-target="#page-content">Benutzer
|
||||
{{if eq .Role 0}}<button class="btn" hx-get="/user/show-all/delete" hx-target="#page-content">Benutzer
|
||||
löschen</button>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
<footer class="text-center text-gray-500 my-8">
|
||||
<p>© 2024 Jason Streifling. Alle Rechte vorbehalten.</p>
|
||||
<p>v0.13.0 - <strong>Alpha: Drastische Änderungen und Fehler vorbehalten.</strong></p>
|
||||
<p>{{.Version}} - <strong>Alpha: Drastische Änderungen und Fehler vorbehalten.</strong></p>
|
||||
</footer>
|
||||
|
||||
<script src="https://unpkg.com/htmx.org@2.0.2"></script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user