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.google.common.collect.ImmutableList.toImmutableList;
22 import static java.util.Objects.requireNonNull;
23
24 import java.util.Set;
25 import java.util.function.Function;
26
27 import com.google.common.collect.ImmutableSet;
28
29 import com.linecorp.armeria.common.HttpRequest;
30 import com.linecorp.armeria.common.HttpResponse;
31 import com.linecorp.armeria.common.HttpStatus;
32 import com.linecorp.armeria.common.util.Exceptions;
33 import com.linecorp.armeria.server.HttpService;
34 import com.linecorp.armeria.server.ServiceRequestContext;
35 import com.linecorp.armeria.server.SimpleDecoratingHttpService;
36 import com.linecorp.armeria.server.annotation.DecoratorFactoryFunction;
37 import com.linecorp.centraldogma.common.ProjectNotFoundException;
38 import com.linecorp.centraldogma.common.RepositoryNotFoundException;
39 import com.linecorp.centraldogma.server.internal.admin.auth.AuthUtil;
40 import com.linecorp.centraldogma.server.internal.api.HttpApiUtil;
41 import com.linecorp.centraldogma.server.metadata.MetadataService;
42 import com.linecorp.centraldogma.server.metadata.MetadataServiceInjector;
43 import com.linecorp.centraldogma.server.metadata.ProjectRole;
44 import com.linecorp.centraldogma.server.metadata.User;
45
46
47
48
49 public final class RequiresRoleDecorator extends SimpleDecoratingHttpService {
50
51 private final Set<ProjectRole> accessibleRoles;
52 private final String roleNames;
53
54 RequiresRoleDecorator(HttpService delegate, Set<ProjectRole> accessibleRoles) {
55 super(delegate);
56 this.accessibleRoles = ImmutableSet.copyOf(requireNonNull(accessibleRoles, "accessibleRoles"));
57 roleNames = String.join(",",
58 accessibleRoles.stream().map(ProjectRole::name).collect(toImmutableList()));
59 }
60
61 @Override
62 public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception {
63 final MetadataService mds = MetadataServiceInjector.getMetadataService(ctx);
64 final User user = AuthUtil.currentUser(ctx);
65
66 final String projectName = ctx.pathParam("projectName");
67 checkArgument(!isNullOrEmpty(projectName), "no project name is specified");
68
69 try {
70 return HttpResponse.from(mds.findRole(projectName, user).handle((role, cause) -> {
71 if (cause != null) {
72 return handleException(ctx, cause);
73 }
74 if (!user.isAdmin() && !accessibleRoles.contains(role)) {
75 return HttpApiUtil.throwResponse(
76 ctx, HttpStatus.FORBIDDEN,
77 "You must have one of the following roles to access the project '%s': %s",
78 projectName, roleNames);
79 }
80 try {
81 return unwrap().serve(ctx, req);
82 } catch (Exception e) {
83 return Exceptions.throwUnsafely(e);
84 }
85 }));
86 } catch (Throwable cause) {
87 return handleException(ctx, cause);
88 }
89 }
90
91 static HttpResponse handleException(ServiceRequestContext ctx, Throwable cause) {
92 cause = Exceptions.peel(cause);
93 if (cause instanceof RepositoryNotFoundException ||
94 cause instanceof ProjectNotFoundException) {
95 return HttpApiUtil.newResponse(ctx, HttpStatus.NOT_FOUND, cause);
96 } else {
97 return Exceptions.throwUnsafely(cause);
98 }
99 }
100
101 public static final class RequiresRoleDecoratorFactory
102 implements DecoratorFactoryFunction<RequiresRole> {
103
104 @Override
105 public Function<? super HttpService, ? extends HttpService> newDecorator(RequiresRole parameter) {
106 return delegate -> new RequiresRoleDecorator(delegate, ImmutableSet.copyOf(parameter.roles()));
107 }
108 }
109 }