Skip to content

Commit c679502

Browse files
committed
Use different ID for docker services if indicated by lab user name
1 parent a7308ac commit c679502

4 files changed

Lines changed: 25 additions & 10 deletions

File tree

‎src/Ctf4e.LabServer/Models/State/UserState.cs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ public class UserState
1414
public int? GroupId { get; set; }
1515

1616
public List<UserState> GroupMembers { get; set; }
17+
18+
/// <summary>
19+
/// User ID to use when communicating with container.
20+
/// Usually matches the CTF user ID, but may be overridden when the CTF system specifies a custom user name of form 'userXX'.
21+
/// </summary>
22+
public int DockerUserId { get; set; }
1723

1824
public string UserName { get; set; }
1925

‎src/Ctf4e.LabServer/Models/State/UserStateFile.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class UserStateFile
99
{
1010
public int? GroupId { get; set; }
1111

12+
public int DockerUserId { get; set; }
1213
public string UserName { get; set; }
1314

1415
public string Password { get; set; }

‎src/Ctf4e.LabServer/Services/DockerService.cs‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ namespace Ctf4e.LabServer.Services;
1212

1313
public interface IDockerService
1414
{
15-
Task InitUserAsync(int userId, string userName, string password, CancellationToken cancellationToken);
16-
Task<(bool passed, string stderr)> GradeAsync(string containerName, string gradingScriptPath, int userId, int exerciseId, string input, CancellationToken cancellationToken);
15+
Task InitUserAsync(int dockerUserId, string userName, string password, CancellationToken cancellationToken);
16+
Task<(bool passed, string stderr)> GradeAsync(string containerName, string gradingScriptPath, int dockerUserId, int exerciseId, string input, CancellationToken cancellationToken);
1717
}
1818

1919
public class DockerService : IDockerService, IDisposable
@@ -40,14 +40,14 @@ public DockerService(IOptions<LabOptions> options)
4040
}
4141
}
4242

43-
public async Task InitUserAsync(int userId, string userName, string password, CancellationToken cancellationToken)
43+
public async Task InitUserAsync(int dockerUserId, string userName, string password, CancellationToken cancellationToken)
4444
{
4545
if(_dockerClient == null)
4646
throw new InvalidOperationException("Docker support is not initialized.");
4747

4848
if(string.IsNullOrWhiteSpace(_options.Value.DockerContainerInitUserScriptPath))
4949
throw new NotSupportedException("User initialization script is not specified.");
50-
50+
5151
// Prepare command
5252
var execCreateResponse = await _dockerClient.Exec.ExecCreateContainerAsync(_options.Value.DockerContainerName, new ContainerExecCreateParameters
5353
{
@@ -58,7 +58,7 @@ public async Task InitUserAsync(int userId, string userName, string password, Ca
5858
Cmd = new List<string>
5959
{
6060
_options.Value.DockerContainerInitUserScriptPath,
61-
userId.ToString(),
61+
dockerUserId.ToString(),
6262
userName,
6363
password
6464
},
@@ -70,7 +70,7 @@ public async Task InitUserAsync(int userId, string userName, string password, Ca
7070
await execStream.ReadOutputToEndAsync(cancellationToken);
7171
}
7272

73-
public async Task<(bool passed, string stderr)> GradeAsync(string containerName, string gradingScriptPath, int userId, int exerciseId, string input, CancellationToken cancellationToken)
73+
public async Task<(bool passed, string stderr)> GradeAsync(string containerName, string gradingScriptPath, int dockerUserId, int exerciseId, string input, CancellationToken cancellationToken)
7474
{
7575
if(_dockerClient == null)
7676
throw new InvalidOperationException("Docker support is not initialized.");
@@ -93,7 +93,7 @@ public async Task InitUserAsync(int userId, string userName, string password, Ca
9393
Cmd = new List<string>
9494
{
9595
gradingScriptPath,
96-
userId.ToString(),
96+
dockerUserId.ToString(),
9797
exerciseId.ToString()
9898
},
9999
Detach = false

‎src/Ctf4e.LabServer/Services/StateService.cs‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ public async Task ReloadAsync(CancellationToken cancellationToken)
152152
Lock = new SemaphoreSlim(1, 1),
153153
GroupId = userStateFile.GroupId,
154154
GroupMembers = [],
155+
DockerUserId = userStateFile.DockerUserId,
155156
UserName = userStateFile.UserName,
156157
Password = userStateFile.Password,
157158
Exercises = new ConcurrentDictionary<int, UserStateFileExerciseEntry>(userStateFile.Exercises.ToDictionary(e => e.ExerciseId)),
@@ -251,6 +252,7 @@ private async Task WriteUserStateFileAsync(int userId, CancellationToken cancell
251252
var userStateFile = new UserStateFile
252253
{
253254
GroupId = userState.GroupId,
255+
DockerUserId = userState.DockerUserId,
254256
UserName = userState.UserName,
255257
Password = userState.Password,
256258
Exercises = userState.Exercises.Values.ToList()
@@ -289,14 +291,20 @@ public async Task ProcessLoginAsync(int userId, int? groupId, string labUserName
289291
}
290292
else
291293
{
294+
// If the CTF system sends a lab user name and that matches 'userXX', use 'XX' as docker user ID
295+
int dockerUserId = userId;
296+
if(labUserName != null && labUserName.StartsWith("user") && int.TryParse(labUserName[4..], out int duid))
297+
dockerUserId = duid;
298+
292299
// Create new account
293300
userState = new UserState
294301
{
295302
Lock = new SemaphoreSlim(1, 1),
296303
GroupId = groupId,
297304
GroupMembers = [], // Will be filled later
298305
Exercises = new ConcurrentDictionary<int, UserStateFileExerciseEntry>(),
299-
UserName = labUserName ?? (_dockerSupportEnabled ? $"user{userId}" : null),
306+
DockerUserId = dockerUserId,
307+
UserName = labUserName ?? (_dockerSupportEnabled ? $"user{dockerUserId}" : null),
300308
Password = labPassword ?? (_dockerSupportEnabled ? RandomStringGenerator.GetRandomString(10) : null),
301309
Log = new UserStateLogContainer(Math.Max(1, _options.Value.UserStateLogSize))
302310
};
@@ -323,7 +331,7 @@ public async Task ProcessLoginAsync(int userId, int? groupId, string labUserName
323331
if(_dockerSupportEnabled
324332
&& !string.IsNullOrWhiteSpace(_options.Value.DockerContainerName)
325333
&& !string.IsNullOrWhiteSpace(_options.Value.DockerContainerInitUserScriptPath))
326-
await _dockerService.InitUserAsync(userId, userState.UserName, userState.Password, CancellationToken.None);
334+
await _dockerService.InitUserAsync(userState.DockerUserId, userState.UserName, userState.Password, CancellationToken.None);
327335

328336
// Store new user state
329337
_userStates.TryAdd(userId, userState);
@@ -438,7 +446,7 @@ public async Task<bool> CheckInputAsync(int exerciseId, int userId, object input
438446
var scriptExercise = exercise as LabConfigurationScriptExerciseEntry;
439447
string containerName = scriptExercise.ContainerName ?? _options.Value.DockerContainerName;
440448
string gradingScriptPath = scriptExercise.GradeScriptPath ?? _options.Value.DockerContainerGradeScriptPath;
441-
(correct, string stderr) = await _dockerService.GradeAsync(containerName, gradingScriptPath, userId, exerciseId, input as string, cancellationToken);
449+
(correct, string stderr) = await _dockerService.GradeAsync(containerName, gradingScriptPath, userState.DockerUserId, exerciseId, input as string, cancellationToken);
442450

443451
userState.Log.AddMessage("Script stderr output", stderr);
444452
}

0 commit comments

Comments
 (0)