Skip to content

Commit

Permalink
feat: add renku search to gateway (#698)
Browse files Browse the repository at this point in the history
  • Loading branch information
Panaetius committed Mar 7, 2024
1 parent cb23949 commit dddd5ce
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 5 deletions.
2 changes: 2 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
web,
notebook_auth,
keycloak_gitlab_auth,
search_auth,
)
from .auth.oauth_redis import OAuthRedis
from .auth.utils import decode_keycloak_jwt
Expand Down Expand Up @@ -135,6 +136,7 @@ def auth():
"gitlab": gitlab_auth.GitlabUserToken,
"renku": renku_auth.RenkuCoreAuthHeaders,
"notebook": notebook_auth.NotebookAuthHeaders,
"search": search_auth.SearchHeaders,
"cli-gitlab": cli_auth.RenkuCLIGitlabAuthHeaders,
"keycloak_gitlab": keycloak_gitlab_auth.KeycloakGitlabAuthHeaders,
}
Expand Down
42 changes: 42 additions & 0 deletions app/auth/search_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
#
# Copyright 2018 - Swiss Data Science Center (SDSC)
# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
# Eidgenössische Technische Hochschule Zürich (ETHZ).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Add the headers for the Renku searchservice."""
import re

from ..config import KC_SUFFIX
from .utils import get_or_set_keycloak_client, get_redis_key_from_token


class SearchHeaders:
def process(self, request, headers):
m = re.search(
r"bearer (?P<token>.+)", headers.get("Authorization", ""), re.IGNORECASE
)
if m:
access_token = m.group("token")

keycloak_redis_key = get_redis_key_from_token(
access_token, key_suffix=KC_SUFFIX
)
keycloak_oidc_client = get_or_set_keycloak_client(keycloak_redis_key)

headers["Renku-Auth-Id-Token"] = keycloak_oidc_client.token["id_token"]
else:
headers["Renku-Auth-Anon-Id"] = request.cookies.get("anon-id", "")

return headers
3 changes: 2 additions & 1 deletion cmd/revproxy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type renkuServicesConfig struct {
CoreServicePaths []string `mapstructure:"renku_services_core_service_paths"`
Auth *url.URL `mapstructure:"renku_services_auth"`
DataService *url.URL `mapstructure:"renku_services_data_service"`
Search *url.URL `mapstructure:"renku_services_search"`
Keycloak *url.URL `mapstructure:"renku_services_keycloak"`
}

Expand Down Expand Up @@ -109,7 +110,7 @@ func getConfig() revProxyConfig {
viper.DecodeHook(
mapstructure.ComposeDecodeHookFunc(
parseStringAsURL(),
mapstructure.StringToSliceHookFunc(","),
mapstructure.StringToSliceHookFunc(","),
),
),
)
Expand Down
3 changes: 3 additions & 0 deletions cmd/revproxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ func setupServer(ctx context.Context, config revProxyConfig) *echo.Echo {
keycloakProxy := proxyFromURL(config.RenkuServices.Keycloak)
keycloakProxyHost := setHost(config.RenkuServices.Keycloak.Host)
dataServiceProxy := proxyFromURL(config.RenkuServices.DataService)
searchProxy := proxyFromURL(config.RenkuServices.Search)
logger := middleware.Logger()

// Initialize common authentication middleware
notebooksAuth := authenticate(AddQueryParams(config.RenkuServices.Auth, map[string]string{"auth": "notebook"}), "Renku-Auth-Access-Token", "Renku-Auth-Id-Token", "Renku-Auth-Git-Credentials", "Renku-Auth-Anon-Id", "Renku-Auth-Refresh-Token")
searchAuth := authenticate(AddQueryParams(config.RenkuServices.Auth, map[string]string{"auth": "search"}), "Renku-Auth-Id-Token", "Renku-Auth-Anon-Id")
dataAuth := authenticate(AddQueryParams(config.RenkuServices.Auth, map[string]string{"auth": "keycloak_gitlab"}), "Authorization", "Gitlab-Access-Token")
renkuAuth := authenticate(AddQueryParams(config.RenkuServices.Auth, map[string]string{"auth": "renku"}), "Authorization", "Renku-user-id", "Renku-user-fullname", "Renku-user-email")
gitlabAuth := authenticate(AddQueryParams(config.RenkuServices.Auth, map[string]string{"auth": "gitlab"}), "Authorization")
Expand Down Expand Up @@ -70,6 +72,7 @@ func setupServer(ctx context.Context, config revProxyConfig) *echo.Echo {
e.Group("/api/datasets", logger, noCookies, regexRewrite("^/api(.*)", "/knowledge-graph$1"), kgProxy)
e.Group("/api/kg", logger, gitlabAuth, noCookies, regexRewrite("^/api/kg(.*)", "/knowledge-graph$1"), kgProxy)
e.Group("/api/data", logger, dataAuth, noCookies, dataServiceProxy)
e.Group("/api/search", logger, searchAuth, noCookies, stripPrefix("/api"), searchProxy)
// /api/kc is used only by the ui and no one else, will be removed when the gateway is in charge of user sessions
e.Group("/api/kc", logger, stripPrefix("/api/kc"), keycloakProxyHost, keycloakProxy)

Expand Down
22 changes: 18 additions & 4 deletions cmd/revproxy/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ func setupTestRevproxy(ctx context.Context, upstreamServerURL *url.URL, upstream
KG: upstreamServerURL,
Webhook: upstreamServerURL,
Auth: authURL,
DataService: upstreamServerURL,
DataService: upstreamServerURL,
Search: upstreamServerURL,
Keycloak: upstreamServerURL,
},
Debug: true,
Expand Down Expand Up @@ -189,7 +190,7 @@ func ParametrizedRouteTest(scenario TestCase) func(*testing.T) {
if scenario.Expected.Path != "" && len(reqs) > 0 {
assert.Equal(t, scenario.Expected.Path, reqs[len(reqs)-1].URL.EscapedPath())
}
if len(scenario.QueryParams) > 0 && len(reqs) > 0 {
if len(scenario.QueryParams) > 0 && len(reqs) > 0 {
assert.Equal(t, reqURLQuery.Encode(), reqs[len(reqs)-1].URL.RawQuery)
}
}
Expand Down Expand Up @@ -218,6 +219,19 @@ func TestInternalSvcRoutes(t *testing.T) {
Path: "/api/notebooks",
Expected: TestResults{Path: "/notebooks", VisitedServerIDs: []string{"auth", "upstream"}},
},
{
Path: "/api/search/test/rejectedAuth",
Non200AuthResponseStatusCode: 401,
Expected: TestResults{VisitedServerIDs: []string{"auth"}, Non200ResponseStatusCode: 401},
},
{
Path: "/api/search/test/acceptedAuth",
Expected: TestResults{Path: "/search/test/acceptedAuth", VisitedServerIDs: []string{"auth", "upstream"}},
},
{
Path: "/api/search",
Expected: TestResults{Path: "/search", VisitedServerIDs: []string{"auth", "upstream"}},
},
{
Path: "/api/projects/123456/graph/status/something/else",
Expected: TestResults{Path: "/projects/123456/events/status/something/else", VisitedServerIDs: []string{"auth", "upstream"}},
Expand Down Expand Up @@ -419,8 +433,8 @@ func TestInternalSvcRoutes(t *testing.T) {
Expected: TestResults{Path: "/projects/123456/webhooks", VisitedServerIDs: []string{"auth", "upstream"}},
},
{
Path: "/api/kc/auth/realms/Renku/protocol/openid-connect/userinfo",
Expected: TestResults{Path: "/auth/realms/Renku/protocol/openid-connect/userinfo", VisitedServerIDs: []string{"upstream"}},
Path: "/api/kc/auth/realms/Renku/protocol/openid-connect/userinfo",
Expected: TestResults{Path: "/auth/realms/Renku/protocol/openid-connect/userinfo", VisitedServerIDs: []string{"upstream"}},
},
}
for _, testCase := range testCases {
Expand Down

0 comments on commit dddd5ce

Please sign in to comment.