Skip to content
Snippets Groups Projects
Commit b166a642 authored by Philipp Hochkamp's avatar Philipp Hochkamp
Browse files

added autoreload of the leaderboard

closes #30
parent 6f29769d
No related branches found
No related tags found
No related merge requests found
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
### Go ### ### Go ###
# Binaries for programs and plugins # Binaries for programs and plugins
wtfd
*.exe *.exe
*.exe~ *.exe~
*.dll *.dll
......
...@@ -3,6 +3,7 @@ package wtfd ...@@ -3,6 +3,7 @@ package wtfd
import ( import (
"errors" "errors"
"fmt" "fmt"
"golang.org/x/crypto/bcrypt"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
_ "github.com/mattn/go-sqlite3" // needed for xorm _ "github.com/mattn/go-sqlite3" // needed for xorm
"os" "os"
...@@ -48,6 +49,53 @@ func ormSync() { ...@@ -48,6 +49,53 @@ func ormSync() {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Login checks if password is right for username and returns the User object of it
func Login(username, passwd string) error {
user, err := Get(username)
if err != nil {
return err
}
if pwdRight := user.ComparePassword(passwd); !pwdRight {
return errWrongPassword
}
fmt.Printf("User login: %s\n", username)
return nil
}
// NewUser creates a new user object
func NewUser(name, password, displayname string) (User, error) {
if Contains(name, displayname) {
return User{}, errUserExisting
}
hash, err := bcrypt.GenerateFromPassword([]byte(password), 14)
if err != nil {
return User{}, err
}
fmt.Printf("New User added: %s\n", name)
return User{Name: name, Hash: hash, DisplayName: displayname}, nil
}
// Contains looks if a username is in the datenbank
func Contains(username, displayname string) bool {
count, _ := ormUserExists(User{Name: username, DisplayName: displayname})
return count
}
// Get gets username based on username
func Get(username string) (User, error) {
user, err := ormLoadUser(username)
if err != nil {
fmt.Printf("Get Error: username: %v, user: %v, err: %v\n", username, user, err)
return User{}, err
}
return user, err
}
func ormStart(logFile string) error { func ormStart(logFile string) error {
var err error var err error
engine, err = xorm.NewEngine("sqlite3", "./state.db") engine, err = xorm.NewEngine("sqlite3", "./state.db")
......
...@@ -10,16 +10,12 @@ import ( ...@@ -10,16 +10,12 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gorilla/securecookie" "github.com/gorilla/securecookie"
"github.com/gorilla/sessions" "github.com/gorilla/sessions"
"golang.org/x/crypto/bcrypt"
"html/template" "html/template"
"io/ioutil" "io/ioutil"
"log" "log"
"math/rand"
"net/http" "net/http"
"os" "os"
"sort"
"strconv" "strconv"
"strings"
) )
const ( const (
...@@ -76,281 +72,33 @@ var ( ...@@ -76,281 +72,33 @@ var (
maxrow = 0 maxrow = 0
) )
// FillChallengeURI Fill host into each challenge's URI field and set HasURI type leaderboardPageData struct {
func (c Challenges) FillChallengeURI(host string) { PageTitle string
for i := range c { User *User
if c[i].URI != "" { IsUser bool
c[i].HasURI = true Points int
c[i].URI = fmt.Sprintf(c[i].URI, host) Leaderboard bool
} else { AllUsers []_ORMUser
c[i].HasURI = false GeneratedName string
} Style template.HTMLAttr
} RowNums []gridinfo
} ColNums []gridinfo
}
// Find finds a challenge from a string type mainPageData struct {
func (c Challenges) Find(id string) (*Challenge, error) { PageTitle string
for _, v := range c { Challenges []*Challenge
if v.Name == id { Leaderboard bool
return v, nil SelectedChallengeID string
} HasSelectedChallengeID bool
} GeneratedName string
return &Challenge{}, fmt.Errorf("no challenge with this id") User *User
} IsUser bool
Points int
// AllDepsCompleted checks if User u has completed all Dependent challenges of c RowNums []gridinfo
func (c Challenge) AllDepsCompleted(u User) bool { ColNums []gridinfo
for _, ch := range c.Deps {
a := false
for _, uch := range u.Completed {
if uch.Name == ch.Name {
a = true
}
}
if a == false {
return false
}
}
return true
}
// Contains looks if a username is in the datenbank
func Contains(username, displayname string) bool {
count, _ := ormUserExists(User{Name: username, DisplayName: displayname})
return count
}
// HasSolvedChallenge returns true if u has solved chall
func (u User) HasSolvedChallenge(chall *Challenge) bool {
for _, c := range u.Completed {
if c.Name == chall.Name {
return true
}
}
return false
}
// CalculatePoints calculates Points and updates user.Points
func (u *User) CalculatePoints() {
points := 0
for _, c := range u.Completed {
points += c.Points
}
u.Points = points
}
// Get gets username based on username
func Get(username string) (User, error) {
user, err := ormLoadUser(username)
if err != nil {
fmt.Printf("Get Error: username: %v, user: %v, err: %v\n", username, user, err)
return User{}, err
}
return user, err
}
func resolveDeps(a []string) []*Challenge {
var toReturn []*Challenge
for _, b := range a {
for _, c := range challs {
if c.Name == b {
toReturn = append(toReturn, c)
}
}
}
return toReturn
}
func countDeps(chall *Challenge) int {
max := 1
if len(chall.Deps) == 0 {
return 0
}
for _, a := range chall.Deps {
depcount := countDeps(a)
if depcount+1 > max {
max = depcount + 1
}
}
//return len(chall.DepIDs) + max
return max
}
func countAllDeps() {
for i := range challs {
challs[i].DepCount = countDeps(challs[i])
}
}
func reverseResolveAllDepIDs() {
for i := range challs {
for j := range challs {
if i != j {
for _, d := range challs[j].Deps {
if d.Name == challs[i].Name {
// fmt.Printf("%s hat %s als revers dep\n", challs[i].Name, challs[j].Name)
challs[i].DepIDs = append(challs[i].DepIDs, challs[j].Name)
break
}
}
}
}
}
}
func calculateRowNums() {
cols := make(map[int][]*Challenge)
for _, chall := range challs {
col := chall.DepCount
cols[col] = append(cols[col], chall)
if col > maxcol {
maxcol = col
}
}
fmt.Println("col\t[ <name>]\tmin\trow")
for i := 0; i <= maxcol; i++ {
if _, ok := cols[i]; !ok {
continue
} //Skip empty columns
for _, chall := range cols[i] {
chall.MinRow = 0
for _, dep := range chall.Deps {
if dep.Row > chall.MinRow {
chall.MinRow = dep.Row
}
}
}
sort.Slice(cols[i], func(x, y int) bool {
if cols[i][x].MinRow == cols[i][y].MinRow {
if len(cols[i][x].DepIDs) == len(cols[i][y].DepIDs) {
return stringCompareLess(cols[i][x].Name, cols[i][y].Name)
} else {
// Sort as less (higher) if it has more dependecies
return len(cols[i][x].DepIDs) > len(cols[i][y].DepIDs)
}
} else {
return cols[i][x].MinRow < cols[i][y].MinRow
}
})
row := 0
for j := 0; j < len(cols[i]); j++ {
if row < cols[i][j].MinRow {
row = cols[i][j].MinRow
}
cols[i][j].Row = row
if row > maxrow {
maxrow = row
}
row++
fmt.Printf("%1d\t[%15s]\t%3d %3d\n", i, cols[i][j].Name, cols[i][j].MinRow, cols[i][j].Row)
}
}
}
// https://stackoverflow.com/a/35099450
func stringCompareLess(si, sj string) bool {
var siLower = strings.ToLower(si)
var sjLower = strings.ToLower(sj)
if siLower == sjLower {
return si < sj
}
return siLower < sjLower
}
func resolveChalls(jsons []*ChallengeJSON) {
i := 0
var idsInChalls []string
for len(jsons) != 0 {
// fmt.Printf("challs: %v, jsons: %v\n",challs,jsons)
this := jsons[i]
if bContainsAllOfA(this.Deps, idsInChalls) {
idsInChalls = append(idsInChalls, this.Name)
challs = append(challs, &Challenge{Name: this.Name, Description: this.Description, Flag: this.Flag, URI: this.URI, Points: this.Points, Deps: resolveDeps(this.Deps), Solution: this.Solution, MinRow: -1, Row: -1, Author: this.Author})
jsons[i] = jsons[len(jsons)-1]
jsons = jsons[:len(jsons)-1]
i = 0
} else {
i++
}
}
countAllDeps()
reverseResolveAllDepIDs()
calculateRowNums()
}
// Login checks if password is right for username and returns the User object of it
func Login(username, passwd string) error {
user, err := Get(username)
if err != nil {
return err
}
if pwdRight := user.ComparePassword(passwd); !pwdRight {
return errWrongPassword
}
fmt.Printf("User login: %s\n", username)
return nil
}
// ComparePassword checks if the password is valid
func (u *User) ComparePassword(password string) bool {
return bcrypt.CompareHashAndPassword(u.Hash, []byte(password)) == nil
}
// NewUser creates a new user object
func NewUser(name, password, displayname string) (User, error) {
if Contains(name, displayname) {
return User{}, errUserExisting
}
hash, err := bcrypt.GenerateFromPassword([]byte(password), 14)
if err != nil {
return User{}, err
}
fmt.Printf("New User added: %s\n", name)
return User{Name: name, Hash: hash, DisplayName: displayname}, nil
}
func generateUserName() (string, error) {
var name string
for _, s := range coolNames {
if exists, err := ormDisplayNameExists(s); !exists {
if err != nil {
return "", err
}
name = s
break
}
} }
for name == "" {
name = strconv.FormatInt(rand.Int63(), 10)
if exists, err := ormDisplayNameExists(name); !exists {
if err != nil {
return "", err
}
name = ""
}
}
return name, nil
}
func leaderboardpage(w http.ResponseWriter, r *http.Request) { func leaderboardpage(w http.ResponseWriter, r *http.Request) {
userobj, ok := getUser(r) userobj, ok := getUser(r)
...@@ -761,58 +509,3 @@ func Server() error { ...@@ -761,58 +509,3 @@ func Server() error {
fmt.Printf("WTFD Server Starting at port %d\n", Port) fmt.Printf("WTFD Server Starting at port %d\n", Port)
return http.ListenAndServe(fmt.Sprintf(":%d", Port), r) return http.ListenAndServe(fmt.Sprintf(":%d", Port), r)
} }
func fixDeps(jsons []*ChallengeJSON) {
challsByName := make(map[string]*ChallengeJSON)
for _, chall := range jsons {
challsByName[chall.Name] = chall
}
for _, chall := range jsons {
keepDep := make(map[string]bool)
//Inititalize maps
for _, dep := range chall.Deps {
keepDep[dep] = true
}
//Kick out redundant challenges
for _, dep := range chall.Deps {
for _, depdep := range challsByName[dep].Deps {
if _, ok := keepDep[depdep]; ok {
keepDep[depdep] = false
}
}
}
//Rebould dependency array
var newdeps []string
for name, keep := range keepDep {
if keep {
newdeps = append(newdeps, name)
}
}
//Write to struct
chall.Deps = newdeps
}
}
func bContainsA(a string, b []string) bool {
for _, c := range b {
if a == c {
return true
}
}
return false
}
func bContainsAllOfA(a, b []string) bool {
for _, c := range a {
if !bContainsA(c, b) {
return false
}
}
return true
}
package wtfd package wtfd
import ( import (
"html/template" "golang.org/x/crypto/bcrypt"
"fmt"
"sort"
) )
// Challenges Array of challenges but in nice with funcitons // Challenges Array of challenges but in nice with funcitons
...@@ -54,33 +56,232 @@ type User struct { ...@@ -54,33 +56,232 @@ type User struct {
Points int Points int
} }
type leaderboardPageData struct {
PageTitle string
User *User
IsUser bool
Points int
Leaderboard bool
AllUsers []_ORMUser
GeneratedName string
Style template.HTMLAttr
RowNums []gridinfo
ColNums []gridinfo
}
type mainPageData struct {
PageTitle string
Challenges []*Challenge
Leaderboard bool
SelectedChallengeID string
HasSelectedChallengeID bool
GeneratedName string
User *User
IsUser bool
Points int
RowNums []gridinfo
ColNums []gridinfo
}
type gridinfo struct { type gridinfo struct {
Index int Index int
Pos int Pos int
} }
// FillChallengeURI Fill host into each challenge's URI field and set HasURI
func (c Challenges) FillChallengeURI(host string) {
for i := range c {
if c[i].URI != "" {
c[i].HasURI = true
c[i].URI = fmt.Sprintf(c[i].URI, host)
} else {
c[i].HasURI = false
}
}
}
// Find finds a challenge from a string
func (c Challenges) Find(id string) (*Challenge, error) {
for _, v := range c {
if v.Name == id {
return v, nil
}
}
return &Challenge{}, fmt.Errorf("no challenge with this id")
}
// AllDepsCompleted checks if User u has completed all Dependent challenges of c
func (c Challenge) AllDepsCompleted(u User) bool {
for _, ch := range c.Deps {
a := false
for _, uch := range u.Completed {
if uch.Name == ch.Name {
a = true
}
}
if a == false {
return false
}
}
return true
}
// ComparePassword checks if the password is valid
func (u *User) ComparePassword(password string) bool {
return bcrypt.CompareHashAndPassword(u.Hash, []byte(password)) == nil
}
func resolveDeps(a []string) []*Challenge {
var toReturn []*Challenge
for _, b := range a {
for _, c := range challs {
if c.Name == b {
toReturn = append(toReturn, c)
}
}
}
return toReturn
}
func countDeps(chall *Challenge) int {
max := 1
if len(chall.Deps) == 0 {
return 0
}
for _, a := range chall.Deps {
depcount := countDeps(a)
if depcount+1 > max {
max = depcount + 1
}
}
//return len(chall.DepIDs) + max
return max
}
func countAllDeps() {
for i := range challs {
challs[i].DepCount = countDeps(challs[i])
}
}
func reverseResolveAllDepIDs() {
for i := range challs {
for j := range challs {
if i != j {
for _, d := range challs[j].Deps {
if d.Name == challs[i].Name {
// fmt.Printf("%s hat %s als revers dep\n", challs[i].Name, challs[j].Name)
challs[i].DepIDs = append(challs[i].DepIDs, challs[j].Name)
break
}
}
}
}
}
}
func calculateRowNums() {
cols := make(map[int][]*Challenge)
for _, chall := range challs {
col := chall.DepCount
cols[col] = append(cols[col], chall)
if col > maxcol {
maxcol = col
}
}
fmt.Println("col\t[ <name>]\tmin\trow")
for i := 0; i <= maxcol; i++ {
if _, ok := cols[i]; !ok {
continue
} //Skip empty columns
for _, chall := range cols[i] {
chall.MinRow = 0
for _, dep := range chall.Deps {
if dep.Row > chall.MinRow {
chall.MinRow = dep.Row
}
}
}
sort.Slice(cols[i], func(x, y int) bool {
if cols[i][x].MinRow == cols[i][y].MinRow {
if len(cols[i][x].DepIDs) == len(cols[i][y].DepIDs) {
return stringCompareLess(cols[i][x].Name, cols[i][y].Name)
} else {
// Sort as less (higher) if it has more dependecies
return len(cols[i][x].DepIDs) > len(cols[i][y].DepIDs)
}
} else {
return cols[i][x].MinRow < cols[i][y].MinRow
}
})
row := 0
for j := 0; j < len(cols[i]); j++ {
if row < cols[i][j].MinRow {
row = cols[i][j].MinRow
}
cols[i][j].Row = row
if row > maxrow {
maxrow = row
}
row++
fmt.Printf("%1d\t[%15s]\t%3d %3d\n", i, cols[i][j].Name, cols[i][j].MinRow, cols[i][j].Row)
}
}
}
func resolveChalls(jsons []*ChallengeJSON) {
i := 0
var idsInChalls []string
for len(jsons) != 0 {
// fmt.Printf("challs: %v, jsons: %v\n",challs,jsons)
this := jsons[i]
if bContainsAllOfA(this.Deps, idsInChalls) {
idsInChalls = append(idsInChalls, this.Name)
challs = append(challs, &Challenge{Name: this.Name, Description: this.Description, Flag: this.Flag, URI: this.URI, Points: this.Points, Deps: resolveDeps(this.Deps), Solution: this.Solution, MinRow: -1, Row: -1, Author: this.Author})
jsons[i] = jsons[len(jsons)-1]
jsons = jsons[:len(jsons)-1]
i = 0
} else {
i++
}
}
countAllDeps()
reverseResolveAllDepIDs()
calculateRowNums()
}
func fixDeps(jsons []*ChallengeJSON) {
challsByName := make(map[string]*ChallengeJSON)
for _, chall := range jsons {
challsByName[chall.Name] = chall
}
for _, chall := range jsons {
keepDep := make(map[string]bool)
//Inititalize maps
for _, dep := range chall.Deps {
keepDep[dep] = true
}
//Kick out redundant challenges
for _, dep := range chall.Deps {
for _, depdep := range challsByName[dep].Deps {
if _, ok := keepDep[depdep]; ok {
keepDep[depdep] = false
}
}
}
//Rebould dependency array
var newdeps []string
for name, keep := range keepDep {
if keep {
newdeps = append(newdeps, name)
}
}
//Write to struct
chall.Deps = newdeps
}
}
// HasSolvedChallenge returns true if u has solved chall
func (u User) HasSolvedChallenge(chall *Challenge) bool {
for _, c := range u.Completed {
if c.Name == chall.Name {
return true
}
}
return false
}
// CalculatePoints calculates Points and updates user.Points
func (u *User) CalculatePoints() {
points := 0
for _, c := range u.Completed {
points += c.Points
}
u.Points = points
}
package wtfd
import (
"strings"
"strconv"
"math/rand"
)
// https://stackoverflow.com/a/35099450
func stringCompareLess(si, sj string) bool {
var siLower = strings.ToLower(si)
var sjLower = strings.ToLower(sj)
if siLower == sjLower {
return si < sj
}
return siLower < sjLower
}
func generateUserName() (string, error) {
var name string
for _, s := range coolNames {
if exists, err := ormDisplayNameExists(s); !exists {
if err != nil {
return "", err
}
name = s
break
}
}
for name == "" {
name = strconv.FormatInt(rand.Int63(), 10)
if exists, err := ormDisplayNameExists(name); !exists {
if err != nil {
return "", err
}
name = ""
}
}
return name, nil
}
func bContainsA(a string, b []string) bool {
for _, c := range b {
if a == c {
return true
}
}
return false
}
func bContainsAllOfA(a, b []string) bool {
for _, c := range a {
if !bContainsA(c, b) {
return false
}
}
return true
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment