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