1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.linecorp.centraldogma.server.internal.api.auth;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Strings.isNullOrEmpty;
21 import static com.linecorp.centraldogma.server.internal.api.auth.RequiresRoleDecorator.handleException;
22 import static java.util.Objects.requireNonNull;
23
24 import java.util.Collection;
25 import java.util.concurrent.CompletionStage;
26 import java.util.function.Function;
27
28 import com.linecorp.armeria.common.HttpRequest;
29 import com.linecorp.armeria.common.HttpResponse;
30 import com.linecorp.armeria.common.HttpStatus;
31 import com.linecorp.armeria.common.util.Exceptions;
32 import com.linecorp.armeria.server.HttpService;
33 import com.linecorp.armeria.server.ServiceRequestContext;
34 import com.linecorp.armeria.server.SimpleDecoratingHttpService;
35 import com.linecorp.armeria.server.annotation.Decorator;
36 import com.linecorp.armeria.server.annotation.DecoratorFactoryFunction;
37 import com.linecorp.centraldogma.server.internal.admin.auth.AuthUtil;
38 import com.linecorp.centraldogma.server.internal.api.HttpApiUtil;
39 import com.linecorp.centraldogma.server.metadata.MetadataService;
40 import com.linecorp.centraldogma.server.metadata.MetadataServiceInjector;
41 import com.linecorp.centraldogma.server.metadata.Permission;
42 import com.linecorp.centraldogma.server.metadata.User;
43 import com.linecorp.centraldogma.server.storage.project.Project;
44
45
46
47
48 public final class RequiresPermissionDecorator extends SimpleDecoratingHttpService {
49
50 private final Permission requiredPermission;
51
52 RequiresPermissionDecorator(HttpService delegate, Permission requiredPermission) {
53 super(delegate);
54 this.requiredPermission = requireNonNull(requiredPermission, "requiredPermission");
55 }
56
57 @Override
58 public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception {
59 final MetadataService mds = MetadataServiceInjector.getMetadataService(ctx);
60 final User user = AuthUtil.currentUser(ctx);
61
62 final String projectName = ctx.pathParam("projectName");
63 checkArgument(!isNullOrEmpty(projectName), "no project name is specified");
64 final String repoName = ctx.pathParam("repoName");
65 checkArgument(!isNullOrEmpty(repoName), "no repository name is specified");
66
67 if (Project.REPO_DOGMA.equals(repoName)) {
68 if (!user.isAdmin()) {
69 return throwForbiddenResponse(ctx, projectName, repoName, "administrator");
70 }
71 return unwrap().serve(ctx, req);
72 }
73 return serveUserRepo(ctx, req, mds, user, projectName, repoName);
74 }
75
76 private static HttpResponse throwForbiddenResponse(ServiceRequestContext ctx, String projectName,
77 String repoName, String adminOrOwner) {
78 return HttpApiUtil.throwResponse(ctx, HttpStatus.FORBIDDEN,
79 "Repository '%s/%s' can be accessed only by an %s.",
80 projectName, repoName, adminOrOwner);
81 }
82
83 private HttpResponse serveUserRepo(ServiceRequestContext ctx, HttpRequest req,
84 MetadataService mds, User user,
85 String projectName, String repoName) throws Exception {
86 final CompletionStage<Collection<Permission>> f;
87 try {
88 f = mds.findPermissions(projectName, repoName, user);
89 } catch (Throwable cause) {
90 return handleException(ctx, cause);
91 }
92
93 return HttpResponse.of(f.handle((permission, cause) -> {
94 if (cause != null) {
95 return handleException(ctx, cause);
96 }
97 if (!permission.contains(requiredPermission)) {
98 return HttpApiUtil.throwResponse(
99 ctx, HttpStatus.FORBIDDEN,
100 "You must have %s permission for repository '%s/%s'.",
101 requiredPermission, projectName, repoName);
102 }
103 try {
104 return unwrap().serve(ctx, req);
105 } catch (Exception e) {
106 return Exceptions.throwUnsafely(e);
107 }
108 }));
109 }
110
111
112
113
114
115 public static final class RequiresReadPermissionDecoratorFactory
116 implements DecoratorFactoryFunction<RequiresReadPermission> {
117 @Override
118 public Function<? super HttpService, ? extends HttpService>
119 newDecorator(RequiresReadPermission parameter) {
120 return delegate -> new RequiresPermissionDecorator(delegate, Permission.READ);
121 }
122 }
123
124
125
126
127
128 public static final class RequiresWritePermissionDecoratorFactory
129 implements DecoratorFactoryFunction<RequiresWritePermission> {
130 @Override
131 public Function<? super HttpService, ? extends HttpService>
132 newDecorator(RequiresWritePermission parameter) {
133 return delegate -> new RequiresPermissionDecorator(delegate, Permission.WRITE);
134 }
135 }
136 }