Rename Exercice's Keys as Flags

This commit is contained in:
nemunaire 2018-09-24 10:00:17 +02:00 committed by Pierre-Olivier Mercier
parent f36e1c4e4d
commit d21f3b0b83
18 changed files with 252 additions and 252 deletions

View File

@ -30,11 +30,11 @@ func init() {
router.PUT("/api/exercices/:eid/hints/:hid", apiHandler(hintHandler(updateExerciceHint)))
router.DELETE("/api/exercices/:eid/hints/:hid", apiHandler(hintHandler(deleteExerciceHint)))
router.GET("/api/exercices/:eid/keys", apiHandler(exerciceHandler(listExerciceKeys)))
router.POST("/api/exercices/:eid/keys", apiHandler(exerciceHandler(createExerciceKey)))
router.GET("/api/exercices/:eid/keys/:kid", apiHandler(keyHandler(showExerciceKey)))
router.PUT("/api/exercices/:eid/keys/:kid", apiHandler(keyHandler(updateExerciceKey)))
router.DELETE("/api/exercices/:eid/keys/:kid", apiHandler(keyHandler(deleteExerciceKey)))
router.GET("/api/exercices/:eid/flags", apiHandler(exerciceHandler(listExerciceFlags)))
router.POST("/api/exercices/:eid/flags", apiHandler(exerciceHandler(createExerciceFlag)))
router.GET("/api/exercices/:eid/flags/:kid", apiHandler(flagHandler(showExerciceFlag)))
router.PUT("/api/exercices/:eid/flags/:kid", apiHandler(flagHandler(updateExerciceFlag)))
router.DELETE("/api/exercices/:eid/flags/:kid", apiHandler(flagHandler(deleteExerciceFlag)))
router.GET("/api/exercices/:eid/quiz", apiHandler(exerciceHandler(listExerciceQuiz)))
router.GET("/api/exercices/:eid/quiz/:qid", apiHandler(quizHandler(showExerciceQuiz)))
@ -50,9 +50,9 @@ func init() {
func(exercice fic.Exercice, _ []byte) (interface{}, error) {
return sync.SyncExerciceHints(sync.GlobalImporter, exercice), nil
})))
router.POST("/api/sync/exercices/:eid/keys", apiHandler(exerciceHandler(
router.POST("/api/sync/exercices/:eid/flags", apiHandler(exerciceHandler(
func(exercice fic.Exercice, _ []byte) (interface{}, error) {
return sync.SyncExerciceKeys(sync.GlobalImporter, exercice), nil
return sync.SyncExerciceFlags(sync.GlobalImporter, exercice), nil
})))
router.POST("/api/sync/exercices/:eid/fixurlid", apiHandler(exerciceHandler(
@ -77,8 +77,8 @@ func listExerciceHints(exercice fic.Exercice, body []byte) (interface{}, error)
return exercice.GetHints()
}
func listExerciceKeys(exercice fic.Exercice, body []byte) (interface{}, error) {
return exercice.GetKeys()
func listExerciceFlags(exercice fic.Exercice, body []byte) (interface{}, error) {
return exercice.GetFlags()
}
func listExerciceQuiz(exercice fic.Exercice, body []byte) (interface{}, error) {
@ -188,56 +188,56 @@ func deleteExerciceHint(hint fic.EHint, _ []byte) (interface{}, error) {
return hint.Delete()
}
type uploadedKey struct {
type uploadedFlag struct {
Label string
Help string
ICase bool
Key string
Flag string
Hash []byte
}
func createExerciceKey(exercice fic.Exercice, body []byte) (interface{}, error) {
var uk uploadedKey
func createExerciceFlag(exercice fic.Exercice, body []byte) (interface{}, error) {
var uk uploadedFlag
if err := json.Unmarshal(body, &uk); err != nil {
return nil, err
}
if len(uk.Key) == 0 {
return nil, errors.New("Key not filled")
if len(uk.Flag) == 0 {
return nil, errors.New("Flag not filled")
}
return exercice.AddRawKey(uk.Label, uk.Help, uk.ICase, uk.Key)
return exercice.AddRawFlag(uk.Label, uk.Help, uk.ICase, uk.Flag)
}
func showExerciceKey(key fic.Key, _ fic.Exercice, body []byte) (interface{}, error) {
return key, nil
func showExerciceFlag(flag fic.Flag, _ fic.Exercice, body []byte) (interface{}, error) {
return flag, nil
}
func updateExerciceKey(key fic.Key, exercice fic.Exercice, body []byte) (interface{}, error) {
var uk uploadedKey
func updateExerciceFlag(flag fic.Flag, exercice fic.Exercice, body []byte) (interface{}, error) {
var uk uploadedFlag
if err := json.Unmarshal(body, &uk); err != nil {
return nil, err
}
if len(uk.Label) == 0 {
key.Label = "Flag"
flag.Label = "Flag"
} else {
key.Label = uk.Label
flag.Label = uk.Label
}
key.Help = uk.Help
key.IgnoreCase = uk.ICase
key.Checksum = uk.Hash
flag.Help = uk.Help
flag.IgnoreCase = uk.ICase
flag.Checksum = uk.Hash
if _, err := key.Update(); err != nil {
if _, err := flag.Update(); err != nil {
return nil, err
}
return key, nil
return flag, nil
}
func deleteExerciceKey(key fic.Key, _ fic.Exercice, _ []byte) (interface{}, error) {
return key.Delete()
func deleteExerciceFlag(flag fic.Flag, _ fic.Exercice, _ []byte) (interface{}, error) {
return flag.Delete()
}
func showExerciceQuiz(quiz fic.MCQ, _ fic.Exercice, body []byte) (interface{}, error) {

View File

@ -165,7 +165,7 @@ func hintHandler(f func(fic.EHint, []byte) (interface{}, error)) func(httprouter
}
}
func keyHandler(f func(fic.Key, fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) {
func flagHandler(f func(fic.Flag, fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) {
return func(ps httprouter.Params, body []byte) (interface{}, error) {
var exercice fic.Exercice
exerciceHandler(func(ex fic.Exercice, _ []byte) (interface{}, error) {
@ -175,12 +175,12 @@ func keyHandler(f func(fic.Key, fic.Exercice, []byte) (interface{}, error)) func
if kid, err := strconv.Atoi(string(ps.ByName("kid"))); err != nil {
return nil, err
} else if keys, err := exercice.GetKeys(); err != nil {
} else if flags, err := exercice.GetFlags(); err != nil {
return nil, err
} else {
for _, key := range keys {
if key.Id == int64(kid) {
return f(key, exercice, body)
for _, flag := range flags {
if flag.Id == int64(kid) {
return f(flag, exercice, body)
}
}
return nil, errors.New("Unable to find the requested key")

View File

@ -35,8 +35,8 @@ func init() {
router.GET("/api/themes/:thid/exercices/:eid/hints", apiHandler(exerciceHandler(listExerciceHints)))
router.POST("/api/themes/:thid/exercices/:eid/hints", apiHandler(exerciceHandler(createExerciceHint)))
router.GET("/api/themes/:thid/exercices/:eid/keys", apiHandler(exerciceHandler(listExerciceKeys)))
router.POST("/api/themes/:thid/exercices/:eid/keys", apiHandler(exerciceHandler(createExerciceKey)))
router.GET("/api/themes/:thid/exercices/:eid/keys", apiHandler(exerciceHandler(listExerciceFlags)))
router.POST("/api/themes/:thid/exercices/:eid/keys", apiHandler(exerciceHandler(createExerciceFlag)))
// Remote
router.GET("/api/remote/themes", apiHandler(sync.ApiListRemoteThemes))
@ -66,7 +66,7 @@ func init() {
})))
router.POST("/api/sync/themes/:thid/exercices/:eid/keys", apiHandler(exerciceHandler(
func(exercice fic.Exercice, _ []byte) (interface{}, error) {
return sync.SyncExerciceKeys(sync.GlobalImporter, exercice), nil
return sync.SyncExerciceFlags(sync.GlobalImporter, exercice), nil
})))
router.POST("/api/sync/themes/:thid/fixurlid", apiHandler(themeHandler(

View File

@ -9,5 +9,5 @@ func init() {
}
func showVersion(_ httprouter.Params, body []byte) (interface{}, error) {
return map[string]interface{}{"version": 0.5}, nil
return map[string]interface{}{"version": 0.6}, nil
}

View File

@ -218,12 +218,12 @@ angular.module("FICApp")
update: {method: 'PUT'}
})
})
.factory("ExerciceKey", function($resource) {
return $resource("/api/exercices/:exerciceId/keys/:keyId", { exerciceId: '@idExercice', keyId: '@id' }, {
.factory("ExerciceFlag", function($resource) {
return $resource("/api/exercices/:exerciceId/flags/:flagId", { exerciceId: '@idExercice', flagId: '@id' }, {
update: {method: 'PUT'}
})
})
.factory("ExerciceMCQKey", function($resource) {
.factory("ExerciceMCQFlag", function($resource) {
return $resource("/api/exercices/:exerciceId/quiz/:mcqId", { exerciceId: '@idExercice', mcqId: '@id' }, {
update: {method: 'PUT'}
})
@ -995,8 +995,8 @@ angular.module("FICApp")
work.push("/api/sync/exercices/" + ex.id + "/files");
if ($scope.syncHints)
work.push("/api/sync/exercices/" + ex.id + "/hints");
if ($scope.syncKeys)
work.push("/api/sync/exercices/" + ex.id + "/keys");
if ($scope.syncFlags)
work.push("/api/sync/exercices/" + ex.id + "/flags");
});
$scope.total = work.length;
go();
@ -1004,7 +1004,7 @@ angular.module("FICApp")
};
$scope.syncFiles = true;
$scope.syncHints = true;
$scope.syncKeys = true;
$scope.syncFlags = true;
})
.controller("ExercicesListController", function($scope, ThemedExercice, $routeParams, $location) {
$scope.exercices = ThemedExercice.query({ themeId: $routeParams.themeId });
@ -1106,35 +1106,35 @@ angular.module("FICApp")
};
})
.controller("ExerciceKeysController", function($scope, ExerciceKey, $routeParams, $rootScope, $http) {
$scope.keys = ExerciceKey.query({ exerciceId: $routeParams.exerciceId });
.controller("ExerciceFlagsController", function($scope, ExerciceFlag, $routeParams, $rootScope, $http) {
$scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId });
$scope.addKey = function() {
$scope.keys.push(new ExerciceKey());
$scope.addFlag = function() {
$scope.flags.push(new ExerciceFlag());
}
$scope.deleteKey = function() {
this.key.$delete(function() {
$scope.keys.splice($scope.keys.indexOf(this.key), 1);
$scope.deleteFlag = function() {
this.flag.$delete(function() {
$scope.flags.splice($scope.flags.indexOf(this.flag), 1);
}, function(response) {
$rootScope.newBox('danger', 'An error occurs when trying to delete flag:', response.data);
});
}
$scope.saveKey = function() {
if (this.key.id) {
this.key.$update();
$scope.saveFlag = function() {
if (this.flag.id) {
this.flag.$update();
} else {
this.key.$save({ exerciceId: $routeParams.exerciceId });
this.flag.$save({ exerciceId: $routeParams.exerciceId });
}
}
$scope.inSync = false;
$scope.syncKeys = function() {
$scope.syncFlags = function() {
$scope.inSync = true;
$http({
url: "/api/sync/exercices/" + $routeParams.exerciceId + "/keys",
url: "/api/sync/exercices/" + $routeParams.exerciceId + "/flags",
method: "POST"
}).then(function(response) {
$scope.inSync = false;
$scope.keys = ExerciceKey.query({ exerciceId: $routeParams.exerciceId });
$scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId });
if (response.data)
$rootScope.newBox('danger', response.data);
else
@ -1146,11 +1146,11 @@ angular.module("FICApp")
};
})
.controller("ExerciceMCQKeysController", function($scope, ExerciceMCQKey, $routeParams, $rootScope, $http) {
$scope.quiz = ExerciceMCQKey.query({ exerciceId: $routeParams.exerciceId });
.controller("ExerciceMCQFlagsController", function($scope, ExerciceMCQFlag, $routeParams, $rootScope, $http) {
$scope.quiz = ExerciceMCQFlag.query({ exerciceId: $routeParams.exerciceId });
$scope.addQuiz = function() {
$scope.quiz.push(new ExerciceMCQKey());
$scope.quiz.push(new ExerciceMCQFlag());
}
$scope.deleteQuiz = function() {
this.q.$delete(function() {

View File

@ -8,8 +8,8 @@
<label class="btn btn-sm btn-secondary" ng-class="{active: syncHints, 'btn-warning': syncHints}" ng-show="whoami">
<input type="checkbox" ng-model="syncHints"> Indices
</label>
<label class="btn btn-sm btn-secondary" ng-class="{active: syncKeys, 'btn-warning': syncKeys}">
<input type="checkbox" ng-model="syncKeys"> Flags
<label class="btn btn-sm btn-secondary" ng-class="{active: syncFlags, 'btn-warning': syncFlags}">
<input type="checkbox" ng-model="syncFlags"> Flags
</label>
</div>
</small>

View File

@ -80,38 +80,38 @@
</div>
</div>
<div class="col-md-4" ng-controller="ExerciceKeysController">
<div class="col-md-4" ng-controller="ExerciceFlagsController">
<div class="card border-success">
<div class="card-header bg-success text-light">
<button ng-click="addKey()" class="float-right btn btn-sm btn-primary ml-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
<button ng-click="syncKeys()" class="float-right btn btn-sm btn-light ml-2"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span></button>
<button ng-click="addFlag()" class="float-right btn btn-sm btn-primary ml-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
<button ng-click="syncFlags()" class="float-right btn btn-sm btn-light ml-2"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span></button>
<h4>Drapeaux</h4>
</div>
<div class="list-group">
<form ng-submit="saveKey()" class="list-group-item form-horizontal bg-light text-dark" ng-repeat="key in keys">
<div class="form-group row" id="key-{{key.id}}">
<input type="text" id="klabel{{key.id}}" ng-model="key.label" class="col form-control" placeholder="Intitulé">
<div class="col-auto" ng-show="key.id">
<button ng-click="deleteKey()" class="btn btn-danger"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>
<form ng-submit="saveFlag()" class="list-group-item form-horizontal bg-light text-dark" ng-repeat="flag in flags">
<div class="form-group row" id="flag-{{flag.id}}">
<input type="text" id="klabel{{flag.id}}" ng-model="flag.label" class="col form-control" placeholder="Intitulé">
<div class="col-auto" ng-show="flag.id">
<button ng-click="deleteFlag()" class="btn btn-danger"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>
</div>
</div>
<div class="form-group row">
<input type="text" id="khelp{{key.id}}" ng-model="key.help" class="col form-control" placeholder="Indication de formatage">
<input type="text" id="khelp{{flag.id}}" ng-model="flag.help" class="col form-control" placeholder="Indication de formatage">
<div class="col-auto">
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
</div>
</div>
<div class="form-group row" ng-show="key.id">
<input type="text" id="kvalue{{key.id}}" ng-model="key.value" class="col form-control" placeholder="Condensat">
<div class="form-group row" ng-show="flag.id">
<input type="text" id="kvalue{{flag.id}}" ng-model="flag.value" class="col form-control" placeholder="Condensat">
</div>
<div class="form-group row" ng-show="!key.id">
<input type="text" id="kvalue{{key.id}}" ng-model="key.key" class="col form-control" placeholder="Chaîne à valider">
<div class="form-group row" ng-show="!flag.id">
<input type="text" id="kvalue{{flag.id}}" ng-model="flag.flag" class="col form-control" placeholder="Chaîne à valider">
</div>
</form>
</div>
</div>
<div class="card border-success mt-2" ng-controller="ExerciceMCQKeysController">
<div class="card border-success mt-2" ng-controller="ExerciceMCQFlagsController">
<div class="card-header bg-success text-light">
<button ng-click="addQuiz()" class="float-right btn btn-sm btn-primary ml-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
<h4>Quizz</h4>

View File

@ -19,9 +19,9 @@ func isFullGraphic(s string) bool {
return true
}
// SyncExerciceKeys reads the content of challenge.txt and import "classic" flags as Key for the given challenge.
func SyncExerciceKeys(i Importer, exercice fic.Exercice) (errs []string) {
if _, err := exercice.WipeKeys(); err != nil {
// SyncExerciceFlags reads the content of challenge.txt and import "classic" flags as Key for the given challenge.
func SyncExerciceFlags(i Importer, exercice fic.Exercice) (errs []string) {
if _, err := exercice.WipeFlags(); err != nil {
errs = append(errs, err.Error())
} else if _, err := exercice.WipeMCQs(); err != nil {
errs = append(errs, err.Error())
@ -40,7 +40,7 @@ func SyncExerciceKeys(i Importer, exercice fic.Exercice) (errs []string) {
errs = append(errs, fmt.Sprintf("%q: WARNING flag #%d: non-printable characters in flag, is this really expected?", path.Base(exercice.Path), nline + 1))
}
if k, err := exercice.AddRawKey(flag.Label, flag.Help, flag.IgnoreCase, flag.Raw); err != nil {
if k, err := exercice.AddRawFlag(flag.Label, flag.Help, flag.IgnoreCase, flag.Raw); err != nil {
errs = append(errs, fmt.Sprintf("%q: error flag #%d: %s", path.Base(exercice.Path), nline + 1, err))
continue
} else {
@ -67,7 +67,7 @@ func SyncExerciceKeys(i Importer, exercice fic.Exercice) (errs []string) {
errs = append(errs, fmt.Sprintf("%q: WARNING flag UCQ #%d: non-printable characters in flag, is this really expected?", path.Base(exercice.Path), nline + 1))
}
if k, err := exercice.AddRawKey(flag.Label, flag.Help, flag.IgnoreCase, flag.Raw); err != nil {
if k, err := exercice.AddRawFlag(flag.Label, flag.Help, flag.IgnoreCase, flag.Raw); err != nil {
errs = append(errs, fmt.Sprintf("%q: error flag UCQ #%d: %s", path.Base(exercice.Path), nline + 1, err))
continue
} else {

View File

@ -34,7 +34,7 @@ func SyncDeep(i Importer) (errs map[string][]string) {
if exercices, err := theme.GetExercices(); err == nil {
for _, exercice := range exercices {
errs[theme.Name] = append(errs[theme.Name], SyncExerciceKeys(i, exercice)...)
errs[theme.Name] = append(errs[theme.Name], SyncExerciceFlags(i, exercice)...)
errs[theme.Name] = append(errs[theme.Name], SyncExerciceFiles(i, exercice)...)
errs[theme.Name] = append(errs[theme.Name], SyncExerciceHints(i, exercice)...)
}

View File

@ -156,8 +156,8 @@ CREATE TABLE IF NOT EXISTS exercice_hints(
return err
}
if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_keys(
id_key INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
CREATE TABLE IF NOT EXISTS exercice_flags(
id_flag INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
id_exercice INTEGER NOT NULL,
type VARCHAR(255) NOT NULL,
help VARCHAR(255) NOT NULL,
@ -171,9 +171,9 @@ CREATE TABLE IF NOT EXISTS exercice_keys(
if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS exercice_files_deps(
id_file INTEGER NOT NULL,
id_key INTEGER NOT NULL,
id_flag INTEGER NOT NULL,
FOREIGN KEY(id_file) REFERENCES exercice_files(id_file),
FOREIGN KEY(id_key) REFERENCES exercice_keys(id_key)
FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
return err
@ -212,12 +212,12 @@ CREATE TABLE IF NOT EXISTS mcq_found(
return err
}
if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS key_found(
id_key INTEGER NOT NULL,
CREATE TABLE IF NOT EXISTS flag_found(
id_flag INTEGER NOT NULL,
id_team INTEGER NOT NULL,
time TIMESTAMP NOT NULL,
CONSTRAINT uc_found UNIQUE (id_key,id_team),
FOREIGN KEY(id_key) REFERENCES exercice_keys(id_key),
CONSTRAINT uc_found UNIQUE (id_flag,id_team),
FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag),
FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {

View File

@ -260,15 +260,15 @@ func (e Exercice) TriedCount() int64 {
// CheckResponse, given both flags and MCQ responses, figures out if thoses are correct (or if they are previously solved).
// In the meanwhile, CheckResponse registers good answers given (but it does not mark the challenge as solved at the end).
func (e Exercice) CheckResponse(respkeys map[string]string, respmcq map[int64]bool, t Team) (bool, error) {
func (e Exercice) CheckResponse(respflags map[string]string, respmcq map[int64]bool, t Team) (bool, error) {
if err := e.NewTry(t); err != nil {
return false, err
} else if keys, err := e.GetKeys(); err != nil {
} else if flags, err := e.GetFlags(); err != nil {
return false, err
} else if mcqs, err := e.GetMCQ(); err != nil {
return false, err
} else if len(keys) < 1 && len(mcqs) < 1 {
return true, errors.New("Exercice with no key registered")
} else if len(flags) < 1 && len(mcqs) < 1 {
return true, errors.New("Exercice with no flag registered")
} else {
valid := true
diff := 0
@ -292,16 +292,16 @@ func (e Exercice) CheckResponse(respkeys map[string]string, respmcq map[int64]bo
}
}
// Check keys
for _, key := range keys {
if res, ok := respkeys[key.Label]; !ok {
// Check flags
for _, flag := range flags {
if res, ok := respflags[flag.Label]; !ok {
valid = false
} else if !key.Check(res) {
if !PartialValidation || t.HasPartiallySolved(key) == nil {
} else if !flag.Check(res) {
if !PartialValidation || t.HasPartiallySolved(flag) == nil {
valid = false
}
} else {
key.FoundBy(t)
flag.FoundBy(t)
}
}

View File

@ -272,25 +272,25 @@ func (f EFile) GetOrigin() string {
}
// AddDepend insert a new dependency to a given flag.
func (f EFile) AddDepend(k Key) (err error) {
_, err = DBExec("INSERT INTO exercice_files_deps (id_file, id_key) VALUES (?, ?)", f.Id, k.Id)
func (f EFile) AddDepend(k Flag) (err error) {
_, err = DBExec("INSERT INTO exercice_files_deps (id_file, id_flag) VALUES (?, ?)", f.Id, k.Id)
return
}
// GetDepends retrieve the flag's dependency list.
func (f EFile) GetDepends() ([]Key, error) {
if rows, err := DBQuery("SELECT id_key FROM exercice_files_deps WHERE id_file = ?", f.Id); err != nil {
func (f EFile) GetDepends() ([]Flag, error) {
if rows, err := DBQuery("SELECT id_flag FROM exercice_files_deps WHERE id_file = ?", f.Id); err != nil {
return nil, err
} else {
defer rows.Close()
var deps = make([]Key, 0)
var deps = make([]Flag, 0)
for rows.Next() {
var d int64
if err := rows.Scan(&d); err != nil {
return nil, err
}
deps = append(deps, Key{d, f.IdExercice, "", "", false, []byte{}})
deps = append(deps, Flag{d, f.IdExercice, "", "", false, []byte{}})
}
if err := rows.Err(); err != nil {
return nil, err

129
libfic/flag.go Normal file
View File

@ -0,0 +1,129 @@
package fic
import (
"time"
"golang.org/x/crypto/blake2b"
)
// Flag represents a flag's challenge, stored as hash.
type Flag struct {
Id int64 `json:"id"`
// IdExercice is the identifier of the underlying challenge
IdExercice int64 `json:"idExercice"`
// Label is the title of the flag as displayed to players
Label string `json:"label"`
// Help is a small piece of text that aims to add useful information like flag format, ...
Help string `json:"help"`
// IgnoreCase indicates if the case is sensitive to case or not
IgnoreCase bool `json:"ignorecase"`
// Checksum is the expected hashed flag
Checksum []byte `json:"value"`
}
// GetFlags returns a list of flags comming with the challenge.
func (e Exercice) GetFlags() ([]Flag, error) {
if rows, err := DBQuery("SELECT id_flag, id_exercice, type, help, ignorecase, cksum FROM exercice_flags WHERE id_exercice = ?", e.Id); err != nil {
return nil, err
} else {
defer rows.Close()
var flags = make([]Flag, 0)
for rows.Next() {
var k Flag
k.IdExercice = e.Id
if err := rows.Scan(&k.Id, &k.IdExercice, &k.Label, &k.Help, &k.IgnoreCase, &k.Checksum); err != nil {
return nil, err
}
flags = append(flags, k)
}
if err := rows.Err(); err != nil {
return nil, err
}
return flags, nil
}
}
// getHashedFlag calculates the expected checksum for the given raw_value.
func getHashedFlag(raw_value string) [blake2b.Size]byte {
hash := blake2b.Sum512([]byte(raw_value))
return hash
}
// AddRawFlag creates and fills a new struct Flag, from a non-hashed flag, and registers it into the database.
func (e Exercice) AddRawFlag(name string, help string, ignorecase bool, raw_value string) (Flag, error) {
hash := getHashedFlag(raw_value)
return e.AddFlag(name, help, ignorecase, hash[:])
}
// AddFlag creates and fills a new struct Flag, from a hashed flag, and registers it into the database.
func (e Exercice) AddFlag(name string, help string, ignorecase bool, checksum []byte) (Flag, error) {
if res, err := DBExec("INSERT INTO exercice_flags (id_exercice, type, help, ignorecase, cksum) VALUES (?, ?, ?, ?, ?)", e.Id, name, help, ignorecase, checksum); err != nil {
return Flag{}, err
} else if kid, err := res.LastInsertId(); err != nil {
return Flag{}, err
} else {
return Flag{kid, e.Id, name, help, ignorecase, checksum}, nil
}
}
// Update applies modifications back to the database.
func (k Flag) Update() (int64, error) {
if res, err := DBExec("UPDATE exercice_flags SET id_exercice = ?, type = ?, help = ?, ignorecase = ?, cksum = ? WHERE id_flag = ?", k.IdExercice, k.Label, k.Help, k.IgnoreCase, k.Checksum, k.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}
// Delete the flag from the database.
func (k Flag) Delete() (int64, error) {
if _, err := DBExec("DELETE FROM exercice_files_deps WHERE id_flag = ?", k.Id); err != nil {
return 0, err
} else if res, err := DBExec("DELETE FROM exercice_flags WHERE id_flag = ?", k.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}
// WipeFlags deletes flags coming with the challenge.
func (e Exercice) WipeFlags() (int64, error) {
if _, err := DBExec("DELETE FROM exercice_files_deps WHERE id_flag IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)", e.Id); err != nil {
return 0, err
} else if res, err := DBExec("DELETE FROM exercice_flags WHERE id_exercice = ?", e.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}
// Check if the given val is the expected one for this flag.
func (k Flag) Check(val string) bool {
hash := getHashedFlag(val)
if len(k.Checksum) != len(hash) {
return false
}
for i := range hash {
if k.Checksum[i] != hash[i] {
return false
}
}
return true
}
// FoundBy registers in the database that the given Team solved the flag.
func (k Flag) FoundBy(t Team) {
DBExec("INSERT INTO flag_found (id_flag, id_team, time) VALUES (?, ?, ?)", k.Id, t.Id, time.Now())
}

View File

@ -1,129 +0,0 @@
package fic
import (
"time"
"golang.org/x/crypto/blake2b"
)
// Key represents a flag's challenge, stored as hash.
type Key struct {
Id int64 `json:"id"`
// IdExercice is the identifier of the underlying challenge
IdExercice int64 `json:"idExercice"`
// Label is the title of the flag as displayed to players
Label string `json:"label"`
// Help is a small piece of text that aims to add useful information like flag format, ...
Help string `json:"help"`
// IgnoreCase indicates if the case is sensitive to case or not
IgnoreCase bool `json:"ignorecase"`
// Checksum is the expected hashed flag
Checksum []byte `json:"value"`
}
// GetKeys returns a list of flags comming with the challenge.
func (e Exercice) GetKeys() ([]Key, error) {
if rows, err := DBQuery("SELECT id_key, id_exercice, type, help, ignorecase, cksum FROM exercice_keys WHERE id_exercice = ?", e.Id); err != nil {
return nil, err
} else {
defer rows.Close()
var keys = make([]Key, 0)
for rows.Next() {
var k Key
k.IdExercice = e.Id
if err := rows.Scan(&k.Id, &k.IdExercice, &k.Label, &k.Help, &k.IgnoreCase, &k.Checksum); err != nil {
return nil, err
}
keys = append(keys, k)
}
if err := rows.Err(); err != nil {
return nil, err
}
return keys, nil
}
}
// getHashedKey calculates the expected checksum for the given raw_value.
func getHashedKey(raw_value string) [blake2b.Size]byte {
hash := blake2b.Sum512([]byte(raw_value))
return hash
}
// AddRawKey creates and fills a new struct Key, from a non-hashed flag, and registers it into the database.
func (e Exercice) AddRawKey(name string, help string, ignorecase bool, raw_value string) (Key, error) {
hash := getHashedKey(raw_value)
return e.AddKey(name, help, ignorecase, hash[:])
}
// AddKey creates and fills a new struct Key, from a hashed flag, and registers it into the database.
func (e Exercice) AddKey(name string, help string, ignorecase bool, checksum []byte) (Key, error) {
if res, err := DBExec("INSERT INTO exercice_keys (id_exercice, type, help, ignorecase, cksum) VALUES (?, ?, ?, ?)", e.Id, name, help, ignorecase, checksum); err != nil {
return Key{}, err
} else if kid, err := res.LastInsertId(); err != nil {
return Key{}, err
} else {
return Key{kid, e.Id, name, help, ignorecase, checksum}, nil
}
}
// Update applies modifications back to the database.
func (k Key) Update() (int64, error) {
if res, err := DBExec("UPDATE exercice_keys SET id_exercice = ?, type = ?, help = ?, ignorecase = ?, cksum = ? WHERE id_key = ?", k.IdExercice, k.Label, k.Help, k.Checksum, k.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}
// Delete the flag from the database.
func (k Key) Delete() (int64, error) {
if _, err := DBExec("DELETE FROM exercice_files_deps WHERE id_key = ?", k.Id); err != nil {
return 0, err
} else if res, err := DBExec("DELETE FROM exercice_keys WHERE id_key = ?", k.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}
// WipeKeys deletes keys coming with the challenge.
func (e Exercice) WipeKeys() (int64, error) {
if _, err := DBExec("DELETE FROM exercice_files_deps WHERE id_key IN (SELECT id_key FROM exercice_keys WHERE id_exercice = ?)", e.Id); err != nil {
return 0, err
} else if res, err := DBExec("DELETE FROM exercice_keys WHERE id_exercice = ?", e.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}
// Check if the given val is the expected one for this flag.
func (k Key) Check(val string) bool {
hash := getHashedKey(val)
if len(k.Checksum) != len(hash) {
return false
}
for i := range hash {
if k.Checksum[i] != hash[i] {
return false
}
}
return true
}
// FoundBy registers in the database that the given Team solved the flag.
func (k Key) FoundBy(t Team) {
DBExec("INSERT INTO key_found (id_key, id_team, time) VALUES (?, ?, ?)", k.Id, t.Id, time.Now())
}

View File

@ -29,15 +29,15 @@ func truncateTable(tables ...string) (error) {
// ResetGame resets all tables containing team attempts and solves.
func ResetGame() (error) {
return truncateTable("team_hints", "key_found", "mcq_found", "exercice_solved", "exercice_tries")
return truncateTable("team_hints", "flag_found", "mcq_found", "exercice_solved", "exercice_tries")
}
// ResetExercices wipes out all challenges (both attempts and statements).
func ResetExercices() (error) {
return truncateTable("team_hints", "exercice_files_deps", "exercice_files", "key_found", "exercice_keys", "exercice_solved", "exercice_tries", "exercice_hints", "mcq_found", "mcq_entries", "exercice_mcq", "exercices", "themes")
return truncateTable("team_hints", "exercice_files_deps", "exercice_files", "flag_found", "exercice_flags", "exercice_solved", "exercice_tries", "exercice_hints", "mcq_found", "mcq_entries", "exercice_mcq", "exercices", "themes")
}
// ResetTeams wipes out all teams, incluings members and attempts.
func ResetTeams() (error) {
return truncateTable("team_hints", "key_found", "mcq_found", "exercice_solved", "exercice_tries", "team_members", "teams")
return truncateTable("team_hints", "flag_found", "mcq_found", "exercice_solved", "exercice_tries", "team_members", "teams")
}

View File

@ -209,9 +209,9 @@ func (t Team) GetSolvedRank(e Exercice) (nb int64, err error) {
}
}
// HasPartiallySolved checks if the Team already has unlocked the given key and returns the validation's timestamp.
func (t Team) HasPartiallySolved(k Key) (tm *time.Time) {
DBQueryRow("SELECT MIN(time) FROM key_found WHERE id_team = ? AND id_key = ?", t.Id, k.Id).Scan(&tm)
// HasPartiallySolved checks if the Team already has unlocked the given flag and returns the validation's timestamp.
func (t Team) HasPartiallySolved(f Flag) (tm *time.Time) {
DBQueryRow("SELECT MIN(time) FROM flag_found WHERE id_team = ? AND id_flag = ?", t.Id, f.Id).Scan(&tm)
return
}

View File

@ -8,7 +8,7 @@ import (
func (t Team) GetHistory() ([]map[string]interface{}, error) {
hist := make([]map[string]interface{}, 0)
if rows, err := DBQuery(`SELECT id_team, "tries" AS kind, time, E.id_exercice, E.title, NULL, NULL FROM exercice_tries T INNER JOIN exercices E ON E.id_exercice = T.id_exercice WHERE id_team = ? UNION SELECT id_team, "solved" AS kind, time, E.id_exercice, E.title, coefficient, NULL FROM exercice_solved S INNER JOIN exercices E ON E.id_exercice = S.id_exercice WHERE id_team = ? UNION SELECT id_team, "hint" AS kind, time, E.id_exercice, E.title, H.id_hint, H.title FROM team_hints T INNER JOIN exercice_hints H ON H.id_hint = T.id_hint INNER JOIN exercices E ON E.id_exercice = H.id_exercice WHERE id_team = ? UNION SELECT id_team, "key_found" AS kind, time, E.id_exercice, E.title, K.id_key, K.type FROM key_found F INNER JOIN exercice_keys K ON K.id_key = F.id_key INNER JOIN exercices E ON K.id_exercice = E.id_exercice WHERE id_team = ? ORDER BY time DESC`, t.Id, t.Id, t.Id, t.Id); err != nil {
if rows, err := DBQuery(`SELECT id_team, "tries" AS kind, time, E.id_exercice, E.title, NULL, NULL FROM exercice_tries T INNER JOIN exercices E ON E.id_exercice = T.id_exercice WHERE id_team = ? UNION SELECT id_team, "solved" AS kind, time, E.id_exercice, E.title, coefficient, NULL FROM exercice_solved S INNER JOIN exercices E ON E.id_exercice = S.id_exercice WHERE id_team = ? UNION SELECT id_team, "hint" AS kind, time, E.id_exercice, E.title, H.id_hint, H.title FROM team_hints T INNER JOIN exercice_hints H ON H.id_hint = T.id_hint INNER JOIN exercices E ON E.id_exercice = H.id_exercice WHERE id_team = ? UNION SELECT id_team, "flag_found" AS kind, time, E.id_exercice, E.title, K.id_flag, K.type FROM flag_found F INNER JOIN exercice_flags K ON K.id_flag = F.id_flag INNER JOIN exercices E ON K.id_exercice = E.id_exercice WHERE id_team = ? ORDER BY time DESC`, t.Id, t.Id, t.Id, t.Id); err != nil {
return nil, err
} else {
defer rows.Close()
@ -64,8 +64,8 @@ func (t Team) DelHistoryItem(kind string, h time.Time, primary *int64, secondary
} else {
return nb, err
}
} else if kind == "key_found" && secondary != nil {
if res, err := DBExec("DELETE FROM key_found WHERE id_team = ? AND time = ? AND id_key = ?", t.Id, h, *secondary); err != nil {
} else if kind == "flag_found" && secondary != nil {
if res, err := DBExec("DELETE FROM flag_found WHERE id_team = ? AND time = ? AND id_flag = ?", t.Id, h, *secondary); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err

View File

@ -32,7 +32,7 @@ type myTeamExercice struct {
Hints []myTeamHint `json:"hints,omitempty"`
Gain int `json:"gain"`
Files []myTeamFile `json:"files,omitempty"`
Keys []string `json:"keys,omitempty"`
Flags []string `json:"keys,omitempty"`
SolvedMat []bool `json:"solved_matrix,omitempty"`
MCQs []myTeamMCQ `json:"mcqs,omitempty"`
SolveDist int64 `json:"solve_dist,omitempty"`
@ -157,18 +157,18 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
}
}
// Expose exercice keys
// Expose exercice flags
exercice.Keys = []string{}
exercice.Flags = []string{}
if keys, err := e.GetKeys(); err != nil {
if flags, err := e.GetFlags(); err != nil {
return nil, err
} else {
for _, k := range keys {
for _, k := range flags {
if t == nil {
exercice.Keys = append(exercice.Keys, fmt.Sprintf("%x", k.Checksum)+k.Label)
exercice.Flags = append(exercice.Flags, fmt.Sprintf("%x", k.Checksum)+k.Label)
} else {
exercice.Keys = append(exercice.Keys, k.Label)
exercice.Flags = append(exercice.Flags, k.Label)
if PartialValidation {
exercice.SolvedMat = append(exercice.SolvedMat, t.HasPartiallySolved(k) != nil)
}