diff --git a/comics.go b/comics.go index bb61631..fd884af 100644 --- a/comics.go +++ b/comics.go @@ -136,6 +136,8 @@ func (c *Comic) readRow(db * sql.Rows) error { } func getComic(id int) (*Comic, error) { + id -= 1 + c := new(Comic) nferr := NoSuchComicErr.With(fmt.Sprintf("id \"%d\"", id)) @@ -236,7 +238,7 @@ func comicView(w http.ResponseWriter, r * http.Request) { } if len(comics) > 0 { - c, err = getComic(i-1) + c, err = getComic(i) if err != nil { errlog.Println(err) return404(w,r) @@ -400,10 +402,19 @@ func return500(w http.ResponseWriter, r * http.Request) { returnError(w, r, http.StatusInternalServerError) } -func newComic(title string, image string, description string, tags string) error { - _, err := db.Exec("INSERT INTO comic(title, image, description, tags) VALUES(?, ?, ?, ?);", +func newComic(title string, image string, description string, tags string) (int, error) { + res, err := db.Exec("INSERT INTO comic(title, image, description, tags) VALUES(?, ?, ?, ?);", title, image, description, tags) - return err + if err != nil { + return -1, nil + } + + id := int64(0) + if id, err = res.LastInsertId(); err != nil { + return 0, err + } + + return int(id), err } func deleteComic(id int) error { @@ -437,7 +448,7 @@ func main() { if len(options.Title) < 1 { panic("missing -l") } - err = newComic(options.Title, options.ImagePath, "", "") + _, err = newComic(options.Title, options.ImagePath, "", "") if err != nil { log.Fatal(err) } diff --git a/manager.go b/manager.go index 3ca3180..38797fd 100644 --- a/manager.go +++ b/manager.go @@ -20,9 +20,10 @@ import ( "golang.org/x/crypto/bcrypt" ) -var seededRand *rand.Rand = nil -var secretKey string = "" -var secretKeyHash string = "" +var seededRand *rand.Rand = nil +var secretKey string = "" +var secretKeyHash string = "" +var tokenSecondsDefault = 60 * 60 * 24 * 14 const userSquema string = ` CREATE TABLE IF NOT EXISTS user ( @@ -38,6 +39,13 @@ CREATE TABLE IF NOT EXISTS token ( username CHAR(20) NOT NULL );` +const isAuthorOfSquema string = ` +CREATE TABLE IF NOT EXISTS isauthorof ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + comicid INT NOT NULL, + userid CHAR(20) NOT NULL +);` + type User struct { ID string Name string @@ -63,12 +71,107 @@ type TokenClaims struct { Username string } +type IsAuthorOf struct { + ID int + ComicID int + UserID string +} + type ViewHandler func(w http.ResponseWriter, r *http.Request) +func (u *User) byID(id string) error { + rows, err := db.Query("SELECT * FROM user WHERE id = ?;", id) + if err != nil { + return err + } + defer rows.Close() + + if !rows.Next() { + return errors.New("no such entry found") + } + + return u.readRow(rows) +} + +func (a *IsAuthorOf) byComic(id int) error { + rows, err := db.Query("SELECT * FROM isauthorof WHERE comicid = ?;", id) + if err != nil { + return err + } + defer rows.Close() + + if !rows.Next() { + return errors.New("no such entry found") + } + + return a.readRow(rows) +} + +func (c *Comic) getAuthor() (*User, error) { + authorOf := &IsAuthorOf{} + user := &User{} + + err := authorOf.byComic(c.ID) + if err != nil { + return nil, err + } + + err = user.byID(authorOf.UserID) + if err != nil { + return nil, err + } + + return user, nil +} + func (u *User) readRow(db * sql.Rows) error { return db.Scan(&u.ID, &u.Name, &u.Password) } +func getComicByID(id int) (*Comic, error) { + comic := &Comic{} + + rows, err := db.Query("SELECT * FROM comic WHERE id = ?;", id) + if err != nil { + return nil, err + } + defer rows.Close() + + if !rows.Next() { + return nil, errors.New("no such comic found") + } + + err = comic.readRow(rows) + if err != nil { + return nil, err + } + + return comic, nil +} + +func (u *User) getComics() ([]Comic, error) { + authors, err := getAllIsAuthorOfs() + if err != nil { + return nil, err + } + + comics := []Comic{} + for _, a := range authors { + if a.UserID != u.ID { + continue + } + + c, err := getComicByID(a.ComicID) + if err != nil { + continue + } + + comics = append(comics, *c) + } + + return comics, nil +} + func (t *TokenClaims) readRow(db * sql.Rows) error { var e int64 err := db.Scan(&t.ID, &e, &t.Username) @@ -76,6 +179,10 @@ func (t *TokenClaims) readRow(db * sql.Rows) error { return err } +func (a *IsAuthorOf) readRow(db *sql.Rows) error { + return db.Scan(&a.ID, &a.ComicID, &a.UserID) +} + func generateToken(username string, seconds time.Duration) (string, *TokenClaims, error) { claims := TokenClaims{ ID: randomString(20), @@ -221,6 +328,53 @@ func getAllUsers() ([]User, error) { return users, nil } +func getAllIsAuthorOfs() ([]IsAuthorOf, error) { + rows, err := db.Query("SELECT * FROM isauthorof ORDER BY id ASC") + if err != nil { + return nil, err + } + defer rows.Close() + + authors := []IsAuthorOf{} + for rows.Next() { + i := IsAuthorOf{} + err := i.readRow(rows) + if err != nil { + return nil, err + } + authors = append(authors, i) + } + + return authors, nil +} + +func addIsAuthorOf(userID string, comicID int) error { + authors, err := getAllIsAuthorOfs() + if err != nil { + return err + } + + for _, a := range authors { + if a.ComicID == comicID && a.UserID == userID { + return nil + } + } + + _, err = db.Exec("INSERT INTO isauthorof(comicid, userid) VALUES(?, ?);", + comicID, userID) + return err +} + +func removeComicFromIsAuthorOf(id int) error { + _, err := db.Exec("DELETE FROM isauthorof WHERE comicid = ?;", id) + return err +} + +func removeUserFromIsAuthorOf(id string) error { + _, err := db.Exec("DELETE FROM isauthorof WHERE userid = ?;", id) + return err +} + func randomString(length int) string { charset := "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789" b := make([]byte, length) @@ -296,7 +450,6 @@ func athenticateUserByPassword(username string, password string) (*User, error) return nil, errors.New("No such user \"" + username + "\" found") } - log.Println(user.ID) password = saltPassword(password, user.ID) if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil { return nil, err @@ -335,12 +488,12 @@ func managerIndexView(w http.ResponseWriter, r * http.Request) { User: *user, } - comics, err := allComics() + comics, err := user.getComics() if err != nil { return } - for i, comic := range comics { + for _, comic := range comics { context.Comics = append(context.Comics, struct { No int Title string @@ -348,7 +501,7 @@ func managerIndexView(w http.ResponseWriter, r * http.Request) { Image string }{ Title: comic.Title, - No: i+1, + No: comic.ID, Date: comic.DateTime, Image: comic.Image, }) @@ -364,18 +517,39 @@ func managerRemoveView(w http.ResponseWriter, r * http.Request) { path := strings.TrimRight(tmp, "/confirmed") isConfirmed := strings.TrimPrefix(strings.TrimPrefix(tmp, path), "/") == "confirmed" + user, _, err := getSessionUser(r) + if err != nil { + log.Println("failed to get session user: " + err.Error()) + return401(w, r) + err = nil + return + } + i, err := strconv.Atoi(path) if err != nil { return404(w, r) return } - comic, err := getComic(i-1) + comic, err := getComicByID(i) if err != nil { return404(w, r) return } + authorOf := &IsAuthorOf{} + err = authorOf.byComic(i) + if err != nil { + errlog.Println(err) + return500(w, r) + return + } + + if authorOf.UserID != user.ID { + return401(w, r) + return + } + if !isConfirmed { err = executeTemplate(w, "confirm", ConfirmContext{ Action: fmt.Sprintf("Remove %s", comic.Title), @@ -396,6 +570,11 @@ func managerRemoveView(w http.ResponseWriter, r * http.Request) { return500(w, r) return } + err = removeComicFromIsAuthorOf(comic.ID) + if err != nil { + return500(w, r) + return + } http.Redirect(w, r, "/", http.StatusSeeOther) } @@ -419,6 +598,14 @@ func managerPublishView(w http.ResponseWriter, r * http.Request) { } } () + user, _, err := getSessionUser(r) + if err != nil { + log.Println("failed to get session user: " + err.Error()) + return401(w, r) + err = nil + return + } + r.ParseMultipartForm(options.UploadSize * (1 << 20)) file, handler, err := r.FormFile("image") if err != nil { @@ -440,7 +627,13 @@ func managerPublishView(w http.ResponseWriter, r * http.Request) { defer f.Close() io.Copy(f, file) - err = newComic(title, filename, "", "") + comicID, err := newComic(title, filename, "", "") + if err != nil { + return + } + + log.Println(user.ID) + err = addIsAuthorOf(user.ID, comicID) if err != nil { return } @@ -478,7 +671,7 @@ func managerLoginView(w http.ResponseWriter, r * http.Request) { return } - token, claims, err := generateToken(user.Name, 60) + token, claims, err := generateToken(user.Name, time.Duration(tokenSecondsDefault)) if err != nil { log.Println(fmt.Sprintf("failed to generate token for \"%s\": %s", user.Name, err.Error())) return500(w, r) @@ -536,6 +729,11 @@ func startManager(address string, port int, dbPath string, mediaPath string) err log.Fatal(err) } + _, err = db.Exec(isAuthorOfSquema) + if err != nil { + log.Fatal(err) + } + _, err = os.Stat(options.SecretPath) if os.IsNotExist(err) { secretKey = randomString(100)