1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.linecorp.centraldogma.server.auth.shiro;
18
19 import static java.util.Objects.requireNonNull;
20
21 import java.util.concurrent.CompletableFuture;
22 import java.util.function.Function;
23
24 import org.apache.shiro.mgt.SecurityManager;
25 import org.apache.shiro.session.Session;
26 import org.apache.shiro.session.mgt.DefaultSessionKey;
27 import org.apache.shiro.subject.Subject;
28 import org.apache.shiro.util.ThreadContext;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 import com.linecorp.armeria.common.HttpRequest;
33 import com.linecorp.armeria.common.HttpResponse;
34 import com.linecorp.armeria.common.HttpStatus;
35 import com.linecorp.armeria.common.RequestHeaders;
36 import com.linecorp.armeria.server.AbstractHttpService;
37 import com.linecorp.armeria.server.ServiceRequestContext;
38 import com.linecorp.armeria.server.auth.AuthTokenExtractors;
39 import com.linecorp.centraldogma.server.internal.api.HttpApiUtil;
40
41
42
43
44 final class LogoutService extends AbstractHttpService {
45
46 private static final Logger logger = LoggerFactory.getLogger(LogoutService.class);
47
48 private final SecurityManager securityManager;
49 private final Function<String, CompletableFuture<Void>> logoutSessionPropagator;
50
51 LogoutService(SecurityManager securityManager,
52 Function<String, CompletableFuture<Void>> logoutSessionPropagator) {
53 this.securityManager = requireNonNull(securityManager, "securityManager");
54 this.logoutSessionPropagator = requireNonNull(logoutSessionPropagator, "logoutSessionPropagator");
55 }
56
57 @Override
58 protected HttpResponse doPost(ServiceRequestContext ctx, HttpRequest req) throws Exception {
59 return HttpResponse.from(
60 req.aggregate().thenApply(msg -> AuthTokenExtractors.oAuth2().apply(
61 RequestHeaders.of(msg.headers())))
62 .thenApplyAsync(token -> {
63 if (token == null) {
64 return HttpResponse.of(HttpStatus.OK);
65 }
66
67 final String sessionId = token.accessToken();
68
69
70 ThreadContext.bind(securityManager);
71 try {
72 final Session session = securityManager.getSession(new DefaultSessionKey(sessionId));
73 if (session != null) {
74 final Subject currentUser = new Subject.Builder(securityManager)
75 .sessionCreationEnabled(false)
76 .sessionId(sessionId)
77 .buildSubject();
78
79
80 final String username = (String) currentUser.getPrincipal();
81 currentUser.logout();
82 }
83 } catch (Throwable t) {
84 logger.warn("{} Failed to log out: {}", ctx, sessionId, t);
85 } finally {
86 ThreadContext.unbindSecurityManager();
87 }
88
89
90
91
92 return HttpResponse.from(
93 logoutSessionPropagator.apply(sessionId).handle((unused, cause) -> {
94 if (cause != null) {
95 return HttpResponse.of(HttpApiUtil.newResponse(
96 ctx, HttpStatus.INTERNAL_SERVER_ERROR, cause));
97 } else {
98 return HttpResponse.of(HttpStatus.OK);
99 }
100 }));
101 }, ctx.blockingTaskExecutor()));
102 }
103 }