diff --git a/.gitignore b/.gitignore index 7a7b0eedef2b8917b496a5033b2738c0f7d51746..e5ce6dd14aa3e1982e36b183022da814c6bdc9a9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ ### Go ### # Binaries for programs and plugins +wtfd *.exe *.exe~ *.dll diff --git a/internal/orm.go b/internal/orm.go index a694dc5b2ba18fe9163217799ebfeddef853e835..47da321270f751df8a6cbf763a901cf710a996b0 100644 --- a/internal/orm.go +++ b/internal/orm.go @@ -3,6 +3,7 @@ package wtfd import ( "errors" "fmt" + "golang.org/x/crypto/bcrypt" "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" // needed for xorm "os" @@ -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 { var err error engine, err = xorm.NewEngine("sqlite3", "./state.db") diff --git a/internal/server.go b/internal/server.go index a410c59409bfd881dc4e67a7d3d570bca9ac33d0..0910693ebbeb4fd9e0dece400df9744984beb865 100644 --- a/internal/server.go +++ b/internal/server.go @@ -10,16 +10,12 @@ import ( "github.com/gorilla/mux" "github.com/gorilla/securecookie" "github.com/gorilla/sessions" - "golang.org/x/crypto/bcrypt" "html/template" "io/ioutil" "log" - "math/rand" "net/http" "os" - "sort" "strconv" - "strings" ) const ( @@ -76,281 +72,33 @@ var ( maxrow = 0 ) -// 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 -} - -// 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 - +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 } -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) { userobj, ok := getUser(r) @@ -761,58 +509,3 @@ func Server() error { fmt.Printf("WTFD Server Starting at port %d\n", Port) 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 -} diff --git a/internal/structs.go b/internal/structs.go index 259de18fb6c47d492e836faef5f0e030adc067e0..cbe48a80dd3521d3b4c5951b044d3a4e22cf6165 100644 --- a/internal/structs.go +++ b/internal/structs.go @@ -1,7 +1,9 @@ package wtfd import ( - "html/template" + "golang.org/x/crypto/bcrypt" + "fmt" + "sort" ) // Challenges Array of challenges but in nice with funcitons @@ -54,33 +56,232 @@ type User struct { 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 { Index 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 +} diff --git a/internal/util.go b/internal/util.go new file mode 100644 index 0000000000000000000000000000000000000000..8cf9ff968d346c12a9620472b08eabf55f15add6 --- /dev/null +++ b/internal/util.go @@ -0,0 +1,66 @@ +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 +}