From d690120f52f2d3666c0d03b83209bd25f1cb91d9 Mon Sep 17 00:00:00 2001 From: Sergey Yakubov <sergey.yakubov@desy.de> Date: Wed, 20 Jun 2018 14:31:40 +0200 Subject: [PATCH] add authorizer --- CMakeLists.txt | 2 + CMakeModules/prepare_asapo.cmake | 3 + authorizer/CMakeLists.txt | 38 +++++++++++ .../src/asapo_authorizer/main/authorizer.go | 32 +++++++++ .../src/asapo_authorizer/server/authorize.go | 41 ++++++++++++ .../asapo_authorizer/server/authorize_test.go | 66 +++++++++++++++++++ .../src/asapo_authorizer/server/get_health.go | 11 ++++ .../server/get_health_test.go | 18 +++++ .../src/asapo_authorizer/server/listroutes.go | 21 ++++++ .../src/asapo_authorizer/server/server.go | 10 +++ .../server/server_nottested.go | 31 +++++++++ config/nomad/authorizer.nmd.in | 49 ++++++++++++++ config/nomad/nginx.nmd.in | 2 +- .../settings/authorizer_settings.json.tpl | 6 ++ tests/automatic/settings/nginx.conf.tpl | 6 ++ 15 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 authorizer/CMakeLists.txt create mode 100644 authorizer/src/asapo_authorizer/main/authorizer.go create mode 100644 authorizer/src/asapo_authorizer/server/authorize.go create mode 100644 authorizer/src/asapo_authorizer/server/authorize_test.go create mode 100644 authorizer/src/asapo_authorizer/server/get_health.go create mode 100644 authorizer/src/asapo_authorizer/server/get_health_test.go create mode 100644 authorizer/src/asapo_authorizer/server/listroutes.go create mode 100644 authorizer/src/asapo_authorizer/server/server.go create mode 100644 authorizer/src/asapo_authorizer/server/server_nottested.go create mode 100644 config/nomad/authorizer.nmd.in create mode 100644 tests/automatic/settings/authorizer_settings.json.tpl diff --git a/CMakeLists.txt b/CMakeLists.txt index 2285e204f..cd1e06fd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,8 @@ add_subdirectory(receiver) add_subdirectory(discovery) +add_subdirectory(authorizer) + if(BUILD_INTEGRATION_TESTS) add_subdirectory(tests) diff --git a/CMakeModules/prepare_asapo.cmake b/CMakeModules/prepare_asapo.cmake index 26bedee49..2f14a215a 100644 --- a/CMakeModules/prepare_asapo.cmake +++ b/CMakeModules/prepare_asapo.cmake @@ -2,6 +2,7 @@ function(prepare_asapo) get_target_property(RECEIVER_DIR receiver-bin BINARY_DIR) get_target_property(RECEIVER_NAME receiver-bin OUTPUT_NAME) get_target_property(DISCOVERY_FULLPATH asapo-discovery EXENAME) + get_target_property(AUTHORIZER_FULLPATH asapo-authorizer EXENAME) get_target_property(BROKER_FULLPATH asapo-broker EXENAME) set(WORK_DIR ${CMAKE_CURRENT_BINARY_DIR}) if (WIN32) @@ -11,8 +12,10 @@ function(prepare_asapo) endif() configure_file(${CMAKE_SOURCE_DIR}/config/nomad/receiver.nmd.in receiver.nmd @ONLY) configure_file(${CMAKE_SOURCE_DIR}/config/nomad/discovery.nmd.in discovery.nmd @ONLY) + configure_file(${CMAKE_SOURCE_DIR}/config/nomad/authorizer.nmd.in authorizer.nmd @ONLY) configure_file(${CMAKE_SOURCE_DIR}/config/nomad/broker.nmd.in broker.nmd @ONLY) configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/discovery_settings.json.tpl discovery.json.tpl COPYONLY) + configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/authorizer_settings.json.tpl authorizer.json.tpl COPYONLY) configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/broker_settings.json.tpl broker.json.tpl COPYONLY) configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/nginx.conf.tpl nginx.conf.tpl COPYONLY) configure_file(${CMAKE_SOURCE_DIR}/config/nomad/nginx.nmd.in nginx.nmd @ONLY) diff --git a/authorizer/CMakeLists.txt b/authorizer/CMakeLists.txt new file mode 100644 index 000000000..1b4b8165a --- /dev/null +++ b/authorizer/CMakeLists.txt @@ -0,0 +1,38 @@ +set (TARGET_NAME asapo-authorizer) + +if (NOT "$ENV{GOPATH}" STREQUAL "") + set(GOPATH $ENV{GOPATH}) +endif() + +if (NOT GOPATH) + message (FATAL_ERROR "GOPATH not set") +endif() + +message(STATUS "global gopath ${GOPATH}") + +IF(WIN32) + set (gopath "${GOPATH}\;${CMAKE_CURRENT_SOURCE_DIR}\;${CMAKE_SOURCE_DIR}/common/go") + set (exe_name "${TARGET_NAME}.exe") +ELSE() + set (gopath ${GOPATH}:${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/common/go) + set (exe_name "${TARGET_NAME}") +ENDIF() + +include(testing_go) + +add_custom_target(asapo-authorizer ALL + COMMAND ${CMAKE_COMMAND} -E env GOPATH=${gopath} + go build ${GO_OPTS} -o ${exe_name} asapo_authorizer/main + VERBATIM) +define_property(TARGET PROPERTY EXENAME + BRIEF_DOCS <executable name> + FULL_DOCS <full-doc>) + +set_target_properties(asapo-authorizer PROPERTIES EXENAME ${CMAKE_CURRENT_BINARY_DIR}/${exe_name}) + + +install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${exe_name} DESTINATION bin) + +gotest(${TARGET_NAME} "./...") +#go_integration_test(${TARGET_NAME}-connectdb "./..." "MongoDBConnect") +#go_integration_test(${TARGET_NAME}-nextrecord "./..." "MongoDBNext") diff --git a/authorizer/src/asapo_authorizer/main/authorizer.go b/authorizer/src/asapo_authorizer/main/authorizer.go new file mode 100644 index 000000000..1a0f39f42 --- /dev/null +++ b/authorizer/src/asapo_authorizer/main/authorizer.go @@ -0,0 +1,32 @@ +//+build !test + +package main + +import ( + log "asapo_common/logger" + "asapo_authorizer/server" + "flag" + "os" +) + +func PrintUsage() { + log.Fatal("Usage: " + os.Args[0] + " -config <config file>") +} + +func main() { + var fname = flag.String("config", "", "config file path") + + flag.Parse() + if *fname == "" { + PrintUsage() + } + + logLevel, err := server.ReadConfig(*fname) + if err != nil { + log.Fatal(err.Error()) + } + + log.SetLevel(logLevel) + + server.Start() +} diff --git a/authorizer/src/asapo_authorizer/server/authorize.go b/authorizer/src/asapo_authorizer/server/authorize.go new file mode 100644 index 000000000..ad5cb4326 --- /dev/null +++ b/authorizer/src/asapo_authorizer/server/authorize.go @@ -0,0 +1,41 @@ +package server + +import ( + "net/http" + "encoding/json" +) + +type authorizationRequest struct { + BeamtimeId string + OriginHost string +} + +func extractRequest(r *http.Request)(request authorizationRequest,err error) { + decoder := json.NewDecoder(r.Body) + err = decoder.Decode(&request) + return +} + +func authorize(request authorizationRequest) bool { + // todo: implement real check + if (request.BeamtimeId != "asapo_test") { + return false + } + + return true +} + +func routeAuthorize(w http.ResponseWriter, r *http.Request) { + request,err := extractRequest(r) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + if (!authorize(request)) { + w.WriteHeader(http.StatusUnauthorized) + return + } + + w.WriteHeader(http.StatusOK) + w.Write([]byte(request.BeamtimeId)) +} diff --git a/authorizer/src/asapo_authorizer/server/authorize_test.go b/authorizer/src/asapo_authorizer/server/authorize_test.go new file mode 100644 index 000000000..81e1df983 --- /dev/null +++ b/authorizer/src/asapo_authorizer/server/authorize_test.go @@ -0,0 +1,66 @@ +package server + +import ( + "asapo_common/utils" + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "strings" + "testing" + "io/ioutil" +) + +type request struct { + path string + cmd string + answer int + message string +} + +func containsMatcher(substr string) func(str string) bool { + return func(str string) bool { return strings.Contains(str, substr) } +} + +func makeRequest(request authorizationRequest) string { + buf, _ := utils.MapToJson(request) + return string(buf) +} + +func doAuthorizeRequest(path string,buf string) *httptest.ResponseRecorder { + mux := utils.NewRouter(listRoutes) + req, _ := http.NewRequest("POST", path, strings.NewReader(buf)) + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + return w +} + +func TestAuthorizeOK(t *testing.T) { + request := makeRequest(authorizationRequest{"asapo_test","host"}) + w := doAuthorizeRequest("/authorize",request) + + body, _ := ioutil.ReadAll(w.Body) + + assert.Equal(t, string(body), "asapo_test", "") + assert.Equal(t, http.StatusOK, w.Code, "") +} + +func TestNotAuthorized(t *testing.T) { + request := makeRequest(authorizationRequest{"any_id","host"}) + w := doAuthorizeRequest("/authorize",request) + assert.Equal(t, http.StatusUnauthorized, w.Code, "") +} + + +func TestAuthorizeWrongRequest(t *testing.T) { + w := doAuthorizeRequest("/authorize","babla") + assert.Equal(t, http.StatusBadRequest, w.Code, "") +} + + +func TestAuthorizeWrongPath(t *testing.T) { + w := doAuthorizeRequest("/authorized","") + assert.Equal(t, http.StatusNotFound, w.Code, "") +} + + + diff --git a/authorizer/src/asapo_authorizer/server/get_health.go b/authorizer/src/asapo_authorizer/server/get_health.go new file mode 100644 index 000000000..b7d9f2446 --- /dev/null +++ b/authorizer/src/asapo_authorizer/server/get_health.go @@ -0,0 +1,11 @@ +package server + +import ( + "net/http" +) + + +func routeGetHealth(w http.ResponseWriter, r *http.Request) { + r.Header.Set("Content-type", "application/json") + w.WriteHeader(http.StatusNoContent) +} diff --git a/authorizer/src/asapo_authorizer/server/get_health_test.go b/authorizer/src/asapo_authorizer/server/get_health_test.go new file mode 100644 index 000000000..fc8d6c2fa --- /dev/null +++ b/authorizer/src/asapo_authorizer/server/get_health_test.go @@ -0,0 +1,18 @@ +package server + +import ( + "github.com/stretchr/testify/assert" + "net/http" + "testing" + "net/http/httptest" + "asapo_common/utils" +) + + +func TestGetNext(t *testing.T) { + mux := utils.NewRouter(listRoutes) + req, _ := http.NewRequest("GET", "/health-check", nil) + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + assert.Equal(t, http.StatusNoContent, w.Code) +} diff --git a/authorizer/src/asapo_authorizer/server/listroutes.go b/authorizer/src/asapo_authorizer/server/listroutes.go new file mode 100644 index 000000000..f32c5c830 --- /dev/null +++ b/authorizer/src/asapo_authorizer/server/listroutes.go @@ -0,0 +1,21 @@ +package server + +import ( + "asapo_common/utils" +) + +var listRoutes = utils.Routes{ + utils.Route{ + "Authorize", + "POST", + "/authorize", + routeAuthorize, + }, + utils.Route{ + "HealthCheck", + "Get", + "/health-check", + routeGetHealth, + }, + +} diff --git a/authorizer/src/asapo_authorizer/server/server.go b/authorizer/src/asapo_authorizer/server/server.go new file mode 100644 index 000000000..088339d07 --- /dev/null +++ b/authorizer/src/asapo_authorizer/server/server.go @@ -0,0 +1,10 @@ +package server + + +type serverSettings struct { + Port int + LogLevel string +} + +var settings serverSettings + diff --git a/authorizer/src/asapo_authorizer/server/server_nottested.go b/authorizer/src/asapo_authorizer/server/server_nottested.go new file mode 100644 index 000000000..6ad0c98f3 --- /dev/null +++ b/authorizer/src/asapo_authorizer/server/server_nottested.go @@ -0,0 +1,31 @@ +//+build !test + +package server + +import ( + log "asapo_common/logger" + "asapo_common/utils" + "errors" + "net/http" + "strconv" +) + +func Start() { + mux := utils.NewRouter(listRoutes) + log.Info("Listening on port: " + strconv.Itoa(settings.Port)) + log.Fatal(http.ListenAndServe(":"+strconv.Itoa(settings.Port), http.HandlerFunc(mux.ServeHTTP))) +} + +func ReadConfig(fname string) (log.Level, error) { + if err := utils.ReadJsonFromFile(fname, &settings); err != nil { + return log.FatalLevel, err + } + + if settings.Port == 0 { + return log.FatalLevel, errors.New("Server port not set") + } + + level, err := log.LevelFromString(settings.LogLevel) + + return level, err +} diff --git a/config/nomad/authorizer.nmd.in b/config/nomad/authorizer.nmd.in new file mode 100644 index 000000000..e75992113 --- /dev/null +++ b/config/nomad/authorizer.nmd.in @@ -0,0 +1,49 @@ +job "authorizer" { + datacenters = ["dc1"] + + type = "service" + + group "group" { + count = 1 + + task "service" { + driver = "raw_exec" + + config { + command = "@AUTHORIZER_FULLPATH@", + args = ["-config","${NOMAD_TASK_DIR}/authorizer.json"] + } + + resources { + cpu = 500 # 500 MHz + memory = 256 # 256MB + network { + port "authorizer" { + static = "5007" + } + } + } + + service { + name = "authorizer" + port = "authorizer" + check { + name = "alive" + type = "http" + path = "/health-check" + interval = "10s" + timeout = "2s" + initial_status = "passing" + } + } + + template { + source = "@WORK_DIR@/authorizer.json.tpl" + destination = "local/authorizer.json" + change_mode = "signal" + change_signal = "SIGHUP" + } + + } + } +} diff --git a/config/nomad/nginx.nmd.in b/config/nomad/nginx.nmd.in index da6826764..76d628eaa 100644 --- a/config/nomad/nginx.nmd.in +++ b/config/nomad/nginx.nmd.in @@ -45,7 +45,7 @@ job "nginx" { check { name = "alive" type = "http" - path = "/nginx_health" + path = "/nginx-health" timeout = "2s" interval = "10s" } diff --git a/tests/automatic/settings/authorizer_settings.json.tpl b/tests/automatic/settings/authorizer_settings.json.tpl new file mode 100644 index 000000000..a1b6b3103 --- /dev/null +++ b/tests/automatic/settings/authorizer_settings.json.tpl @@ -0,0 +1,6 @@ +{ + "Port": {{ env "NOMAD_PORT_authorizer" }}, + "LogLevel":"debug" +} + + diff --git a/tests/automatic/settings/nginx.conf.tpl b/tests/automatic/settings/nginx.conf.tpl index 88b81082f..a545307b3 100644 --- a/tests/automatic/settings/nginx.conf.tpl +++ b/tests/automatic/settings/nginx.conf.tpl @@ -19,6 +19,7 @@ http { server { listen {{ env "NOMAD_PORT_nginx" }}; set $discovery_endpoint discovery.service.asapo; + set $authorizer_endpoint authorizer.service.asapo; # set $fluentd_endpoint localhost; location /discovery/ { rewrite ^/discovery(/.*) $1 break; @@ -28,6 +29,11 @@ http { # rewrite ^/logs(/.*) $1 break; proxy_pass http://localhost:9880/asapo; } + location /authorizer/ { + rewrite ^/authorizer(/.*) $1 break; + proxy_pass http://$authorizer_endpoint:5007$uri; + } + location /nginx-health { return 200 "healthy\n"; -- GitLab