Rework SslHandlerFactory
[netconf.git] / apps / callhome-provider / src / main / java / org / opendaylight / netconf / callhome / server / tls / CallHomeTlsSessionContextManager.java
1 /*
2  * Copyright (c) 2023 PANTHEON.tech s.r.o. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.callhome.server.tls;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.util.concurrent.SettableFuture;
13 import io.netty.channel.Channel;
14 import io.netty.handler.ssl.SslHandler;
15 import java.security.PublicKey;
16 import javax.net.ssl.SSLPeerUnverifiedException;
17 import org.opendaylight.netconf.callhome.server.AbstractCallHomeSessionContextManager;
18 import org.opendaylight.netconf.callhome.server.CallHomeStatusRecorder;
19 import org.opendaylight.netconf.client.SimpleNetconfClientSessionListener;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 public class CallHomeTlsSessionContextManager extends AbstractCallHomeSessionContextManager<CallHomeTlsSessionContext> {
24     private static final Logger LOG = LoggerFactory.getLogger(CallHomeTlsSessionContextManager.class);
25
26     private final CallHomeTlsAuthProvider authProvider;
27     private final CallHomeStatusRecorder statusRecorder;
28
29     public CallHomeTlsSessionContextManager(final CallHomeTlsAuthProvider authProvider,
30         final CallHomeStatusRecorder statusRecorder) {
31         super();
32         this.authProvider = requireNonNull(authProvider);
33         this.statusRecorder = requireNonNull(statusRecorder);
34     }
35
36     @Override
37     public CallHomeTlsSessionContext findByChannel(final Channel channel) {
38         requireNonNull(channel);
39         return channel.isOpen() ? createValidContext(channel) : null;
40     }
41
42     private CallHomeTlsSessionContext createValidContext(final Channel channel) {
43         // extract peer public key from SSL session
44         final PublicKey publicKey;
45         try {
46             final var cert = channel.pipeline().get(SslHandler.class).engine().getSession()
47                 .getPeerCertificates()[0];
48             publicKey = cert.getPublicKey();
49         } catch (SSLPeerUnverifiedException e) {
50             LOG.error("Exception retrieving certificate", e);
51             return null;
52         }
53         // identify connection
54         final String id = authProvider.idFor(publicKey);
55         if (id == null) {
56             statusRecorder.reportUnknown(channel.remoteAddress(), publicKey);
57             return null;
58         }
59
60         final var context = createContext(id, channel);
61         if (context == null) {
62             // if there is an issue creating context then the cause expected to be
63             // logged within overridden createContext() method
64             return null;
65         }
66         register(context);
67         // close callback
68         channel.closeFuture().addListener(ignored -> {
69             remove(id);
70             if (context.settableFuture().isDone()) {
71                 // disconnect after netconf session established
72                 statusRecorder.reportDisconnected(id);
73             }
74         });
75
76         LOG.debug("Session context is created for TLS session: {}", context);
77         return context;
78     }
79
80     /**
81      * Builds {@link CallHomeTlsSessionContext} based on {@link Channel} object.
82      *
83      * <p> The method expected to be overridden in order to inject
84      * {@link org.opendaylight.netconf.client.NetconfClientSessionListener NetconfClientSessionListener} and/or
85      * {@link SettableFuture} of a {@link org.opendaylight.netconf.client.NetconfClientSession NetconfClientSession}
86      *  to be established on current connection.
87      *
88      * @param id unique client (device) identifier
89      * @param channel netty channel instance
90      * @return created object or {@code null} if it cannot be created for some reason
91      */
92     public CallHomeTlsSessionContext createContext(final String id, final Channel channel) {
93         return new CallHomeTlsSessionContext(id, channel, new SimpleNetconfClientSessionListener(),
94             SettableFuture.create());
95     }
96 }