yats.git

commit 5b4f3afb4570814803a1ee5f75eece5b8613add1

Author: Paolo Lulli <paolo@lulli.net>

Add show perms endpoint

 server/.goreleaser.yaml | 3 
 server/config/config.go | 8 -
 server/db/queries.go | 37 ++++++++
 server/go.mod | 2 
 server/go.sum | 3 
 server/main.go | 6 +
 server/model/models.go | 16 +++
 server/rest/rest-permissions.go | 38 ++++++++
 server/service-rest.go | 2 
 server/tcp/tcpserver.go | 160 +++++++++++++++++++++++++++++++++++
 server/tlv/tlvpackets.go | 54 +++++++++++


diff --git a/server/.goreleaser.yaml b/server/.goreleaser.yaml
index 260042f009333556eb9ad3e4110e994ac810ede4..d8f19a8e5da34cc78bc2e962f28d53023a4f0843 100644
--- a/server/.goreleaser.yaml
+++ b/server/.goreleaser.yaml
@@ -28,6 +28,7 @@             Yats Server - Yet Another Time Serie
     formats:
       - rpm
       - deb
+      - apk
 
 archives:
   - format: tar.gz
@@ -51,4 +52,4 @@   sort: asc
   filters:
     exclude:
       - "^docs:"
-      - "^test:"
\ No newline at end of file
+      - "^test:"




diff --git a/server/config/config.go b/server/config/config.go
index eb79fec7366c8746a75ae7184a2a46688b960c34..4e63b02f697cb7ec37185035e20483cf2ebb9d8a 100644
--- a/server/config/config.go
+++ b/server/config/config.go
@@ -11,8 +11,6 @@
 package config
 
 import (
-	"fmt"
-
 	"github.com/tkanos/gonfig"
 )
 
@@ -30,11 +28,6 @@ 	"weekly":  Weekly,
 	"monthly": Monthly,
 }
 
-func main() {
-	interval := archFrequency["daily"]
-	fmt.Println(interval) //print the enum value
-}
-
 type Configuration struct {
 	DbUsername string `json:"databaseUsername"`
 	DbPassword string `json:"databasePassword"`
@@ -44,6 +37,7 @@ 	DbName     string `json:"databaseSchema"`
 
 	RestAddress string `json:"restAddress"`
 	GrpcAddress string `json:"grpcAddress"`
+	TcpAddress  string `json:"tcpAddress"`
 
 	ArchiveFrequency string `json:"archiveFrequency"`
 	ArchiveDirectory string `json:"archiveDirectory"`




diff --git a/server/db/queries.go b/server/db/queries.go
index 2932aedc0d779bbbef916a60a2e4a3ddaf21510f..5d3a2c49fc7a327d920230cb05ed1d41862764da 100644
--- a/server/db/queries.go
+++ b/server/db/queries.go
@@ -17,6 +17,43 @@ 	"time"
 	"yats-server/model"
 )
 
+func GetExternalSources(session *gocql.Session, idClient string) []model.ExternalSource {
+	var sources []model.ExternalSource
+	m := map[string]interface{}{}
+
+	q := fmt.Sprintf("SELECT app,type,name from sources where id_client='%s'", idClient)
+	fmt.Println(q)
+	iter := session.Query(q).Iter()
+	for iter.MapScan(m) {
+		sources = append(sources, model.ExternalSource{
+			Name: m["name"].(string),
+			App:  m["app"].(string),
+			Type: m["type"].(string),
+		})
+
+		m = map[string]interface{}{}
+	}
+	return sources
+}
+
+func GetAvailableMetrics(session *gocql.Session, idClient string) []model.AvailableMetric {
+	var metrics []model.AvailableMetric
+	m := map[string]interface{}{}
+
+	q := fmt.Sprintf("SELECT name,description from metric_info where id_client='%s'", idClient)
+	fmt.Println(q)
+	iter := session.Query(q).Iter()
+	for iter.MapScan(m) {
+		metrics = append(metrics, model.AvailableMetric{
+			Name:        m["name"].(string),
+			Description: m["description"].(string),
+		})
+
+		m = map[string]interface{}{}
+	}
+	return metrics
+}
+
 func MetricsFrom(session *gocql.Session, idClient string, metricName string, fromTime string, limit int32) []model.MetricModel {
 	var metrics []model.MetricModel
 	m := map[string]interface{}{}




diff --git a/server/go.mod b/server/go.mod
index 938fec5831fec21f91fd666a4d44bb549aa93cc9..ab487ba5f4d59a678153f08878f14efb36d46aa3 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -5,7 +5,9 @@
 require (
 	github.com/gin-gonic/gin v1.10.0
 	github.com/gocql/gocql v1.6.0
+	github.com/pauloavelar/go-tlv v1.1.0
 	github.com/pd0mz/go-maidenhead v1.0.0
+	github.com/pkg/errors v0.9.1
 	github.com/swaggo/files v1.0.1
 	github.com/swaggo/gin-swagger v1.6.0
 	github.com/swaggo/swag v1.16.3




diff --git a/server/go.sum b/server/go.sum
index c4193f3be44eb3761e2d678253eeda7dba25f51d..b53dfe07271158987c156b87384f949f3b68c5fb 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -167,6 +167,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pauloavelar/go-tlv v1.1.0 h1:xdDbtVt501KYQudQVGdsNlM3vQHLGMRyLXRTw2T03lA=
+github.com/pauloavelar/go-tlv v1.1.0/go.mod h1:iF/18VEBGcZotmu6H6Z5DHIS9iRQYHSUJX3lxl47D3w=
 github.com/pborman/getopt v0.0.0-20180729010549-6fdd0a2c7117/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
 github.com/pd0mz/go-maidenhead v1.0.0 h1:zl2AXA36LnmP5TDEfshM0fWi1mc08fNc6qhj7YD5xjw=
 github.com/pd0mz/go-maidenhead v1.0.0/go.mod h1:4Q+QSDCqWqlabstLGUVm47rAcL06nEEty2d3KzsTNMk=
@@ -174,6 +176,7 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
 github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
 github.com/pierrec/lz4/v4 v4.1.8 h1:ieHkV+i2BRzngO4Wd/3HGowuZStgq6QkPsD1eolNAO4=
 github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=




diff --git a/server/main.go b/server/main.go
index 3cef44af8e1054c066c14da115b488835db8ea8f..8d1b43da918050d0c9c4c63d704ab75e9e189b26 100644
--- a/server/main.go
+++ b/server/main.go
@@ -18,6 +18,7 @@ 	"yats-server/config"
 	"yats-server/db"
 	"yats-server/docs"
 	"yats-server/grpc"
+	"yats-server/tcp"
 )
 
 var configuration config.Configuration
@@ -62,6 +63,11 @@
 	if configuration.GrpcAddress != "" {
 		fmt.Printf("Starting GRPC Server on address: %s\n", configuration.GrpcAddress)
 		go grpc.RunUnsecureYatsGrpcServer(configuration.GrpcAddress)
+	}
+
+	if configuration.TcpAddress != "" {
+		fmt.Printf("Starting TCP Server on address: %s\n", configuration.TcpAddress)
+		go tcp.RunTcpServer(configuration)
 	}
 
 	fmt.Printf("Starting REST Server on address: %s\n", configuration.RestAddress)




diff --git a/server/model/models.go b/server/model/models.go
index 60caf49170bc31892e5319a83351519518a90a4a..6b664dcce129d9008c2093bb0c870dcd4f98e66d 100644
--- a/server/model/models.go
+++ b/server/model/models.go
@@ -79,3 +79,19 @@ 	From              int64  `json:"from" parquet:"name=mtime, type=INT64, convertedtype=TIMESTAMP_MILLIS"`
 	To                int64  `json:"to" parquet:"name=mtime, type=INT64, convertedtype=TIMESTAMP_MILLIS"`
 	SourceApplication string `json:"source" parquet:"name=source_application, type=BYTE_ARRAY, convertedtype=UTF8"`
 }
+
+type AvailableMetric struct {
+	Name        string `json:"name"`
+	Description string `json:"description"`
+}
+
+type ExternalSource struct {
+	Name string `json:"name"`
+	App  string `json:"app"`
+	Type string `json:"type"`
+}
+
+type PermsResponse struct {
+	Metrics []AvailableMetric `json:"metrics"`
+	Sources []ExternalSource  `json:"sources"`
+}




diff --git a/server/rest/rest-permissions.go b/server/rest/rest-permissions.go
new file mode 100644
index 0000000000000000000000000000000000000000..2d4077d49211c998617201992c18bd9348b99d9f
--- /dev/null
+++ b/server/rest/rest-permissions.go
@@ -0,0 +1,38 @@
+/**
+ * Yats - yats
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Paolo Lulli <kevwe.com>
+ * @copyright Paolo Lulli 2024
+ */
+package rest
+
+import (
+	"github.com/gin-gonic/gin"
+	"net/http"
+	"yats-server/config"
+	"yats-server/db"
+)
+
+// ShowPermissions godoc
+// @Param X-SSL-Client-CN header string true "clientCN"
+// @Summary Show available Metrics and Event for current user
+// @Schemes
+// @Description Show available Metrics and Event for current user
+// @Tags Metrics
+// @Produce json
+// @Success 200 {string} []model.PermsResponse
+// @Router /perms [get]
+func ShowPermissions(cfg config.Configuration) gin.HandlerFunc {
+	return func(c *gin.Context) {
+
+		clientCN := GetClientCN(c, cfg)
+
+		availableMetrics := db.GetAvailableMetrics(db.Session, clientCN)
+		externalSources := db.GetExternalSources(db.Session, clientCN)
+
+		c.IndentedJSON(http.StatusAccepted, gin.H{"metrics": availableMetrics, "externalSources": externalSources})
+	}
+}




diff --git a/server/service-rest.go b/server/service-rest.go
index 06a6fc9560a0768f3ea5720ea8934b994b2f4b81..a97ef26319273df2386044b650b393c88f22be35 100644
--- a/server/service-rest.go
+++ b/server/service-rest.go
@@ -39,6 +39,8 @@ 	router.GET("/event/:from", rest.GetEventsFrom(cfg))
 
 	router.POST("/position", rest.WritePosition(cfg))
 
+	router.GET("/perms", rest.ShowPermissions(cfg))
+
 	enableSwaggerEndpoint(router)
 
 	if cfg.GrafanaActive == "true" {




diff --git a/server/tcp/tcpserver.go b/server/tcp/tcpserver.go
new file mode 100644
index 0000000000000000000000000000000000000000..c041a2782a97f85b40512ead4892149624e98ca7
--- /dev/null
+++ b/server/tcp/tcpserver.go
@@ -0,0 +1,160 @@
+/**
+ * Yats - yats
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ *
+ * @author Paolo Lulli <kevwe.com>
+ * @copyright Paolo Lulli 2024
+ */
+
+package tcp
+
+import (
+	"crypto/tls"
+	"fmt"
+	"log"
+	"net"
+	"os"
+	"os/signal"
+	"sync"
+	"syscall"
+	"time"
+	"yats-server/config"
+	"yats-server/tlv"
+)
+
+type tcpserver struct {
+	wg         sync.WaitGroup
+	listener   net.Listener
+	shutdown   chan struct{}
+	connection chan net.Conn
+}
+
+func createServer(cfg config.Configuration) (*tcpserver, error) {
+
+	var listener net.Listener
+	var err error
+
+	if cfg.TlsActive == "true" {
+		cert, err := tls.LoadX509KeyPair(cfg.TlsCertificate, cfg.TlsKeyFile)
+		if err != nil {
+			log.Fatalf("server: loadkeys: %s", err)
+		}
+		tlsconfig := tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: false}
+		//listener, err := tls.Dial("tcp", cfg.TcpAddress, &tlsconfig)
+
+		listener, err = tls.Listen("tcp", cfg.TcpAddress, &tlsconfig)
+	} else {
+		listener, err = net.Listen("tcp", cfg.TcpAddress)
+	}
+	if err != nil {
+		return nil, fmt.Errorf("failed to listen on address %s: %w", cfg.TcpAddress, err)
+	}
+
+	return &tcpserver{
+		listener:   listener,
+		shutdown:   make(chan struct{}),
+		connection: make(chan net.Conn),
+	}, nil
+}
+
+func (s *tcpserver) acceptConnections() {
+	defer s.wg.Done()
+
+	for {
+		select {
+		case <-s.shutdown:
+			return
+		default:
+			conn, err := s.listener.Accept()
+			if err != nil {
+				continue
+			}
+			s.connection <- conn
+		}
+	}
+}
+
+func (s *tcpserver) handleConnections() {
+	defer s.wg.Done()
+
+	for {
+		select {
+		case <-s.shutdown:
+			return
+		case conn := <-s.connection:
+			go s.handleConnection(conn)
+		}
+	}
+}
+
+func (s *tcpserver) handleConnection(conn net.Conn) {
+	defer conn.Close()
+
+	tlscon, ok := conn.(*tls.Conn)
+	if ok {
+		state := tlscon.ConnectionState()
+		log.Println("Server: client public key is:")
+		clientCN := state.PeerCertificates[0].Subject.CommonName
+		fmt.Printf("clientCN: %s\n", clientCN)
+	}
+
+	// Read incoming data
+	buf := make([]byte, 1024)
+	_, err := conn.Read(buf)
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+
+	fmt.Printf("Received: %s", buf)
+	decodeTlv, bytes, err := tlv.DecodeTlv(buf)
+	if err == nil {
+		fmt.Printf("Decoded: tlv: %s with value: %x", decodeTlv, bytes)
+	}
+}
+
+func (s *tcpserver) Start() {
+	s.wg.Add(2)
+	go s.acceptConnections()
+	go s.handleConnections()
+}
+
+func (s *tcpserver) Stop() {
+	close(s.shutdown)
+	s.listener.Close()
+
+	done := make(chan struct{})
+	go func() {
+		s.wg.Wait()
+		close(done)
+	}()
+
+	select {
+	case <-done:
+		return
+	case <-time.After(time.Second):
+		fmt.Println("Timed out waiting for connections to finish.")
+		return
+	}
+}
+
+func RunTcpServer(cfg config.Configuration) {
+	s, err := createServer(cfg)
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+
+	s.Start()
+
+	// Wait for a SIGINT or SIGTERM signal to gracefully shut down the tcpserver
+	sigChan := make(chan os.Signal, 1)
+	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
+	<-sigChan
+
+	fmt.Println("Shutting down tcpserver...")
+	s.Stop()
+	fmt.Println("tcpserver stopped.")
+}




diff --git a/server/tlv/tlvpackets.go b/server/tlv/tlvpackets.go
new file mode 100644
index 0000000000000000000000000000000000000000..ea58be33405dacab4f782c568b890e5a6031c0b6
--- /dev/null
+++ b/server/tlv/tlvpackets.go
@@ -0,0 +1,54 @@
+package tlv
+
+import (
+	"fmt"
+	"github.com/pkg/errors"
+
+	"github.com/pauloavelar/go-tlv/tlv"
+)
+
+func GetTlvString(data []byte) (string, error) {
+	n, _, err := tlv.DecodeSingle(data)
+	if err != nil {
+		return "", errors.WithStack(err)
+	}
+
+	return n.String(), nil
+}
+
+func GetNodes(data []byte) (tlv.Nodes, error) {
+	n, _, err := tlv.DecodeSingle(data)
+	if err != nil {
+		return nil, errors.WithStack(err)
+	}
+
+	return n.GetNodes()
+}
+
+func DecodeTlv(data []byte) (tlv.Tag, []byte, error) {
+	n, _, err := tlv.DecodeSingle(data)
+	if err != nil {
+		return 0, nil, errors.WithStack(err)
+	}
+	return n.Tag, n.Value, nil
+}
+
+func example() {
+	data := []byte{0xF0, 0x02, 0x00, 0x01, 0xFF}
+
+	n, read, err := tlv.DecodeSingle(data)
+	if err != nil {
+		panic(err) // invalid payload length vs bytes available
+	}
+
+	fmt.Printf("Read: %d", read)
+
+	n.String()         // returns a base64 representation of the raw message
+	n.GetNodes()       // parses the value as TLV and returns a Nodes structure (or error)
+	n.GetUint8()       // parses the value as uint8 (returns error if value is too small)
+	n.GetPaddedUint8() // parses the value as uint8 and pads it if too small
+
+	fmt.Printf("string: %s, tag: %d", n.String(), n.Tag)
+
+	// all available types: bool, uint8, uint16, uint32, uint64, string, time.Time and Nodes
+}