From a961fe020d69544c9e75465662f6f1a31bf393f0 Mon Sep 17 00:00:00 2001
From: Andre Sterba <andre.sterba@stud.h-da.de>
Date: Mon, 2 May 2022 12:53:56 +0000
Subject: [PATCH] Ensure default role and user will be created at controller
 startup

See merge request danet/gosdn!299
---
 controller/controller.go | 116 +++++++++++++++++++++++++++++++++------
 go.mod                   |   2 +
 go.sum                   |   2 +
 3 files changed, 104 insertions(+), 16 deletions(-)

diff --git a/controller/controller.go b/controller/controller.go
index 570c7439a..93cd300d0 100644
--- a/controller/controller.go
+++ b/controller/controller.go
@@ -2,6 +2,7 @@ package controller
 
 import (
 	"context"
+	"fmt"
 	"net"
 	"net/http"
 	"os"
@@ -11,6 +12,7 @@ import (
 	"time"
 
 	"github.com/google/uuid"
+	"github.com/sethvargo/go-password/password"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 	"google.golang.org/grpc"
@@ -39,13 +41,13 @@ var coreOnce sync.Once
 
 // Core is the representation of the controller's core
 type Core struct {
-	pndc       networkdomain.PndStore
-	userc      rbac.UserService
-	rolec      rbac.RoleService
-	httpServer *http.Server
-	grpcServer *grpc.Server
-	nbi        *nbi.NorthboundInterface
-	stopChan   chan os.Signal
+	pndStore    networkdomain.PndStore
+	userService rbac.UserService
+	roleService rbac.RoleService
+	httpServer  *http.Server
+	grpcServer  *grpc.Server
+	nbi         *nbi.NorthboundInterface
+	stopChan    chan os.Signal
 
 	csbiClient cpb.CsbiServiceClient
 }
@@ -60,10 +62,10 @@ func initialize() error {
 	}
 
 	c = &Core{
-		pndc:     nucleus.NewPndStore(),
-		userc:    rbacImpl.NewUserService(rbacImpl.NewUserStore()),
-		rolec:    rbacImpl.NewRoleService(rbacImpl.NewRoleStore()),
-		stopChan: make(chan os.Signal, 1),
+		pndStore:    nucleus.NewPndStore(),
+		userService: rbacImpl.NewUserService(rbacImpl.NewUserStore()),
+		roleService: rbacImpl.NewRoleService(rbacImpl.NewRoleStore()),
+		stopChan:    make(chan os.Signal, 1),
 	}
 
 	// Setting up signal capturing
@@ -87,6 +89,16 @@ func initialize() error {
 		return err
 	}
 
+	err = ensureDefaultRoleExists()
+	if err != nil {
+		return err
+	}
+
+	err = ensureDefaultUserExists()
+	if err != nil {
+		return err
+	}
+
 	return nil
 }
 
@@ -101,7 +113,7 @@ func startGrpc() error {
 	jwtManager := rbacImpl.NewJWTManager("", (10000 * time.Hour)) //TODO(faseid): add real secret and proper duration data here!
 	setupGRPCServerWithCorrectSecurityLevel(jwtManager)
 
-	c.nbi = nbi.NewNBI(c.pndc, c.userc, c.rolec)
+	c.nbi = nbi.NewNBI(c.pndStore, c.userService, c.roleService)
 	c.nbi.Auth = nbi.NewAuthServer(jwtManager)
 
 	pb.RegisterCoreServiceServer(c.grpcServer, c.nbi.Core)
@@ -136,7 +148,7 @@ func createSouthboundInterfaces() (southbound.SouthboundInterface, error) {
 
 // createPrincipalNetworkDomain initializes the controller with an initial PND
 func createPrincipalNetworkDomain() error {
-	basePnd, err := c.pndc.Get(store.Query{ID: config.BasePndUUID})
+	basePnd, err := c.pndStore.Get(store.Query{ID: config.BasePndUUID})
 	if err != nil {
 		log.Info(err)
 	}
@@ -152,7 +164,7 @@ func createPrincipalNetworkDomain() error {
 		if err != nil {
 			return err
 		}
-		err = c.pndc.Add(pnd)
+		err = c.pndStore.Add(pnd)
 		if err != nil {
 			return err
 		}
@@ -162,6 +174,78 @@ func createPrincipalNetworkDomain() error {
 	return nil
 }
 
+func ensureDefaultRoleExists() error {
+	defaultAdminRoleName := "admin"
+	adminRole, err := c.roleService.Get(store.Query{ID: uuid.Nil, Name: defaultAdminRoleName})
+	if err != nil {
+		log.Info(err)
+	}
+
+	if adminRole == nil {
+		err := c.roleService.Add(rbacImpl.NewRole(uuid.New(), defaultAdminRoleName, "admin role", []string{
+			"/gosdn.core.CoreService/GetPnd",
+			"/gosdn.core.CoreService/GetPndList",
+			"/gosdn.core.CoreService/CreatePndList",
+			"/gosdn.core.CoreService/DeletePnd",
+			"/gosdn.rbac.AuthService/CreateUsers",
+			"/gosdn.rbac.AuthService/GetUser",
+			"/gosdn.rbac.AuthService/GetUsers",
+			"/gosdn.rbac.AuthService/UpdateUsers",
+			"/gosdn.rbac.AuthService/DeleteUsers",
+			"/gosdn.rbac.AuthService/CreateRoles",
+			"/gosdn.rbac.AuthService/GetRole",
+			"/gosdn.rbac.AuthService/GetRoles",
+			"/gosdn.rbac.AuthService/UpdateRoles",
+			"/gosdn.rbac.AuthService/DeletePermissionsForRole",
+			"/gosdn.rbac.AuthService/DeleteRoles",
+			"/gosdn.pnd.PndService/GetOnd",
+			"/gosdn.pnd.PndService/GetOndList",
+			"/gosdn.pnd.PndService/GetSbi",
+			"/gosdn.pnd.PndService/GetSbiList",
+			"/gosdn.pnd.PndService/GetPath",
+			"/gosdn.pnd.PndService/GetChange",
+			"/gosdn.pnd.PndService/GetChangeList",
+			"/gosdn.pnd.PndService/SetOndList",
+			"/gosdn.pnd.PndService/SetChangeList",
+			"/gosdn.pnd.PndService/SetPathList",
+			"/gosdn.pnd.PndService/SetSbiList",
+			"/gosdn.pnd.PndService/DeleteOnd",
+			"/gosdn.southbound.SbiService/GetSchema",
+		}))
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func ensureDefaultUserExists() error {
+	defaultUserName := "admin"
+	adminUser, err := c.userService.Get(store.Query{ID: uuid.Nil, Name: defaultUserName})
+	if err != nil {
+		log.Info(err)
+	}
+
+	if adminUser == nil {
+		// Generate a password that is 16 characters long with 3 digits, 0 symbols,
+		// allowing upper and lower case letters, disallowing repeat characters.
+		generatedPassword, err := password.Generate(16, 3, 0, true, false)
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		fmt.Printf("########\n Generated admin password: %s\n ########\n", generatedPassword)
+
+		err = c.userService.Add(rbacImpl.NewUser(uuid.New(), defaultUserName, map[string]string{config.BasePndUUID.String(): "admin"}, generatedPassword, ""))
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 // Run calls initialize to start the controller
 func Run(ctx context.Context) error {
 	var initError error
@@ -193,10 +277,10 @@ func shutdown() error {
 
 func callback(id uuid.UUID, ch chan device.Details) {
 	if ch != nil {
-		c.pndc.AddPendingChannel(id, ch)
+		c.pndStore.AddPendingChannel(id, ch)
 		log.Infof("pending channel %v added", id)
 	} else {
-		c.pndc.RemovePendingChannel(id)
+		c.pndStore.RemovePendingChannel(id)
 		log.Infof("pending channel %v removed", id)
 	}
 }
diff --git a/go.mod b/go.mod
index f14f10e38..866ab3d64 100644
--- a/go.mod
+++ b/go.mod
@@ -33,6 +33,8 @@ require (
 	google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa
 )
 
+require github.com/sethvargo/go-password v0.2.0 // indirect
+
 require (
 	github.com/Microsoft/go-winio v0.5.1 // indirect
 	github.com/Microsoft/hcsshim v0.9.2 // indirect
diff --git a/go.sum b/go.sum
index f59c16085..2c50c5676 100644
--- a/go.sum
+++ b/go.sum
@@ -979,6 +979,8 @@ github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
 github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
+github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI=
+github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
 github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
-- 
GitLab