Skip to content

Commit

Permalink
Add new REQUIRE_AUTHENTICATED_SUBMISSIONS config option (#2119)
Browse files Browse the repository at this point in the history
This builds on top of the work done in #2115, and adds a new
`REQUIRE_AUTHENTICATED_SUBMISSIONS` configuration option which requires
regular users to enable authenticated submissions when creating or
editing projects. Instance administrators are able to override this
setting.

This should be merged after #2115.
  • Loading branch information
williamjallen authored Apr 4, 2024
1 parent 5715655 commit ff21d16
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
# Whether or not a Project administrator can register a user
# PROJECT_ADMIN_REGISTRATION_FORM_ENABLED=true

# Require all new projects to use authenticated submissions.
# Instance administrators can override this, and this setting has no effect on
# existing projects.
# REQUIRE_AUTHENTICATED_SUBMISSIONS=false


# ldap.php
#LDAP_HOSTS=
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/ProjectController.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public function apiCreateProject(): JsonResponse
}
} else {
// Initialize some variables for project creation.
$project_response['AuthenticateSubmissions'] = 0;
$project_response['AuthenticateSubmissions'] = (bool) config('cdash.require_authenticated_submissions') ? 1 : 0;
$project_response['Public'] = Project::ACCESS_PRIVATE;
$project_response['AutoremoveMaxBuilds'] = 500;
$project_response['AutoremoveTimeframe'] = 60;
Expand Down
31 changes: 31 additions & 0 deletions app/Rules/ProjectAuthenticateSubmissions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Auth;
use Illuminate\Translation\PotentiallyTranslatedString;

class ProjectAuthenticateSubmissions implements ValidationRule
{
/**
* Run the validation rule.
*
* @param Closure(string): PotentiallyTranslatedString $fail
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$value = (bool) $value;

$user = Auth::user();
if ($user !== null && $user->admin) {
// Instance admins can set any value they wish
return;
}

if ((bool) config('cdash.require_authenticated_submissions') && !$value) {
$fail('This CDash instance is configured to require authenticated submissions.');
}
}
}
10 changes: 7 additions & 3 deletions app/cdash/public/api/v1/project.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
require_once 'include/api_common.php';


use App\Rules\ProjectAuthenticateSubmissions;
use App\Rules\ProjectVisibilityAllowed;
use App\Utils\RepositoryUtils;
use CDash\Model\Project;
Expand Down Expand Up @@ -258,12 +259,15 @@ function populate_project($Project)
$project_settings['CvsUrl'] = str_replace('&amp;', '&', $cvsurl);
}

if (Validator::make([
$validator = Validator::make([
'visibility' => $project_settings['Public'],
'authenticatesubmissions' => (bool) ($project_settings['AuthenticateSubmissions'] ?? false),
], [
'visibility' => new ProjectVisibilityAllowed(),
])->fails()) {
abort(403, "Project visibility {$project_settings['Public']} prohibited for this instance.");
'authenticatesubmissions' => new ProjectAuthenticateSubmissions(),
]);
if ($validator->fails()) {
abort(403, $validator->messages());
}

foreach ($project_settings as $k => $v) {
Expand Down
1 change: 1 addition & 0 deletions config/cdash.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,6 @@
'user_create_projects' => env('USER_CREATE_PROJECTS', false),
// Defaults to public. Only meaningful if USER_CREATE_PROJECT=true.
'max_project_visibility' => env('MAX_PROJECT_VISIBILITY', 'PUBLIC'),
'require_authenticated_submissions' => env('REQUIRE_AUTHENTICATED_SUBMISSIONS', false),
'use_vcs_api' => env('USE_VCS_API', true),
];
6 changes: 6 additions & 0 deletions graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ type Project {
"Visibility."
visibility: ProjectVisibility! @rename(attribute: "public")

"A boolean indicating whether authenticated submissions are required."
authenticateSubmissions: Boolean! @rename(attribute: "authenticatesubmissions")

builds: [Build!]! @hasMany @orderBy(column: "id")

"The sites which have submitted a build to this project."
Expand Down Expand Up @@ -97,6 +100,9 @@ input CreateProjectInput {

"Visibility."
visibility: ProjectVisibility! @rename(attribute: "public") @rules(attribute: "public", apply: ["App\\Rules\\ProjectVisibilityAllowed"])

"A boolean indicating whether authenticated submissions are required."
authenticateSubmissions: Boolean! @rename(attribute: "authenticatesubmissions") @rules(attribute: "authenticatesubmissions", apply: ["App\\Rules\\ProjectAuthenticateSubmissions"])
}


Expand Down
2 changes: 1 addition & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -29726,7 +29726,7 @@ parameters:

-
message: "#^Dynamic call to static method Illuminate\\\\Testing\\\\TestResponse\\:\\:assertGraphQLErrorMessage\\(\\)\\.$#"
count: 4
count: 5
path: tests/Feature/GraphQL/ProjectTest.php

-
Expand Down
79 changes: 79 additions & 0 deletions tests/Feature/GraphQL/ProjectTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ public function testCreateProjectNoUser(): void
'description' => 'test',
'homeurl' => 'https://cdash.org',
'visibility' => 'PUBLIC',
'authenticateSubmissions' => false,
],
])->assertGraphQLErrorMessage('This action is unauthorized.');

Expand All @@ -341,6 +342,7 @@ public function testCreateProjectUnauthorizedUser(): void
'description' => 'test',
'homeurl' => 'https://cdash.org',
'visibility' => 'PUBLIC',
'authenticateSubmissions' => false,
],
])->assertGraphQLErrorMessage('This action is unauthorized.');

Expand Down Expand Up @@ -368,6 +370,7 @@ public function testCreateProjectUserCreateProjectNoUser(): void
'description' => 'test',
'homeurl' => 'https://cdash.org',
'visibility' => 'PUBLIC',
'authenticateSubmissions' => false,
],
])->assertGraphQLErrorMessage('This action is unauthorized.');

Expand Down Expand Up @@ -395,6 +398,7 @@ public function testCreateProjectUserCreateProject(): void
'description' => 'test',
'homeurl' => 'https://cdash.org',
'visibility' => 'PUBLIC',
'authenticateSubmissions' => false,
],
]);

Expand Down Expand Up @@ -428,6 +432,7 @@ public function testCreateProjectAdmin(): void
'description' => 'test',
'homeurl' => 'https://cdash.org',
'visibility' => 'PUBLIC',
'authenticateSubmissions' => false,
],
]);

Expand Down Expand Up @@ -691,6 +696,7 @@ public function testCreateProjectMaxVisibility(string $user, string $visibility,
'description' => 'test',
'homeurl' => 'https://cdash.org',
'visibility' => $visibility,
'authenticateSubmissions' => false,
],
]);

Expand All @@ -712,4 +718,77 @@ public function testCreateProjectMaxVisibility(string $user, string $visibility,
$response->assertGraphQLErrorMessage('Validation failed for the field [createProject].');
}
}

/**
* @return array{
* array{
* string,
* bool,
* bool,
* bool
* }
* }
*/
public function authenticatedSubmissionRules(): array
{
return [
['normal', false, false, true],
['normal', true, false, true],
['normal', false, true, false],
['normal', true, true, true],
// Instance admins can set any value
['admin', false, false, true],
['admin', true, false, true],
['admin', false, true, true],
['admin', true, true, true],
];
}

/**
* @dataProvider authenticatedSubmissionRules
*/
public function testRequireAuthenticatedSubmissions(
string $user,
bool $use_authenticated_submits,
bool $require_authenticated_submissions,
bool $result
): void {
Config::set('cdash.user_create_projects', true);
Config::set('cdash.require_authenticated_submissions', $require_authenticated_submissions);

$name = 'test-project' . Str::uuid();
$response = $this->actingAs($this->users[$user])->graphQL('
mutation CreateProject($input: CreateProjectInput!) {
createProject(input: $input) {
authenticateSubmissions
}
}
', [
'input' => [
'name' => $name,
'description' => 'test',
'homeurl' => 'https://cdash.org',
'visibility' => 'PUBLIC',
'authenticateSubmissions' => $use_authenticated_submits,
],
]);

if ($result) {
$project = Project::where('name', $name)->firstOrFail();
$response->assertJson([
'data' => [
'createProject' => [
'authenticateSubmissions' => $use_authenticated_submits,
],
],
], true);
$project->delete();
} else {
// A final check to ensure this project wasn't created anyway
$this->assertDatabaseMissing(Project::class, [
'name' => $name,
]);
$response->assertGraphQLErrorMessage('Validation failed for the field [createProject].');
}
}
}

0 comments on commit ff21d16

Please sign in to comment.