Compare commits

...

8 Commits
master ... srs

Author SHA1 Message Date
nemunaire 1713db351e SRS branch 2016-10-13 20:20:35 +02:00
nemunaire 8c2aa18495 Also display team without point 2016-10-13 20:20:34 +02:00
nemunaire 9fd786cb04 [admin] Improve interface 2016-10-13 20:16:54 +02:00
nemunaire fd8778f533 [admin] statistic generation 2016-10-13 20:16:54 +02:00
nemunaire b2051ac3fe [admin] Can retrieves tries rate 2016-10-13 20:16:53 +02:00
nemunaire 83b3600e60 Ready to publish solutions 2016-10-13 20:16:42 +02:00
nemunaire 6cc54635a5 frontend: log time.json GET 2016-10-13 19:56:33 +02:00
nemunaire 047e8f63a7 Add translation to french 2016-10-13 19:56:33 +02:00
17 changed files with 751 additions and 52 deletions

View File

@ -85,6 +85,14 @@ func listTeam(args []string, body []byte) (interface{}, error) {
return fic.MyJSONTeam(team, true)
} else if args[1] == "wait.json" {
return fic.MyJSONTeam(team, false)
} else if args[1] == "stats.json" {
if team != nil {
return team.GetStats()
} else {
return fic.GetTeamsStats(nil)
}
} else if args[1] == "tries" {
return fic.GetTries(team, nil)
} else if team != nil && args[1] == "members" {
return team.GetMembers()
} else if args[1] == "certificate" && team != nil {
@ -95,6 +103,8 @@ func listTeam(args []string, body []byte) (interface{}, error) {
} else if len(args) == 1 {
if args[0] == "teams.json" {
return fic.ExportTeams()
} else if args[0] == "tries" {
return fic.GetTries(nil, nil)
} else if args[0] == "nginx" {
return nginxGenTeam()
} else if args[0] == "binding" {

BIN
admin/static/img/epita.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
admin/static/img/fic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
admin/static/img/srs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -2,15 +2,40 @@
<html ng-app="FICApp">
<head>
<meta charset="utf-8">
<title>Challenge Forensic FIC 2016 - Administration</title>
<title>Challenge Forensic - Administration</title>
<link href="/css/bootstrap.min.css" rel="stylesheet">
<base href="/">
<script src="//d3js.org/d3.v3.min.js"></script>
</head>
<body>
<div class="container">
<div class="page-header">
<h1>Challenge FIC Epita!</h1>
<nav class="navbar navbar-inverse navbar-static-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">
<img alt="FIC" src="img/fic.png" style="height: 100%">
</a>
</div>
<ul class="nav navbar-nav">
<li><a href="/teams">&Eacute;quipes</a></li>
<li><a href="/themes">Thèmes</a></li>
<li><a href="/exercices">Exercices</a></li>
<li><a href="/events">&Eacute;vénements</a></li>
</ul>
<p id="clock" class="navbar-text navbar-right" ng-controller="CountdownController">
<span id="hours">{{ time.hours | time }}</span>
<span class="point">:</span>
<span id="min">{{ time.minutes | time }}</span>
<span class="point">:</span>
<span id="sec">{{ time.seconds | time }}</span>
</p>
</div>
</nav>
<div class="container">
<div class="row">
<div class="col-sm-12" ng-view></div>
</div>

View File

@ -17,34 +17,89 @@ angular.module("FICApp", ["ngRoute", "ngResource"])
controller: "TeamsListController",
templateUrl: "views/team-list.html"
})
.when("/teams/:teamId", {
controller: "TeamController",
templateUrl: "views/team.html"
})
.when("/teams/new", {
controller: "TeamNewController",
templateUrl: "views/team-new.html"
})
.when("/", {
templateUrl: "views/home.html"
});
$locationProvider.html5Mode(true);
});
angular.module("FICApp")
.factory("Version", function($resource) {
return $resource("/api/version")
})
.factory("Team", function($resource) {
return $resource("/api/teams/:teamId", { teamId: '@id' }, {
'save': {method: 'PATCH'},
})
});
angular.module("FICApp")
})
.factory("TeamMember", function($resource) {
return $resource("/api/teams/:teamId/members", { teamId: '@id' })
})
.factory("TeamMy", function($resource) {
return $resource("/api/teams/:teamId/my.json", { teamId: '@id' })
})
.factory("Teams", function($resource) {
return $resource("/api/teams/teams.json")
})
.factory("TeamStats", function($resource) {
return $resource("/api/teams/:teamId/stats.json", { teamId: '@id' })
})
.factory("TeamPresence", function($resource) {
return $resource("/api/teams/:teamId/tries", { teamId: '@id' })
})
.factory("Theme", function($resource) {
return $resource("/api/themes/:themeId", null, {
'save': {method: 'PATCH'},
})
});
angular.module("FICApp")
.factory("Exercice", function($resource) {
return $resource("/api/themes/:themeId/:exerciceId", null, {
'query': {method: "GET", url: "/api/themes/:themeId/exercices", isArray: true},
'save': {method: 'PATCH'},
})
.factory("Themes", function($resource) {
return $resource("/api/themes/themes.json", null, {
'get': {method: 'GET'},
})
})
.factory("Exercice", function($resource) {
return $resource("/api/exercices/:exerciceId")
});
String.prototype.capitalize = function() {
return this
.toLowerCase()
.replace(
/(^|\s)([a-z])/g,
function(m,p1,p2) { return p1+p2.toUpperCase(); }
);
}
angular.module("FICApp")
.filter("capitalize", function() {
return function(input) {
return input.capitalize();
}
})
.filter("time", function() {
return function(input) {
if (input == undefined) {
return "--";
} else if (input >= 10) {
return input;
} else {
return "0" + input;
}
}
})
.controller("VersionController", function($scope, Version) {
$scope.v = Version.get();
})
.controller("ThemesListController", function($scope, Theme, $location) {
$scope.themes = Theme.query();
$scope.fields = ["id", "name"];
@ -81,14 +136,278 @@ angular.module("FICApp")
.controller("TeamsListController", function($scope, Team, $location) {
$scope.teams = Team.query();
$scope.fields = ["id", "name"];
$scope.fields = ["id", "name", "initialName"];
$scope.show = function(id) {
$location.url("/teams/" + id);
};
})
.controller("TeamController", function($scope, Team, TeamMember, $routeParams) {
$scope.team = Team.get({ teamId: $routeParams.teamId });
$scope.members = TeamMember.query({ teamId: $routeParams.teamId });
})
.controller("TeamStatsController", function($scope, TeamStats, $routeParams) {
$scope.teamstats = TeamStats.get({ teamId: $routeParams.teamId });
$scope.teamstats.$promise.then(function(res) {
solvedByLevelPie("#pieLevels", res.levels);
solvedByThemesPie("#pieThemes", res.themes);
});
})
.controller("TeamExercicesController", function($scope, Teams, Themes, TeamMy, Exercice, $routeParams) {
$scope.teams = Teams.get();
$scope.themes = Themes.get();
$scope.exercices = Exercice.query();
$scope.my = TeamMy.get({ teamId: $routeParams.teamId });
$scope.teams.$promise.then(function(res){
$scope.nb_teams = 0;
$scope.nb_reg_teams = Object.keys(res).length;
angular.forEach(res, function(team, tid) {
if (team.rank)
$scope.nb_teams += 1;
}, 0);
});
$scope.my.$promise.then(function(res){
$scope.solved_exercices = 0;
angular.forEach(res.exercices, function(exercice, eid) {
if (exercice.solved) {
$scope.solved_exercices += 1;
}
}, 0);
});
})
.controller("TeamNewController", function($scope, Team, $location) {
$scope.contact = new Team({
})
})
.controller("PresenceController", function($scope, TeamPresence, $routeParams) {
$scope.presence = TeamPresence.query({ teamId: $routeParams.teamId });
$scope.presence.$promise.then(function(res) {
presenceCal("#presenceCal", res);
});
})
.controller("CountdownController", function($scope, $http, $timeout) {
$scope.time = {};
function updTime() {
$timeout.cancel($scope.cbm);
$scope.cbm = $timeout(updTime, 1000);
if (sessionStorage.userService) {
var time = angular.fromJson(sessionStorage.userService);
var srv_cur = (Date.now() + (time.cu * 1000 - time.he)) / 1000;
var remain = time.du;
if (time.st == Math.floor(srv_cur)) {
$scope.refresh(true);
}
if (time.st > 0 && time.st <= srv_cur) {
remain = time.st + time.du - srv_cur;
}
if (remain < 0) {
remain = 0;
$scope.time.end = true;
$scope.time.expired = true;
} else if (remain < 60) {
$scope.time.end = false;
$scope.time.expired = true;
} else {
$scope.time.end = false;
$scope.time.expired = false;
}
$scope.time.start = time.st * 1000;
$scope.time.duration = time.du;
$scope.time.remaining = remain;
$scope.time.hours = Math.floor(remain / 3600);
$scope.time.minutes = Math.floor((remain % 3600) / 60);
$scope.time.seconds = Math.floor(remain % 60);
}
}
$http.get("/time.json").success(function(time) {
time.he = (new Date()).getTime();
sessionStorage.userService = angular.toJson(time);
updTime();
});
});
function solvedByLevelPie(location, data) {
var width = d3.select(location).node().getBoundingClientRect().width - 10,
height = d3.select(location).node().getBoundingClientRect().width - 10,
radius = Math.min(width, height) / 2,
innerRadius = 0.1 * radius;
var color = d3.scale.ordinal()
.range(["#9E0041", "#C32F4B", "#E1514B", "#F47245", "#FB9F59", "#FEC574", "#FAE38C", "#EAD195", "#C7E89E", "#9CD6A4", "#6CC4A4", "#4D9DB4", "#4776B4", "#5E4EA1"]);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.width; });
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(function (d) {
return (radius - innerRadius) * (d.data.score / 100.0) + innerRadius;
});
var outlineArc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(radius);
var svg = d3.select(location).append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
data.forEach(function(d) {
d.score = d.solved * 100 / d.total;
d.width = d.tries + 1;
});
var path = svg.selectAll(".solidArc")
.data(pie(data))
.enter().append("path")
.attr("fill", function(d) { return color(d.data.tip); })
.attr("class", "solidArc")
.attr("stroke", "gray")
.attr("d", arc);
var outerPath = svg.selectAll(".outlineArc")
.data(pie(data))
.enter().append("path")
.attr("fill", "none")
.attr("stroke", "gray")
.attr("class", "outlineArc")
.attr("d", outlineArc);
var labelArc = d3.svg.arc()
.outerRadius(0.8 * radius)
.innerRadius(0.8 * radius);
svg.selectAll(".labelArc")
.data(pie(data))
.enter().append("text")
.attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.data.tip + ": " + d.data.solved + "/" + d.data.total; });
svg.selectAll(".label2Arc")
.data(pie(data))
.enter().append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.data.tries; });
}
function solvedByThemesPie(location, data) {
var width = d3.select(location).node().getBoundingClientRect().width,
height = d3.select(location).node().getBoundingClientRect().width,
radius = Math.min(width, height) / 2,
innerRadius = 0.1 * radius;
var color = d3.scale.ordinal()
.range(["#9E0041", "#C32F4B", "#E1514B", "#F47245", "#FB9F59", "#FEC574", "#FAE38C", "#EAD195", "#C7E89E", "#9CD6A4", "#6CC4A4", "#4D9DB4", "#4776B4", "#5E4EA1"]);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.width; });
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(function (d) {
return (radius - innerRadius) * (d.data.score / 100.0) + innerRadius;
});
var outlineArc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(radius);
var svg = d3.select(location).append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
data.forEach(function(d) {
d.score = d.solved * 100 / d.total;
d.width = d.tries + 0.5;
});
var path = svg.selectAll(".solidArc")
.data(pie(data))
.enter().append("path")
.attr("fill", function(d) { return color(d.data.tip); })
.attr("class", "solidArc")
.attr("stroke", "gray")
.attr("d", arc);
var outerPath = svg.selectAll(".outlineArc")
.data(pie(data))
.enter().append("path")
.attr("fill", "none")
.attr("stroke", "gray")
.attr("class", "outlineArc")
.attr("d", outlineArc);
svg.selectAll(".label2Arc")
.data(pie(data))
.enter().append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.data.solved; });
var labelArc = d3.svg.arc()
.outerRadius(0.8 * radius)
.innerRadius(0.8 * radius);
svg.selectAll(".labelArc")
.data(pie(data))
.enter().append("text")
.attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.data.tip + ": " + d.data.tries; });
}
function presenceCal(location, data) {
var width = d3.select(location).node().getBoundingClientRect().width,
height = 80,
cellSize = 17; // cell size
var percent = d3.format(".1%"),
format = d3.time.format("%H:%M");
var color = d3.scale.quantize()
.domain([0, 16])
.range(d3.range(8).map(function(d) { return "q" + d + "-8"; }));
var svg = d3.select(location).selectAll("svg")
.data(d3.range(26, 29))
.enter().append("svg")
.attr("width", width)
.attr("height", height)
.attr("class", "RdYlGn")
.append("g")
.attr("transform", "translate(" + ((width - cellSize * 24) / 2) + "," + (height - cellSize * 4 - 1) + ")");
svg.append("text")
.attr("transform", "translate(-6," + cellSize * 2.6 + ")rotate(-90)")
.style("text-anchor", "middle")
.text(function(d) { return d + "-02"; });
var rect = svg.selectAll(".quarter")
.data(function(d) { return d3.time.minutes(new Date(2016, 1, d, 0), new Date(2016, 1, d, 24), 15); })
.enter().append("rect")
.attr("width", cellSize)
.attr("height", cellSize)
.attr("class", function(d) { return color(data.reduce(function(prev, cur){
cur = new Date(cur).getTime();
dv = d.getTime();
return prev + ((dv <= cur && cur < dv+15*60000)?1:0);
}, 0));
}

View File

@ -0,0 +1,9 @@
<div class="well well-lg">
<h3>Interface d'administration du challenge</h3>
<p>
Sélectionnez une action dans le menu ci-dessus.
</p>
<p ng-controller="VersionController">
Version de l'API : {{ v.version }}
</p>
</div>

View File

@ -0,0 +1,54 @@
<style>
.RdYlGn .q0-8{fill:rgb(245,250,250)}
.RdYlGn .q1-8{fill:rgb(190,200,200)}
.RdYlGn .q2-8{fill:rgb(170,180,180)}
.RdYlGn .q3-8{fill:rgb(150,160,160)}
.RdYlGn .q4-8{fill:rgb(130,140,140)}
.RdYlGn .q5-8{fill:rgb(110,120,120)}
.RdYlGn .q6-8{fill:rgb(90,100,100)}
.RdYlGn .q7-8{fill:rgb(70,80,80)}
</style>
<h1>{{ team.name }}<span ng-show="team.name != team.initialName"> ({{ team.initialName}})</span> <small><span ng-repeat="member in members"><span ng-show="$last && !$first"> et </span><span ng-show="$middle">, </span>{{ member.firstname | capitalize }} <em ng-show="member.nickname">{{ member.nickname }}</em> {{ member.lastname | capitalize }}</span></small></h1>
<div ng-controller="TeamExercicesController">
<dl class="dl-horizontal">
<dt>Points</dt>
<dd>{{ my.score }}</dd>
<dt>Classement</dt>
<dd>{{ teams[my.team_id].rank }}/{{ nb_teams }} ({{ nb_reg_teams }} registered teams)</dd>
</dl>
<h2>Présence</h2>
<div id="presenceCal" ng-controller="PresenceController">
</div>
<h2>Exercices résolus : {{ solved_exercices }}/{{ exercices.length }} {{ solved_exercices * 100 / exercices.length | number:0 }}%</h2>
<dl>
<div style="float: left;padding: 0 5px; margin: 5px; border: 1px solid #ccc; border-radius: 3px; min-width: 5vw" ng-repeat="(tid,theme) in themes" class="text-center">
<dt>{{ theme.name }}</dt>
<dd>
<ul class="list-unstyled">
<li ng-repeat="(eid,exercice) in theme.exercices" ng-show="my.exercices[eid] && my.exercices[eid].solved"><a href="https://fic.srs.epita.fr/{{ my.exercices[eid].theme_id }}/{{ eid }}" target="_blank"><abbr title="{{ my.exercices[eid].statement }}">{{ exercice.title }}</abbr></a> (<abbr title="{{ my.exercices[eid].solved_time | date:'mediumDate' }} à {{ my.exercices[eid].solved_time | date:'mediumTime' }}">{{ my.exercices[eid].solved_number }}<sup>e</sup></abbr>)</li>
</ul>
</dd>
</div>
</dl>
<div class="clearfix"></div>
<div class="container" ng-controller="TeamStatsController">
<div class="row">
<div class="col-sm-6" id="pieLevels">
<h4 class="text-center">Tentatives par niveaux</h4>
</div>
<div class="col-sm-6" id="pieThemes">
<h4 class="text-center">Tentatives par thèmes</h4>
</div>
</div>
</div>
</div>

View File

@ -32,7 +32,12 @@
<div class="navbar navbar-default">
<div class="container">
<div class="row">
<div class="navbar-header col-sm-3">
<div class="navbar-header col-sm-3" ng-show="!(time.start || my.team_id)">
<a href="https://www.forum-fic.com/">
<img src="/img/fic.png" alt="Forum International de la Cybersécurité" class="center-block">
</a>
</div>
<div class="navbar-header col-sm-3" ng-show="(time.start || my.team_id)">
<a href="/">
<img src="/img/fic.png" alt="Forum International de la Cybersécurité" class="center-block">
</a>
@ -49,8 +54,18 @@
<span class="point">:</span>
<span id="sec">{{ time.seconds | time }}</span>
</div>
<div id="clock" class="col-sm-7" ng-show="!(!time.start || my.team_id)">
{{ time.start | date:"shortDate" }}
<div id="clock" class="col-sm-7" ng-show="!(time.start || my.team_id)" style="padding: 25px">
<div class="btn-group btn-group-justified btn-group-lg">
<a class="btn btn-default" href="/">
<span class="glyphicon glyphicon-home"></span> Accueil
</a>
<a class="btn btn-default" href="/rank">
<span class="glyphicon glyphicon-list"></span> Classement
</a>
<a class="btn btn-default" href="https://www.youtube.com/playlist?list=PLSJ8QLhKMtQv7jRhdAn9wXSMYTsvqfieX">
<span class="glyphicon glyphicon-blackboard"></span> Vidéos
</a>
</div>
</div>
</div>
</div>
@ -101,6 +116,7 @@
<script src="/js/bootstrap.min.js"></script>
<script src="/js/angular-route.min.js"></script>
<script src="/js/angular-sanitize.min.js"></script>
<script src="/js/i18n/angular-locale_fr-fr.js"></script>>
<script src="/js/app.js"></script>
</body>
</html>

View File

@ -92,7 +92,7 @@ angular.module("FICApp")
}
}
})
.controller("DataController", function($scope, $http, $rootScope, $timeout) {
.controller("DataController", function($sce, $scope, $http, $rootScope, $timeout) {
var actMenu = function() {
if ($scope.my && $scope.themes) {
angular.forEach($scope.themes, function(theme, key) {
@ -139,6 +139,11 @@ angular.module("FICApp")
}
$http.get("/my.json").success(function(my) {
$scope.my = my;
angular.forEach($scope.my.exercices, function(exercice, eid) {
if (exercice.video_uri) {
exercice.video_uri = $sce.trustAsResourceUrl(exercice.video_uri);
}
});
actMenu();
});
console.log("refresh!");

View File

@ -0,0 +1,124 @@
'use strict';
angular.module("ngLocale", [], ["$provide", function($provide) {
var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
$provide.value("$locale", {
"DATETIME_FORMATS": {
"AMPMS": [
"AM",
"PM"
],
"DAY": [
"dimanche",
"lundi",
"mardi",
"mercredi",
"jeudi",
"vendredi",
"samedi"
],
"ERANAMES": [
"avant J\u00e9sus-Christ",
"apr\u00e8s J\u00e9sus-Christ"
],
"ERAS": [
"av. J.-C.",
"ap. J.-C."
],
"FIRSTDAYOFWEEK": 0,
"MONTH": [
"janvier",
"f\u00e9vrier",
"mars",
"avril",
"mai",
"juin",
"juillet",
"ao\u00fbt",
"septembre",
"octobre",
"novembre",
"d\u00e9cembre"
],
"SHORTDAY": [
"dim.",
"lun.",
"mar.",
"mer.",
"jeu.",
"ven.",
"sam."
],
"SHORTMONTH": [
"janv.",
"f\u00e9vr.",
"mars",
"avr.",
"mai",
"juin",
"juil.",
"ao\u00fbt",
"sept.",
"oct.",
"nov.",
"d\u00e9c."
],
"STANDALONEMONTH": [
"Janvier",
"F\u00e9vrier",
"Mars",
"Avril",
"Mai",
"Juin",
"Juillet",
"Ao\u00fbt",
"Septembre",
"Octobre",
"Novembre",
"D\u00e9cembre"
],
"WEEKENDRANGE": [
5,
6
],
"fullDate": "EEEE d MMMM y",
"longDate": "d MMMM y",
"medium": "d MMM y HH:mm:ss",
"mediumDate": "d MMM y",
"mediumTime": "HH:mm:ss",
"short": "dd/MM/y HH:mm",
"shortDate": "dd/MM/y",
"shortTime": "HH:mm"
},
"NUMBER_FORMATS": {
"CURRENCY_SYM": "\u20ac",
"DECIMAL_SEP": ",",
"GROUP_SEP": "\u00a0",
"PATTERNS": [
{
"gSize": 3,
"lgSize": 3,
"maxFrac": 3,
"minFrac": 0,
"minInt": 1,
"negPre": "-",
"negSuf": "",
"posPre": "",
"posSuf": ""
},
{
"gSize": 3,
"lgSize": 3,
"maxFrac": 2,
"minFrac": 2,
"minInt": 1,
"negPre": "-",
"negSuf": "\u00a0\u00a4",
"posPre": "",
"posSuf": "\u00a0\u00a4"
}
]
},
"id": "fr-fr",
"pluralCat": function(n, opt_precision) { var i = n | 0; if (i == 0 || i == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
});
}]);

View File

@ -199,6 +199,7 @@
<script src="/js/angular-animate.min.js"></script>
<script src="/js/angular-route.min.js"></script>
<script src="/js/angular-sanitize.min.js"></script>
<script src="/js/i18n/angular-locale_fr-fr.js"></script>>
<script src="/js/public.js"></script>
</body>
</html>

View File

@ -11,19 +11,7 @@
Passez voir l'équipe serveur pour corriger cela.
</p>
<p>
Compromissions, défauts de configuration, utilisations malveillantes,
contournements des règles de sécurité, &hellip; tous les jours nous mettons
en danger nos données.
</p>
<p>
Saurez-vous identifier les différents vecteurs de fuites de données avec
lesquels nos systèmes d'informations et nos utilisateurs font faces ?
</p>
<p>
<strong>Attention :</strong> puisqu'il s'agit de captures effectuées dans
le but de découvrir si des actes malveillants ont été commis sur différents
systèmes d'information, les contenus qui sont
téléchargeables <em>peuvent</em> contenir du contenu malveillant !
Le sujet du rush est disponible <a href="2017-RUSH-SRS-DEFNET-FIC.pdf">ici</a>.
</p>
<p>
Bon courage !
@ -74,10 +62,3 @@
</div>
</div>
<div ng-controller="RankController" ng-show="!(my.team_id)">
<ng-include src="'views/rank.html'">
</div>
<!-- Avoid title rewrite... -->
<div ng-controller="HomeController"></div>

View File

@ -44,7 +44,7 @@
<div class="panel-title">Soumettre une solution</div>
</div>
<ul class="list-group" ng-show="(my.exercices[current_exercice].solved_number || my.exercices[current_exercice].submitted || sberr)">
<li class="list-group-item text-warning" ng-show="my.exercices[current_exercice].solved_number">{{ my.exercices[current_exercice].solved_number }} tentative(s) effectuée(s). Dernière solution envoyée à {{ my.exercices[current_exercice].solved_time | date:"fullDate" }}.</li>
<li class="list-group-item text-warning" ng-show="my.exercices[current_exercice].solved_number">{{ my.exercices[current_exercice].solved_number }} tentative(s) effectuée(s). Dernière solution envoyée le {{ my.exercices[current_exercice].solved_time | date:"mediumDate" }} à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}.</li>
<li class="list-group-item" ng-class="messageClass" ng-show="my.exercices[current_exercice].submitted || sberr"><strong ng-show="!sberr">Votre solution a bien été envoyée !</strong><strong ng-show="sberr">{{ sberr }}</strong> {{ message }}</li>
</ul>
<div class="panel-body" ng-show="!my.exercices[current_exercice].submitted || sberr">
@ -65,13 +65,13 @@
<div class="panel-title">Challenge réussi !</div>
</div>
<div class="panel-body">
Vous êtes la {{ my.exercices[current_exercice].solved_number }}<sup>e</sup> équipe à avoir résolu ce challenge à {{ my.exercices[current_exercice].solved_time | date:"fullDate" }}. Vous avez marqué {{ themes[current_theme].exercices[current_exercice].gain }} points !
Vous êtes la {{ my.exercices[current_exercice].solved_number }}<sup>e</sup> équipe à avoir résolu ce challenge le {{ my.exercices[current_exercice].solved_time | date:"mediumDate" }} à {{ my.exercices[current_exercice].solved_time | date:"mediumTime" }}. Vous avez marqué {{ themes[current_theme].exercices[current_exercice].gain }} points !
</div>
</div>
<div class="panel panel-success" ng-show="(!my.team_id && my.exercices[current_exercice].keys)">
<div class="panel-heading">
<div class="panel-title">Clefs du challenge</div>
<div class="panel-title">Solution du challenge</div>
</div>
<div class="panel-body">
<p>
@ -81,5 +81,8 @@
<dt>{{ key.slice(128) }}</dt>
<dd class="samp"><code>{{ key.slice(0, 128) }}</code></dd>
</dl>
<iframe type="text/html" ng-show="my.exercices[current_exercice].video_uri" ng-src="{{ my.exercices[current_exercice].video_uri }}" frameborder="0" style="width: 100%; height: 35vw">
Regardez la vidéo de résolution de cet exercice : <a ng-href="{{ my.exercices[current_exercice].video_uri }}">{{ my.exercices[current_exercice].video_uri }}</a>.
</iframe>
</div>
</div>

View File

@ -47,16 +47,7 @@
Vous n'êtes pas encore connecté en tant qu'équipe sur notre serveur.
</p>
<p>
Après avoir suivi le guide présent sur la clef USB que nous vous
avons remis et ajouté votre certificat dans votre système ou votre
navigateur, ce dernier devrait vous afficher une boîte de dialogue
vous demandant de choisir parmi les certificats installés sur votre
machine.
</p>
<p>
Si vous avez accédé à cette page avant d'avoir ajouté le certificat,
il peut être nécessaire de relancer votre navigateur, afin qu'il
démarre une nouvelle session.
Le sujet est disponible <a href="/RUSH-SRS-DEFNET-FIC.pdf">ici</a> !
</p>
<p>
Si malgré tout, vous n'arrivez pas à accéder à l'espace de votre

View File

@ -3,6 +3,7 @@ package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"time"
)
@ -19,6 +20,8 @@ type timeObject struct {
}
func (t TimeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf("Handling %s request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
w.Header().Set("Content-Type", "application/json")
if j, err := json.Marshal(timeObject{t.StartTime.Unix(), time.Now().Unix(), int(t.Duration.Seconds())}); err != nil {

View File

@ -1,6 +1,7 @@
package fic
import (
"database/sql"
"fmt"
"time"
)
@ -93,7 +94,7 @@ func (t Team) GetPoints() (int64, error) {
}
func GetRank() (map[int64]int, error) {
if rows, err := DBQuery("SELECT id_team, SUM(E.gain) AS score, MAX(S.time) FROM exercice_solved S INNER JOIN exercices E ON E.id_exercice = S.id_exercice GROUP BY id_team HAVING score > 0 ORDER BY score DESC, time ASC"); err != nil {
if rows, err := DBQuery("SELECT id_team, SUM(E.gain) AS score, MAX(S.time) FROM exercice_solved S INNER JOIN exercices E ON E.id_exercice = S.id_exercice GROUP BY id_team ORDER BY score DESC, time ASC"); err != nil {
return nil, err
} else {
defer rows.Close()
@ -119,7 +120,7 @@ func GetRank() (map[int64]int, error) {
}
func GetTryRank() ([]int64, error) {
if rows, err := DBQuery("SELECT id_team, COUNT(*) AS score FROM exercice_tries GROUP BY id_team HAVING score > 0 ORDER BY score DESC"); err != nil {
if rows, err := DBQuery("SELECT id_team, COUNT(*) AS score FROM exercice_tries GROUP BY id_team ORDER BY score DESC"); err != nil {
return nil, err
} else {
defer rows.Close()
@ -152,6 +153,61 @@ func (t Team) HasAccess(e Exercice) bool {
}
}
func NbTry(t *Team, e Exercice) int {
var cnt *int
if t != nil {
DBQueryRow("SELECT COUNT(*) FROM exercice_tries WHERE id_team = ? AND id_exercice = ?", t.Id, e.Id).Scan(&cnt)
} else {
DBQueryRow("SELECT COUNT(*) FROM exercice_tries WHERE id_exercice = ?", e.Id).Scan(&cnt)
}
if cnt == nil {
return 0
} else {
return *cnt
}
}
func GetTries(t *Team, e *Exercice) ([]time.Time, error) {
var rows *sql.Rows
var err error
if t == nil {
if e == nil {
rows, err = DBQuery("SELECT time FROM exercice_tries ORDER BY time ASC")
} else {
rows, err = DBQuery("SELECT time FROM exercice_tries WHERE id_exercice = ? ORDER BY time ASC", e.Id)
}
} else {
if e == nil {
rows, err = DBQuery("SELECT time FROM exercice_tries WHERE id_team = ? ORDER BY time ASC", t.Id)
} else {
rows, err = DBQuery("SELECT time FROM exercice_tries WHERE id_team = ? AND id_exercice = ? ORDER BY time ASC", t.Id, e.Id)
}
}
if err != nil {
return nil, err
} else {
defer rows.Close()
times := make([]time.Time, 0)
for rows.Next() {
var tm time.Time
if err := rows.Scan(&tm); err != nil {
return nil, err
}
times = append(times, tm)
}
if err := rows.Err(); err != nil {
return nil, err
}
return times, nil
}
}
func (t Team) HasSolved(e Exercice) (bool, time.Time, int64) {
var nb *int64
var tm *time.Time
@ -170,6 +226,108 @@ func (t Team) HasSolved(e Exercice) (bool, time.Time, int64) {
}
}
func IsSolved(e Exercice) (int, time.Time) {
var nb *int
var tm *time.Time
if DBQueryRow("SELECT COUNT(id_exercice), MIN(time) FROM exercice_solved WHERE id_exercice = ?", e.Id).Scan(&nb, &tm); nb == nil || tm == nil {
return 0, time.Time{}
} else {
return *nb, *tm
}
}
type statLine struct {
Tip string `json:"tip"`
Total int `json:"total"`
Solved int `json:"solved"`
Tried int `json:"tried"`
Tries int `json:"tries"`
}
type teamStats struct {
Levels []statLine `json:"levels"`
Themes []statLine `json:"themes"`
}
func (s *teamStats) GetLevel(level int) *statLine {
level -= 1
for len(s.Levels) <= level {
s.Levels = append(s.Levels, statLine{
fmt.Sprintf("Level %d", (len(s.Levels) + 1)),
0,
0,
0,
0,
})
}
return &s.Levels[level]
}
func (t Team) GetStats() (interface{}, error) {
return GetTeamsStats(&t)
}
func GetTeamsStats(t *Team) (interface{}, error) {
stat := teamStats{}
if themes, err := GetThemes(); err != nil {
return nil, err
} else {
for _, theme := range themes {
total := 0
solved := 0
tried := 0
tries := 0
if exercices, err := theme.GetExercices(); err != nil {
return nil, err
} else {
for _, exercice := range exercices {
var lvl int
if lvl, err = exercice.GetLevel(); err != nil {
return nil, err
}
sLvl := stat.GetLevel(lvl)
total += 1
sLvl.Total += 1
if t != nil {
if b, _, _ := t.HasSolved(exercice); b {
solved += 1
sLvl.Solved += 1
}
} else {
if n, _ := IsSolved(exercice); n > 0 {
solved += 1
sLvl.Solved += 1
}
}
try := NbTry(t, exercice)
if try > 0 {
tried += 1
tries += try
sLvl.Tried += 1
sLvl.Tries += try
}
}
}
stat.Themes = append(stat.Themes, statLine{
theme.Name,
total,
solved,
tried,
tries,
})
}
return stat, nil
}
}
type exportedTeam struct {
Name string `json:"name"`