Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hide meta repository from users #1115

Merged
merged 4 commits into from
Mar 20, 2025
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
@@ -292,7 +292,7 @@ private CompletableFuture<Void> purgeRepository(PurgeRepositoryCommand c) {
}

private CompletableFuture<CommitResult> push(RepositoryCommand<?> c, boolean normalizing) {
if (c.projectName().equals(INTERNAL_PROJECT_DOGMA) || c.repositoryName().equals(Project.REPO_DOGMA) ||
if (c.projectName().equals(INTERNAL_PROJECT_DOGMA) || Project.isInternalRepo(c.repositoryName()) ||
!writeQuotaEnabled()) {
return push0(c, normalizing);
}
Original file line number Diff line number Diff line change
@@ -60,7 +60,6 @@
import com.linecorp.armeria.server.annotation.RequestConverter;
import com.linecorp.centraldogma.common.Author;
import com.linecorp.centraldogma.common.Change;
import com.linecorp.centraldogma.common.ChangeType;
import com.linecorp.centraldogma.common.Entry;
import com.linecorp.centraldogma.common.InvalidPushException;
import com.linecorp.centraldogma.common.Markup;
@@ -79,15 +78,13 @@
import com.linecorp.centraldogma.server.command.Command;
import com.linecorp.centraldogma.server.command.CommandExecutor;
import com.linecorp.centraldogma.server.command.CommitResult;
import com.linecorp.centraldogma.server.internal.admin.auth.AuthUtil;
import com.linecorp.centraldogma.server.internal.api.auth.RequiresRepositoryRole;
import com.linecorp.centraldogma.server.internal.api.converter.ChangesRequestConverter;
import com.linecorp.centraldogma.server.internal.api.converter.CommitMessageRequestConverter;
import com.linecorp.centraldogma.server.internal.api.converter.MergeQueryRequestConverter;
import com.linecorp.centraldogma.server.internal.api.converter.QueryRequestConverter;
import com.linecorp.centraldogma.server.internal.api.converter.WatchRequestConverter;
import com.linecorp.centraldogma.server.internal.api.converter.WatchRequestConverter.WatchRequest;
import com.linecorp.centraldogma.server.metadata.User;
import com.linecorp.centraldogma.server.storage.project.Project;
import com.linecorp.centraldogma.server.storage.repository.FindOption;
import com.linecorp.centraldogma.server.storage.repository.FindOptions;
@@ -201,8 +198,7 @@ public CompletableFuture<PushResultDto> push(
Author author,
CommitMessageDto commitMessage,
@RequestConverter(ChangesRequestConverter.class) Iterable<Change<?>> changes) {
final User user = AuthUtil.currentUser(ctx);
checkPush(repository.name(), changes, user.isSystemAdmin());
checkMetaRepoPush(repository.name(), changes);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we remove this validation as accessing meta is prohibited by the decorator?

Copy link
Contributor Author

@minwoox minwoox Mar 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

meterRegistry.counter("commits.push",
"project", repository.parent().name(),
"repository", repository.name())
@@ -445,11 +441,7 @@ public <T> CompletableFuture<MergedEntryDto<T>> mergeFiles(
return repository.mergeFiles(rev, query).thenApply(DtoConverter::convert);
}

/**
* Checks if the commit is for creating a file and raises a {@link InvalidPushException} if the
* given {@code repoName} field is one of {@code meta} and {@code dogma} which are internal repositories.
*/
public static void checkPush(String repoName, Iterable<Change<?>> changes, boolean isSystemAdmin) {
public static void checkMetaRepoPush(String repoName, Iterable<Change<?>> changes) {
if (Project.REPO_META.equals(repoName)) {
final boolean hasChangesOtherThanMetaRepoFiles =
Streams.stream(changes).anyMatch(change -> !isMetaFile(change.path()));
@@ -458,31 +450,6 @@ public static void checkPush(String repoName, Iterable<Change<?>> changes, boole
"The " + Project.REPO_META + " repository is reserved for internal usage.");
}

if (isSystemAdmin) {
// A system admin may push the legacy files to test the mirror migration.
} else {
for (Change<?> change : changes) {
// 'mirrors.json' and 'credentials.json' are disallowed to be created or modified.
// 'mirrors/{id}.json' and 'credentials/{id}.json' must be used instead.
final String path = change.path();
if (change.type() == ChangeType.REMOVE) {
continue;
}
if ("/mirrors.json".equals(path)) {
throw new InvalidPushException(
"'/mirrors.json' file is not allowed to create. " +
"Use '/mirrors/{id}.json' file or " +
"'/api/v1/projects/{projectName}/mirrors' API instead.");
}
if ("/credentials.json".equals(path)) {
throw new InvalidPushException(
"'/credentials.json' file is not allowed to create. " +
"Use '/credentials/{id}.json' file or " +
"'/api/v1/projects/{projectName}/credentials' API instead.");
}
}
}

// TODO(ikhoon): Disallow creating a mirror with the commit API. Mirroring REST API should be used
// to validate the input.
final Optional<String> notAllowedLocalRepo =
@@ -499,7 +466,7 @@ public static void checkPush(String repoName, Iterable<Change<?>> changes, boole
final JsonNode localRepoNode = node.get(MIRROR_LOCAL_REPO);
if (localRepoNode != null) {
final String localRepo = localRepoNode.textValue();
if (Project.isReservedRepoName(localRepo)) {
if (Project.isInternalRepo(localRepo)) {
return localRepo;
}
}
Original file line number Diff line number Diff line change
@@ -101,8 +101,7 @@ public CompletableFuture<List<RepositoryDto>> listRepositories(ServiceRequestCon
}

return project.repos().list().values().stream()
.filter(r -> user.isSystemAdmin() || !Project.REPO_DOGMA.equals(r.name()))
.filter(r -> hasOwnerRole || !Project.REPO_META.equals(r.name()))
.filter(r -> user.isSystemAdmin() || !Project.isInternalRepo(r.name()))
.map(DtoConverter::convert)
.collect(toImmutableList());
});
@@ -121,9 +120,9 @@ public CompletableFuture<RepositoryDto> createRepository(ServiceRequestContext c
CreateRepositoryRequest request,
Author author) {
final String repoName = request.name();
if (Project.isReservedRepoName(repoName)) {
if (Project.isInternalRepo(repoName)) {
return HttpApiUtil.throwResponse(ctx, HttpStatus.FORBIDDEN,
"A reserved repository cannot be created.");
"An internal repository cannot be created.");
}
final CommandExecutor commandExecutor = executor();
final CompletableFuture<Revision> future =
@@ -142,9 +141,9 @@ public CompletableFuture<Void> removeRepository(ServiceRequestContext ctx,
@Param String repoName,
Repository repository,
Author author) {
if (Project.isReservedRepoName(repoName)) {
if (Project.isInternalRepo(repoName)) {
return HttpApiUtil.throwResponse(ctx, HttpStatus.FORBIDDEN,
"A reserved repository cannot be removed.");
"An internal repository cannot be removed.");
}
return RepositoryServiceUtil.removeRepository(executor(), mds, author,
repository.parent().name(), repoName)
Original file line number Diff line number Diff line change
@@ -89,7 +89,7 @@ public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exc
return unwrap().serve(ctx, req);
}

if (Project.REPO_DOGMA.equals(repoName)) {
if (Project.isInternalRepo(repoName)) {
return throwForbiddenResponse(ctx, projectName, repoName);
}
return serveUserRepo(ctx, req, user, projectName, maybeRemoveGitSuffix(repoName));
Original file line number Diff line number Diff line change
@@ -70,7 +70,7 @@ public Object convertRequest(
checkArgument(!isNullOrEmpty(repositoryName),
"repository name should not be null or empty.");

if (Project.REPO_DOGMA.equals(repositoryName) && !user.isSystemAdmin()) {
if (Project.isInternalRepo(repositoryName) && !user.isSystemAdmin()) {
return HttpApiUtil.throwResponse(
ctx, HttpStatus.FORBIDDEN,
"Repository '%s/%s' can be accessed only by a system administrator.",
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.linecorp.centraldogma.server.metadata;
package com.linecorp.centraldogma.server.internal.metadata;

import static com.linecorp.centraldogma.server.metadata.MetadataService.METADATA_JSON;

@@ -27,10 +27,11 @@
import com.linecorp.centraldogma.common.Revision;
import com.linecorp.centraldogma.internal.Jackson;
import com.linecorp.centraldogma.server.command.ContentTransformer;
import com.linecorp.centraldogma.server.metadata.ProjectMetadata;

class ProjectMetadataTransformer extends ContentTransformer<JsonNode> {
public class ProjectMetadataTransformer extends ContentTransformer<JsonNode> {

ProjectMetadataTransformer(BiFunction<Revision, ProjectMetadata, ProjectMetadata> transformer) {
public ProjectMetadataTransformer(BiFunction<Revision, ProjectMetadata, ProjectMetadata> transformer) {
super(METADATA_JSON, EntryType.JSON,
(headRevision, jsonNode) -> Jackson.valueToTree(
transformer.apply(headRevision, projectMetadata(jsonNode))));
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2025 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Metadata management for Central Dogma projects.
*/
@NonNullByDefault
package com.linecorp.centraldogma.server.internal.metadata;

import com.linecorp.centraldogma.common.util.NonNullByDefault;
Original file line number Diff line number Diff line change
@@ -1046,7 +1046,7 @@ private static void safeRelease(InterProcessMutex mtx) {
@Nullable
private WriteLock acquireWriteLock(NormalizingPushCommand command) throws Exception {
if (command.projectName().equals(INTERNAL_PROJECT_DOGMA) ||
command.repositoryName().equals(Project.REPO_DOGMA)) {
Project.isInternalRepo(command.repositoryName())) {
// Do not check quota for internal project and repository.
return null;
}
Loading