diff --git a/admin/admin.go b/admin/admin.go index d667096..f4d3fe0 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -7,32 +7,34 @@ package admin import ( + "database/sql" + "encoding/json" "fmt" + "golang.org/x/crypto/bcrypt" "io" "io/ioutil" "net/http" "os" "strings" - "encoding/json" - "golang.org/x/crypto/bcrypt" - "database/sql" - _ "github.com/mattn/go-sqlite3" "github.com/gorilla/mux" + _ "github.com/mattn/go-sqlite3" "github.com/gmemstr/pogo/common" ) + type User struct { - Id int `json:"id"` - Dbun string `json:"username"` - Dbrn string `json:"realname"` - Dbem string `json:"email"` + Id int `json:"id"` + Dbun string `json:"username"` + Dbrn string `json:"realname"` + Dbem string `json:"email"` } type UserList struct { Users []User } + /* - * The following is a set of admin commands + * The following is a set of admin commands * that the average user probably shouldn't be * able to have access to, mostly user management. */ @@ -48,7 +50,7 @@ func AddUser() common.Handler { StatusCode: http.StatusInternalServerError, } } - statement, err := db.Prepare("INSERT INTO users(username,hash,realname,email) VALUES (?,?,?,?)") + statement, err := db.Prepare("INSERT INTO users(username,hash,realname,email,permissions) VALUES (?,?,?,?,?)") if err != nil { return &common.HTTPError{ Message: fmt.Sprintf("error preparing sqlite3 statement: %v", err), @@ -68,10 +70,11 @@ func AddUser() common.Handler { password := strings.Join(r.Form["password"], "") realname := strings.Join(r.Form["realname"], "") email := strings.Join(r.Form["email"], "") + permissions := strings.Join(r.Form["permissions"], "") hash, err := bcrypt.GenerateFromPassword([]byte(password), 4) - _, err = statement.Exec(username,hash,realname,email) + _, err = statement.Exec(username, hash, realname, email, permissions) if err != nil { return &common.HTTPError{ Message: fmt.Sprintf("error executing sqlite3 statement: %v", err), @@ -110,9 +113,10 @@ func EditUser() common.Handler { newpassword := strings.Join(r.Form["newpw1"], "") realname := strings.Join(r.Form["realname"], "") email := strings.Join(r.Form["email"], "") + permissions := strings.Join(r.Form["permissions"], "") pwhash, err := bcrypt.GenerateFromPassword([]byte(password), 4) - statement, err := db.Prepare("UPDATE users SET username=?, hash=?, realname=?, email=? WHERE id=?") + statement, err := db.Prepare("UPDATE users SET username=?, hash=?, realname=?, email=?, permissions=? WHERE id=?") if err != nil { return &common.HTTPError{ Message: fmt.Sprintf("error preparing sqlite3 statement: %v", err), @@ -145,7 +149,7 @@ func EditUser() common.Handler { Message: fmt.Sprintf("error executing sqlite3 statement: %v", err), StatusCode: http.StatusInternalServerError, } - } + } } fmt.Println(hash) if bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) != nil { @@ -158,9 +162,9 @@ func EditUser() common.Handler { if newpassword != "" { pwhash, err = bcrypt.GenerateFromPassword([]byte(newpassword), 4) - } + } - _, err = statement.Exec(username,pwhash,realname,email,id) + _, err = statement.Exec(username, pwhash, realname, email, id, permissions) if err != nil { return &common.HTTPError{ Message: fmt.Sprintf("error executing sqlite3 statement: %v", err), @@ -328,15 +332,15 @@ func EditEpisode() common.Handler { fmt.Println(filename) description := strings.Join(r.Form["description"], "") - if ("./podcasts" + PreviousFilename + ".mp3" != filename) { - err = os.Rename("./podcasts/" + PreviousFilename + ".mp3", filename) + if "./podcasts"+PreviousFilename+".mp3" != filename { + err = os.Rename("./podcasts/"+PreviousFilename+".mp3", filename) if err != nil { return &common.HTTPError{ Message: err.Error(), StatusCode: http.StatusBadRequest, } } - err = os.Rename("./podcasts/" + PreviousFilename + "_SHOWNOTES.md", shownotes) + err = os.Rename("./podcasts/"+PreviousFilename+"_SHOWNOTES.md", shownotes) if err != nil { return &common.HTTPError{ Message: err.Error(), diff --git a/assets/config/users.db b/assets/config/users.db index ce23fae..7b05088 100644 Binary files a/assets/config/users.db and b/assets/config/users.db differ diff --git a/assets/web/static/app.js b/assets/web/static/app.js index 806cab8..b4013ff 100644 --- a/assets/web/static/app.js +++ b/assets/web/static/app.js @@ -91,6 +91,12 @@ const usernew = { + +

@@ -205,6 +211,7 @@ const episodemanagement = { } else { var t = JSON.parse(items).items for (var i = t.length - 1; i >= 0; i--) { + console.log(i) this.items.push({ title: t[i].title, url: t[i].url, @@ -312,10 +319,14 @@ const customcss = { get("/admin/css", (err, css) => { this.loading = false - if (err) { - this.error = err.toString() + if (css == "{}") { + this.css = "You aren't allowed to edit this CSS!" } else { - this.css = css + if (err) { + this.error = err.toString() + } else { + this.css = css + } } }) } @@ -348,8 +359,13 @@ const app = new Vue({ function get(url,callback) { var xmlHttp = new XMLHttpRequest(); xmlHttp.onreadystatechange = function() { - if (xmlHttp.readyState == 4 && xmlHttp.status == 200) - callback(null, xmlHttp.responseText) + if (xmlHttp.readyState == 4 && xmlHttp.status == 200) { + if (xmlHttp.responseText == "Unauthorized") { + callback(null, "{}") + } else { + callback(null, xmlHttp.responseText) + } + } } xmlHttp.open("GET", url, true); xmlHttp.send(null); diff --git a/auth/auth.go b/auth/auth.go index a76db02..19b356c 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -6,6 +6,7 @@ import ( "crypto/hmac" "crypto/rand" "crypto/sha256" + "database/sql" "encoding/base64" "encoding/hex" "encoding/json" @@ -13,8 +14,10 @@ import ( "fmt" "log" "net/http" - "strings" "os" + "strings" + + _ "github.com/mattn/go-sqlite3" "github.com/gmemstr/pogo/common" ) @@ -27,7 +30,39 @@ const ( cookieExpiry = 60 * 60 * 24 * 30 // 30 days in seconds ) -func RequireAuthorization() common.Handler { +func UserPermissions(username string, permission int) (bool, error) { + + db, err := sql.Open("sqlite3", "assets/config/users.db") + defer db.Close() + isAllowed := false + if err != nil { + return isAllowed, err + } + + statement, err := db.Prepare("SELECT permissions FROM users WHERE username=?") + if err != nil { + return isAllowed, err + } + + rows, err := statement.Query(username) + if err != nil { + return isAllowed, err + } + + var level int + for rows.Next() { + err = rows.Scan(&level) + if err != nil { + return isAllowed, err + } + if level >= permission { + isAllowed = true + } + } + return isAllowed, nil +} + +func RequireAuthorization(permission int) common.Handler { return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError { usr, err := DecryptCookie(r) if err != nil { @@ -46,6 +81,17 @@ func RequireAuthorization() common.Handler { } rc.User = usr + + username := rc.User.Username + + hasPermission, err := UserPermissions(string(username), permission) + + if !hasPermission { + return &common.HTTPError{ + Message: "Unauthorized! Redirecting to /admin", + StatusCode: http.StatusUnauthorized, + } + } return nil } } diff --git a/router/router.go b/router/router.go index 0fe53aa..8a0fdeb 100644 --- a/router/router.go +++ b/router/router.go @@ -1,21 +1,21 @@ package router import ( + "database/sql" "encoding/json" "fmt" + "golang.org/x/crypto/bcrypt" "io/ioutil" "log" "net/http" "strings" - "golang.org/x/crypto/bcrypt" - "database/sql" _ "github.com/mattn/go-sqlite3" - "github.com/gorilla/mux" "github.com/gmemstr/pogo/admin" "github.com/gmemstr/pogo/auth" "github.com/gmemstr/pogo/common" + "github.com/gorilla/mux" ) type NewConfig struct { @@ -70,7 +70,7 @@ func Init() *mux.Router { // Authenticated endpoints should be passed to BasicAuth() // first r.Handle("/admin", Handle( - auth.RequireAuthorization(), + auth.RequireAuthorization(0), adminHandler(), )).Methods("GET", "POST") @@ -79,45 +79,45 @@ func Init() *mux.Router { )).Methods("GET", "POST") r.Handle("/admin/publish", Handle( - auth.RequireAuthorization(), + auth.RequireAuthorization(0), admin.CreateEpisode(), )).Methods("POST") r.Handle("/admin/edituser", Handle( - auth.RequireAuthorization(), + auth.RequireAuthorization(2), admin.EditUser(), )).Methods("POST") r.Handle("/admin/newuser", Handle( - auth.RequireAuthorization(), + auth.RequireAuthorization(2), admin.AddUser(), )).Methods("POST") r.Handle("/admin/deleteuser/{id}", Handle( - auth.RequireAuthorization(), + auth.RequireAuthorization(2), admin.DeleteUser(), )).Methods("GET") r.Handle("/admin/edit", Handle( - auth.RequireAuthorization(), + auth.RequireAuthorization(1), admin.EditEpisode(), )).Methods("POST") r.Handle("/admin/delete", Handle( - auth.RequireAuthorization(), + auth.RequireAuthorization(1), admin.RemoveEpisode(), )).Methods("GET") r.Handle("/admin/css", Handle( - auth.RequireAuthorization(), + auth.RequireAuthorization(1), admin.CustomCss(), )).Methods("GET", "POST") r.Handle("/admin/adduser", Handle( - auth.RequireAuthorization(), + auth.RequireAuthorization(2), admin.AddUser(), )).Methods("POST") r.Handle("/admin/listusers", Handle( - auth.RequireAuthorization(), + auth.RequireAuthorization(1), admin.ListUsers(), )).Methods("GET") @@ -169,13 +169,14 @@ func loginHandler() common.Handler { StatusCode: http.StatusBadRequest, } } - var id int - var dbun string + var id int + var dbun string var dbhsh string - var dbrn string - var dbem string + var dbrn string + var dbem string + var dbperm int for rows.Next() { - err := rows.Scan(&id,&dbun,&dbhsh,&dbrn,&dbem) + err := rows.Scan(&id, &dbun, &dbhsh, &dbrn, &dbem, &dbperm) if err != nil { return &common.HTTPError{ Message: fmt.Sprintf("error in decoding sql data", err), @@ -186,7 +187,7 @@ func loginHandler() common.Handler { } // Create a cookie here because the credentials are correct if dbun == username && bcrypt.CompareHashAndPassword([]byte(dbhsh), []byte(password)) == nil { - c, err := auth.CreateSession(&common.User{ + c, err := auth.CreateSession(&common.User{ Username: username, }) if err != nil { @@ -204,7 +205,6 @@ func loginHandler() common.Handler { return nil } - return &common.HTTPError{ Message: "Invalid credentials!", StatusCode: http.StatusUnauthorized,