Skip to content

Commit

Permalink
initial app service implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
ricoschulte committed Mar 10, 2023
1 parent 7939a84 commit 2192b10
Show file tree
Hide file tree
Showing 19 changed files with 2,020 additions and 3 deletions.
9 changes: 7 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@ module github.com/ricoschulte/go-myapps
go 1.19

require (
github.com/go-chi/chi v1.5.4
github.com/gorilla/websocket v1.5.0
github.com/stretchr/testify v1.8.1
gotest.tools v2.2.0+incompatible
)

require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
require (
github.com/go-chi/chi/v5 v5.0.1 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
)

require (
github.com/chi-middleware/logrus-logger v0.2.0
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sirupsen/logrus v1.9.0
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
21 changes: 20 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
@@ -1,29 +1,48 @@
github.com/chi-middleware/logrus-logger v0.2.0 h1:Do3vcVSRsLh7zSRKxsVg5Kr5//rTqytwprCR1HzVqT8=
github.com/chi-middleware/logrus-logger v0.2.0/go.mod h1:ie/rvKsXrtqqsnJd3qtSEnLxgCs1I758WYmHdv6CRt0=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
github.com/go-chi/chi/v5 v5.0.1 h1:ALxjCrTf1aflOlkhMnCUP86MubbWFrzB3gkRPReLpTo=
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
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=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
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=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
Expand Down
130 changes: 130 additions & 0 deletions helper/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package helper

import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"sort"
"strings"
)

// func PrettyPrintJSON(data []map[string]string) string {
func PrettyPrintJSON(data any) string {
b, err := json.MarshalIndent(data, "", " ")
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
return string(b)
}

/*
splits a string by delimiter but ignores delimiter escaped by escape
in: aaaaa:bbbb:cccc\:dddd
out: [aaaaa,bbbb,cccc\:dddd]
splitIgnoreEscape(line2, ':', '\\')
*/
func SplitIgnoreEscape(str string, delimiter byte, escape byte) []string {
var parts []string
var buffer strings.Builder

escaped := false

for i := 0; i < len(str); i++ {
char := str[i]

if !escaped && char == escape {
escaped = true
continue
}

if escaped {
if char != delimiter && char != escape {
buffer.WriteByte(escape)
}
buffer.WriteByte(char)
escaped = false
continue
}

if char == delimiter {
parts = append(parts, buffer.String())
buffer.Reset()
continue
}

buffer.WriteByte(char)
}

parts = append(parts, buffer.String())

return parts
}

/*
splits a string by delimiter and returns a slice of non empty strings
SplitStringIgnoreEmpty(value, ";")
in "aaaa;bbbb;;cccc"
out ["aaaa", "bbbb", "cccc"]
*/
func SplitStringIgnoreEmpty(value string, delimiter string) []string {
filteredSlice := make([]string, 0)
for _, str := range strings.Split(value, ";") {
if strings.TrimSpace(str) != "" {
filteredSlice = append(filteredSlice, str)
}
}
return filteredSlice
}

// sha256 hash from map[string]string
func Sha265HashFromMap(m map[string]string) string {
// Get a sorted slice of the keys
keys := make([]string, 0, len(m))
for key := range m {
keys = append(keys, key)
}
sort.Strings(keys)

// Concatenate the key-value pairs
var str string
for _, key := range keys {
str += key + m[key]
}

// Compute the SHA-256 hash
hash := sha256.Sum256([]byte(str))

// Return the hexadecimal encoding of the hash
return hex.EncodeToString(hash[:])
}

/*
takes two []string slices as input and returns true if all the elements in the first slice (s1) are present in the second slice (s2), regardless of their order, and false otherwise.
The function first checks if the two slices have the same length, and if not, it immediately returns false. Then, it creates a map (m) and adds each element of the second slice (s2) to the map as a key with a value of true.
Next, it iterates over the elements of the first slice (s1) and checks if each element is present in the map (m). If any element is not present in the map, the function returns false. If all elements are present in the map, the function returns true.
*/
func AreEqualSlices(s1, s2 []string) bool {
if len(s1) != len(s2) {
return false
}

m := make(map[string]bool)
for _, v := range s2 {
m[v] = true
}

for _, v := range s1 {
if _, ok := m[v]; !ok {
return false
}
}

return true
}
25 changes: 25 additions & 0 deletions service/epsignal/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package epsignal

import (
"github.com/ricoschulte/go-myapps/service"
log "github.com/sirupsen/logrus"
)

type EpSignal struct {
}

func (api *EpSignal) GetApiName() string {
return "EpSignal"
}

func (api *EpSignal) OnConnect(connection *service.AppServicePbxConnection) {
log.WithField("api", api.GetApiName()).Warn("OnConnect not implemented ")
}

func (api *EpSignal) OnDisconnect(connection *service.AppServicePbxConnection) {
log.WithField("api", api.GetApiName()).Warn("OnDisconnect not implemented ")
}

func (api *EpSignal) HandleMessage(connection *service.AppServicePbxConnection, msg *service.BaseMessage, message []byte) {
log.WithField("api", api.GetApiName()).Warn("HandleMessage not implemented ")
}
97 changes: 97 additions & 0 deletions service/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package service

import (
"fmt"
"io"

"net/http"

log "github.com/sirupsen/logrus"
)

func AppIndex(appservice *AppService, w http.ResponseWriter, req *http.Request) {
w.Header().Add("Content-Type", "text/html")
w.Write([]byte("app index"))
}

// handles both websocket and http GET/POST requests on the same path
func handleConnectionForHttpOrWebsocket(appservice *AppService, w http.ResponseWriter, r *http.Request) {
log.Warnf("serve handleConnectionForHttpOrWebsocket %s", r.URL.Path)
if r.URL.Path != "/test.company.com/testservice/mytestservice" {
return
}
if r.Method != http.MethodGet && r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}

if r.Header.Get("Upgrade") == "websocket" {
HandleWebsocket(appservice, w, r)
} else {
AppIndex(appservice, w, r)
}
}

func ServeFile(fs http.FileSystem, w http.ResponseWriter, r *http.Request, filename string, contenttype string) {
log.Warnf("ServeFile %s", filename)

file, err := fs.Open(filename)
if err != nil {
fmt.Fprintf(w, "%v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer file.Close()

content, err_file := io.ReadAll(file)
if err_file != nil {
fmt.Fprintf(w, "Error while reading file: %v", err_file)
http.Error(w, err_file.Error(), http.StatusInternalServerError)
return
}

w.Header().Add("Content-Type", contenttype)
_, err_write := w.Write(content)
if err_write != nil {
fmt.Fprintf(w, "Error while writing to response: %v", err_write)
http.Error(w, err_write.Error(), http.StatusInternalServerError)
return
}

}

// func GetHttpRoutes(appservice *AppService, mux *chi.Mux) error {

// // Serve static files on app path
// // mux.Get("/static/*",
// // http.StripPrefix("/test.company.com/testservice/mytestservice/static/",
// // http.FileServer(appservice.Fs),
// // ).ServeHTTP,
// // )

// // mux.Get("/admin.htm", func(w http.ResponseWriter, r *http.Request) {
// // ServeFile(appservice.Fs, w, r, "admin.htm", "text/html")
// // })
// // mux.Get("/user.htm", func(w http.ResponseWriter, r *http.Request) {
// // log.Warnf("serve /user.htm %s", r.URL.Path)
// // ServeFile(appservice.Fs, w, r, "user.htm", "text/html")
// // })
// // mux.Get("/searchapi.htm", func(w http.ResponseWriter, r *http.Request) {
// // ServeFile(appservice.Fs, w, r, "searchapi.htm", "text/html")
// // })

// // mux.Get("/user.png", func(w http.ResponseWriter, r *http.Request) {
// // ServeFile(appservice.Fs, w, r, "user.png", "image/png")
// // })
// // mux.Get("/admin.png", func(w http.ResponseWriter, r *http.Request) {
// // ServeFile(appservice.Fs, w, r, "admin.png", "image/png")
// // })
// // mux.Get("/app.css", func(w http.ResponseWriter, r *http.Request) {
// // ServeFile(appservice.Fs, w, r, "app.css", "text/css; charset=utf-8")
// // })
// // mux.Get("/app.js", func(w http.ResponseWriter, r *http.Request) {
// // ServeFile(appservice.Fs, w, r, "app.js", "text/javascript; charset=utf-8")
// // })

// return nil
// }
64 changes: 64 additions & 0 deletions service/innotools.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package service

import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"math/rand"
"strings"
"time"

"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)

type MyAppsUtils struct{}

func (mu *MyAppsUtils) GetDigestHashForUserLogingToAppService(app string, domain string, sip string, guid string, dn string, info string, challenge string, password string) string {
str := fmt.Sprintf("%s:%s:%s:%s:%s:%s:%s:%s", app, domain, sip, guid, dn, info, challenge, password)
bytes := sha256.Sum256([]byte(str))
return hex.EncodeToString(bytes[:])
}

func (mu *MyAppsUtils) GetDigestForAppLoginFromJson(message_json, password, challenge string) (string, error) {
var msgin AppLogin
if err := json.Unmarshal([]byte(message_json), &msgin); err != nil {
return "", err
}
var calculated_digest string

if msgin.PbxObj != "" {
// user/admin login
log.Trace("GetDigestForAppLoginFromJson user/admin login")
info, _ := msgin.InfoAsUserDigestString()
calculated_digest = mu.GetDigestHashForUserLogingToAppService(msgin.App, msgin.Domain, msgin.Sip, msgin.Guid, msgin.Dn, info, challenge, password)
} else {
// pbxobj login
log.Trace("GetDigestForAppLoginFromJson pbxobj login")
info, _ := msgin.InfoAsPbxobjectDigestString()
calculated_digest = mu.GetDigestHashForUserLogingToAppService(msgin.App, msgin.Domain, msgin.Sip, msgin.Guid, msgin.Dn, info, challenge, password)
}

return calculated_digest, nil
}

func (mu *MyAppsUtils) GetRandomHexString(n int) string {
charPool := "abcdef0123456789"
rand.Seed(time.Now().UnixNano()) // does
b := strings.Builder{}
for i := 0; i < n; i++ {
b.WriteByte(charPool[rand.Intn(len(charPool))])
}
return b.String()
}

func CheckAppPasswordForMaximumLength(password string) error {
if password == "" {
return errors.Errorf("Error: App password cant be empty")
}
if len(password) > 15 {
return errors.Errorf("Error: App password invalid. The app password cant be longer than 15 chars. This is a limitation of passwords stored in pbx-objects. the currently set password has a length of %v", len(password))
}
return nil
}
Loading

0 comments on commit 2192b10

Please sign in to comment.