Merge "Return errormessages in failing imperative workflows"
[ccsdk/cds.git] / ms / blueprintsprocessor / application / src / main / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / security / BasicAuthServerInterceptor.kt
1 /*
2  * Copyright (C) 2019 Bell Canada.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.onap.ccsdk.cds.blueprintsprocessor.security
17
18 import io.grpc.Metadata
19 import io.grpc.ServerCall
20 import io.grpc.ServerCallHandler
21 import io.grpc.ServerInterceptor
22 import io.grpc.Status
23 import org.onap.ccsdk.cds.controllerblueprints.core.logger
24 import org.springframework.security.authentication.BadCredentialsException
25 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
26 import org.springframework.security.core.AuthenticationException
27 import org.springframework.security.core.context.SecurityContextHolder
28 import org.springframework.stereotype.Component
29 import java.nio.charset.StandardCharsets
30 import java.util.Base64
31
32 @Component
33 class BasicAuthServerInterceptor(private val authenticationManager: AuthenticationManager) :
34     ServerInterceptor {
35
36     private val log = logger(BasicAuthServerInterceptor::class)
37
38     override fun <ReqT, RespT> interceptCall(
39         call: ServerCall<ReqT, RespT>,
40         headers: Metadata,
41         next: ServerCallHandler<ReqT, RespT>
42     ): ServerCall.Listener<ReqT> {
43         val authHeader = headers.get(Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER))
44
45         if (authHeader.isNullOrEmpty()) {
46             throw Status.UNAUTHENTICATED.withDescription("Missing required authentication")
47                 .asRuntimeException()
48         }
49
50         try {
51             val tokens = decodeBasicAuth(authHeader)
52             val username = tokens[0]
53
54             log.info("Basic Authentication Authorization header found for user: {}", username)
55
56             val authRequest = UsernamePasswordAuthenticationToken(username, tokens[1])
57             val authResult = authenticationManager.authenticate(authRequest).block()
58
59             log.info("Authentication success: {}", authResult)
60
61             SecurityContextHolder.getContext().authentication = authResult
62         } catch (e: AuthenticationException) {
63             SecurityContextHolder.clearContext()
64
65             log.info("Authentication request failed: {}", e.message)
66
67             throw Status.UNAUTHENTICATED.withDescription(e.message).withCause(e).asRuntimeException()
68         }
69
70         return next.startCall(call, headers)
71     }
72
73     private fun decodeBasicAuth(authHeader: String): Array<String> {
74         val basicAuth: String
75         try {
76             basicAuth = String(
77                 Base64.getDecoder().decode(authHeader.substring(6).toByteArray(StandardCharsets.UTF_8)),
78                 StandardCharsets.UTF_8
79             )
80         } catch (e: IllegalArgumentException) {
81             throw BadCredentialsException("Failed to decode basic authentication token")
82         } catch (e: IndexOutOfBoundsException) {
83             throw BadCredentialsException("Failed to decode basic authentication token")
84         }
85
86         val delim = basicAuth.indexOf(':')
87         if (delim == -1) {
88             throw BadCredentialsException("Failed to decode basic authentication token")
89         }
90
91         return arrayOf(basicAuth.substring(0, delim), basicAuth.substring(delim + 1))
92     }
93 }