diff --git a/src/PanoptesFrontend/Pages/Register.razor b/src/PanoptesFrontend/Pages/Register.razor
index f548edb..9d07928 100644
--- a/src/PanoptesFrontend/Pages/Register.razor
+++ b/src/PanoptesFrontend/Pages/Register.razor
@@ -1,6 +1,7 @@
@page "/account/register"
@using PanoptesFrontend.Data.Account;
@using PanoptesFrontend.Services;
+@using System.Text.Json;
@inject IAccountService AccountService
@@ -33,6 +34,8 @@
private async void OnValidSubmit()
{
Console.WriteLine("Method called");
+ var user = JsonSerializer.Serialize(model);
+ Console.WriteLine(user);
await AccountService.Register(model);
}
}
\ No newline at end of file
diff --git a/src/PanoptesFrontend/PanoptesFrontend.csproj b/src/PanoptesFrontend/PanoptesFrontend.csproj
index 8993fbf..839378d 100644
--- a/src/PanoptesFrontend/PanoptesFrontend.csproj
+++ b/src/PanoptesFrontend/PanoptesFrontend.csproj
@@ -8,10 +8,12 @@
+
+
diff --git a/src/PanoptesFrontend/Program.cs b/src/PanoptesFrontend/Program.cs
index 8349c9f..66258be 100644
--- a/src/PanoptesFrontend/Program.cs
+++ b/src/PanoptesFrontend/Program.cs
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using PanoptesFrontend.Services;
+using Blazored.LocalStorage;
var builder = WebApplication.CreateBuilder(args);
@@ -8,8 +9,9 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddHttpClient();
-builder.Services.AddSingleton();
-builder.Services.AddSingleton();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+builder.Services.AddBlazoredLocalStorage();
var app = builder.Build();
diff --git a/src/PanoptesFrontend/Services/AccountService.cs b/src/PanoptesFrontend/Services/AccountService.cs
index a50067e..21a3915 100644
--- a/src/PanoptesFrontend/Services/AccountService.cs
+++ b/src/PanoptesFrontend/Services/AccountService.cs
@@ -1,8 +1,7 @@
using PanoptesFrontend.Data.Account;
using PanoptesFrontend.Data;
-using Microsoft.AspNetCore.Components;
-using System.Collections.Generic;
-using System.Threading.Tasks;
+using Blazored.LocalStorage;
+using System.Text.Json;
namespace PanoptesFrontend.Services;
@@ -17,24 +16,30 @@ public interface IAccountService
public class AccountService : IAccountService
{
private IHttpService httpService;
+ private ILocalStorageService localStorage;
- public AccountService(IHttpService httpService) {
+ public AccountService(IHttpService httpService, ILocalStorageService localStorage) {
this.httpService = httpService;
+ this.localStorage = localStorage;
}
public async Task Register(AddUser model){
- // endpoint doesnt exist yet
- await httpService.PostAsync("http://localhost:8080/user/register", model);
+ await httpService.PostAsync("http://localhost:10000/user/register", model);
}
public async Task Login(LoginUser model){
- // endpoint doesnt exist yet
- await httpService.PostAsync("http://localhost:8080/user/authenticate", model);
+ var response = await httpService.PostAsync("http://localhost:10000/user/login", model);
+
+ var jsonDoc = JsonDocument.Parse(response);
+ var token = jsonDoc.RootElement.GetProperty("token").GetString();
+
+ // Save the JWT token to local storage
+ await localStorage.SetItemAsync("authToken", token);
}
public async Task Logout(User model){
- // endpoint doesnt exist yet
- await httpService.PostAsync("http://localhost:8080/user/logout", model);
+ await httpService.PostAsync("http://localhost:10000/user/logout", model);
+ await localStorage.RemoveItemAsync("authToken");
}
}
\ No newline at end of file
diff --git a/src/PanoptesFrontend/Services/HttpService.cs b/src/PanoptesFrontend/Services/HttpService.cs
index 1b60c6b..30171a8 100644
--- a/src/PanoptesFrontend/Services/HttpService.cs
+++ b/src/PanoptesFrontend/Services/HttpService.cs
@@ -8,7 +8,7 @@ using System.Text.Json;
public interface IHttpService
{
Task GetAsync(string endpoint);
- Task PostAsync(string endpoint, object value);
+ Task PostAsync(string endpoint, object value);
}
public class HttpService : IHttpService {
@@ -32,11 +32,9 @@ public class HttpService : IHttpService {
}
}
- public async Task PostAsync(string endpoint, object value){
- var request = await httpClient.PostAsJsonAsync(endpoint, value);
-
- request.EnsureSuccessStatusCode();
-
- return request.StatusCode;
+ public async Task PostAsync(string endpoint, object value){
+ var response = await httpClient.PostAsJsonAsync(endpoint, value);
+ response.EnsureSuccessStatusCode();
+ return await response.Content.ReadAsStringAsync();
}
}
\ No newline at end of file
diff --git a/src/backend/cmd/main.go b/src/backend/cmd/main.go
index ea487a1..66c5cd3 100644
--- a/src/backend/cmd/main.go
+++ b/src/backend/cmd/main.go
@@ -3,11 +3,13 @@ package main
import (
"database/sql"
- "encoding/json"
+ _"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
+ "github.com/dgrijalva/jwt-go"
+ "golang.org/x/crypto/bcrypt"
"github.com/labstack/echo/v4"
"gitlab.computing.dcu.ie/murphg62/2023-ca400-murphg62-byrnm257/src/backend/pkg/database"
@@ -23,6 +25,7 @@ const (
)
var db, err = database.ConnectDB(host, port, user, password, dbname)
+var jwtSecret = []byte("fAHr+Hlho9qhCePEuMxLVG2i/1tiEqtocAWkcYRJx0s=")
type Schema struct {
Name string `json:"Name"`
@@ -35,6 +38,11 @@ type Module struct {
Container string `json:"Container"`
}
+type User struct {
+ Username string `json:"Username"`
+ Password string `json:"Password"`
+}
+
func homePage(c echo.Context) error {
return c.String(http.StatusOK, "Welcome to the home page!")
@@ -80,44 +88,131 @@ func getStats(c echo.Context) error {
}
-func GetModules(db *sql.DB) http.HandlerFunc{
- return func(w http.ResponseWriter, r *http.Request) {
- if r.Method == "GET" {
- var Modules []Module
- rows, err := db.Query("Select name, container_name FROM Module")
- if err != nil {
- panic(err)
- }
- defer rows.Close()
-
- for rows.Next() {
- var module Module
- if err := rows.Scan(&module.Name, &module.Container); err != nil {
- panic(err)
- }
- Modules = append(Modules, module)
- }
+func GetModules(db *sql.DB) echo.HandlerFunc {
+ return func(c echo.Context) error {
+ modules := make([]Module, 0)
+ rows, err := db.Query("SELECT name, container_name FROM Module")
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
- w.Header().Set("Content-Type", "application/json")
- if err := json.NewEncoder(w).Encode(Modules); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- }
- }
- }
+ for rows.Next() {
+ var module Module
+ if err := rows.Scan(&module.Name, &module.Container); err != nil {
+ return err
+ }
+ modules = append(modules, module)
+ }
+
+ return c.JSON(http.StatusOK, modules)
+ }
}
+func RegisterUser(db *sql.DB) echo.HandlerFunc {
+ return func(c echo.Context) error {
+ user := new(User)
+ if err := c.Bind(user); err != nil {
+ return err
+ }
+
+ // Check if user already exists
+ var count int
+ if err := db.QueryRow("SELECT COUNT(*) FROM users WHERE name = $1", user.Username).Scan(&count); err != nil {
+ return err
+ }
+ if count > 0 {
+ return c.JSON(http.StatusBadRequest, map[string]string{"error": "Username already in use"})
+ }
+
+ // Hash password
+ hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
+ if err != nil {
+ return err
+ }
+
+ // Insert new user into database
+ _, err = db.Exec("INSERT INTO users(name, password) VALUES ($1, $2)", user.Username, string(hashedPassword))
+ if err != nil {
+ return err
+ }
+
+ return c.JSON(http.StatusOK, map[string]string{"message": "User created"})
+ }
+}
+
+
+func LoginUser(db *sql.DB, jwtSecret []byte) echo.HandlerFunc {
+ return func(c echo.Context) error {
+ login := new(User)
+ if err := c.Bind(login); err != nil {
+ return err
+ }
+
+ // Check if user exists
+ var user User
+ if err := db.QueryRow("SELECT name, password FROM users WHERE name = $1", login.Username).Scan(&user.Username, &user.Password); err != nil {
+ if err == sql.ErrNoRows {
+ return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid username or password"})
+ }
+ return err
+ }
+
+ if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(login.Password)); err != nil {
+ return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid username or password"})
+ }
+
+ // Generate JWT token
+ token := jwt.New(jwt.SigningMethodHS256)
+ claims := token.Claims.(jwt.MapClaims)
+ claims["name"] = user.Username
+ tokenString, err := token.SignedString(jwtSecret)
+ if err != nil {
+ return err
+ }
+
+ return c.JSON(http.StatusOK, map[string]string{"token": tokenString})
+ }
+}
+
+func LogoutUser() echo.HandlerFunc {
+ return func(c echo.Context) error {
+ // Clear JWT token cookie
+ cookie := &http.Cookie{
+ Name: "jwt",
+ Value: "",
+ HttpOnly: true,
+ Path: "/",
+ }
+ http.SetCookie(c.Response().Writer, cookie)
+
+ return c.NoContent(http.StatusOK)
+ }
+}
+
+
func main() {
if err != nil {
panic(err)
}
db.Exec(`CREATE TABLE IF NOT EXISTS Module (
name VARCHAR(64),
- container_name VARCHAR(64)
- )`)
+ container_name VARCHAR(64));
+
+ CREATE TABLE IF NOT EXISTS users (
+ id SERIAL PRIMARY KEY,
+ name VARCHAR(64) UNIQUE NOT NULL,
+ password VARCHAR(255) NOT NULL
+ )
+ `)
e := echo.New()
e.GET("/", homePage)
e.GET("/:container/schema", getSchema)
e.GET("/:container/stats", getStats)
+ e.GET("/modules", GetModules(db))
+ e.POST("/user/register", RegisterUser(db))
+ e.POST("/user/login", LoginUser(db, jwtSecret))
+ e.POST("/user/logout", LogoutUser())
e.Logger.Fatal(e.Start(":10000"))
defer db.Close()
}
diff --git a/src/backend/go.mod b/src/backend/go.mod
index f18123d..aacd066 100644
--- a/src/backend/go.mod
+++ b/src/backend/go.mod
@@ -12,15 +12,16 @@ require (
require (
github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
+ github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
- golang.org/x/crypto v0.6.0 // indirect
- golang.org/x/net v0.7.0 // indirect
- golang.org/x/sys v0.5.0 // indirect
- golang.org/x/text v0.7.0 // indirect
+ golang.org/x/crypto v0.8.0 // indirect
+ golang.org/x/net v0.9.0 // indirect
+ golang.org/x/sys v0.7.0 // indirect
+ golang.org/x/text v0.9.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/src/backend/go.sum b/src/backend/go.sum
index 2be4c4a..521cb41 100644
--- a/src/backend/go.sum
+++ b/src/backend/go.sum
@@ -2,8 +2,9 @@ github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20O
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
@@ -12,22 +13,6 @@ github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/lib/pq v1.10.8 h1:3fdt97i/cwSU83+E0hZTC/Xpc9mTZxc6UWSCRcSbxiE=
github.com/lib/pq v1.10.8/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
@@ -35,9 +20,16 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
+github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
@@ -45,18 +37,29 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
+golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
+golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=