Skip to content
This repository has been archived by the owner on Feb 15, 2023. It is now read-only.

Add pairing implementation #9

Merged
merged 36 commits into from
Nov 19, 2021
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
36033a0
Add pairing implementation
gudgl Nov 2, 2021
dc593db
Fix typo
gudgl Nov 4, 2021
dd12917
Fix urls
gudgl Nov 4, 2021
5b161fc
Create request with context
gudgl Nov 4, 2021
6c39173
Refactor pair
gudgl Nov 4, 2021
f385142
Tidy up dependencues
gudgl Nov 4, 2021
288bb06
Remove split word tags
gudgl Nov 6, 2021
ac56683
Use session lock
gudgl Nov 6, 2021
c6442c8
Refactor pairing
gudgl Nov 6, 2021
d000ae9
Improve log
gudgl Nov 6, 2021
c8bdfc6
Wrap errors
gudgl Nov 6, 2021
91f8a95
Remove extra letter
gudgl Nov 6, 2021
9df2c05
use session lock when pairing
gudgl Nov 6, 2021
d274b24
Use new pair method
gudgl Nov 6, 2021
1429d5a
remove handling marshal request
gudgl Nov 6, 2021
c3a8aed
Try removingpackage
gudgl Nov 6, 2021
079d368
Revert last commit
gudgl Nov 6, 2021
8f7633c
move pairing code to create handler
gudgl Nov 7, 2021
8d6e0d2
Rename pcpairing to pairing package
gudgl Nov 7, 2021
aa56bda
Decrease log level
gudgl Nov 7, 2021
9b4b2da
Use WriteJSONError for error response
gudgl Nov 7, 2021
3eab51e
Fix url sent to pairing
gudgl Nov 7, 2021
3d3f176
Add error to the response
gudgl Nov 7, 2021
e8b5ca4
Add logs andm ore descriptiove errors
gudgl Nov 7, 2021
cc326d3
Go fmt
gudgl Nov 7, 2021
e201499
Remove codes map
gudgl Nov 7, 2021
862fab8
Fix pairing
gudgl Nov 7, 2021
4d20b13
Fix returned status code when pair is successful
gudgl Nov 9, 2021
613f503
Extract pairing on create
gudgl Nov 9, 2021
bfdd52e
Fix linter
gudgl Nov 9, 2021
88636f5
Rework pairing
gudgl Nov 12, 2021
6330983
Go fmt
gudgl Nov 12, 2021
42764b8
Refactor getting session when paring
gudgl Nov 15, 2021
1ef1e4e
Refactor pairing
gudgl Nov 17, 2021
d4f72f4
Remove server address
gudgl Nov 19, 2021
15d80d9
Go fmt
gudgl Nov 19, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions api/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ type Session struct {
ID string
SessionURL string
AgentMSH string
PairingCode string `json:"PairingCode,omitempty"`
PairingURL string `json:"PairingURL,omitempty"`
AgentConfig AgentConfig
ExpiresAt time.Time
}
Expand All @@ -29,3 +31,12 @@ type ListSessionsItem struct {
SessionPassword string `json:",omitempty"`
ExpiresAt time.Time
}

type PairedSession struct {
AgentMSH string
SupporterName string
SupporterAvatar string
CompanyName string
CompanyLogo string
ExpiresAt time.Time
}
3 changes: 2 additions & 1 deletion cmd/plexus/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ var serve = &cli.Command{
}

opt := &handler.Options{
Config: cfg.AsControlConfig(),
ControlConfig: cfg.AsControlConfig(),
PairingConfig: cfg.AsPairingConfig(),
Auth: auth,
Log: zerologger.Get(),
Prefix: cfg.PathPrefix,
Expand Down
15 changes: 15 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/rs/zerolog"

"github.com/cloudradar-monitoring/plexus/control"
"github.com/cloudradar-monitoring/plexus/pcpairing"
gudgl marked this conversation as resolved.
Show resolved Hide resolved
)

var (
Expand Down Expand Up @@ -40,6 +41,11 @@ type Config struct {

AuthUser string `split_words:"true"`
AuthPass string `split_words:"true"`

PairingURL string `split_words:"true"`
PairingTTL int `split_words:"true"`
CompanyName string `split_words:"true"`
CompanyLogo string `split_words:"true"`
}

func (s *Config) AsControlConfig() *control.Config {
Expand All @@ -52,6 +58,15 @@ func (s *Config) AsControlConfig() *control.Config {
}
}

func (s *Config) AsPairingConfig() *pcpairing.Config {
return &pcpairing.Config{
PairingURL: s.PairingURL,
PairingTTL: s.PairingTTL,
CompanyName: s.CompanyName,
CompanyLogo: s.CompanyLogo,
}
}

func (s *Config) MeshCentralControlURL() string {
return s.MeshCentralURL + "/" + s.MeshCentralDomain + "/control.ashx"
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ require (
github.com/kelseyhightower/envconfig v1.4.0
github.com/rs/zerolog v1.23.0
github.com/urfave/cli/v2 v2.3.0
golang.org/x/net v0.0.0-20201021035429-f5854403a974
)
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down
38 changes: 35 additions & 3 deletions handler/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
// @Param ttl formData int true "the time to live for the session"
// @Param username formData string true "the credentials to open the remote control interface & delete the session"
// @Param password formData string true "the credentials to open the remote control interface & delete the session"
// @Param supporter_name formData string true "the supporter name"
// @Param supporter_avatar formData string true "the supporter avatar"
// @Success 200 {object} api.Session
// @Failure 400 {object} api.Error
// @Failure 500 {object} api.Error
Expand All @@ -36,6 +38,8 @@ func (h *Handler) CreateSession(rw http.ResponseWriter, r *http.Request) {
ttlStr := r.FormValue("ttl")
user := r.FormValue("username")
pass := r.FormValue("password")
supName := r.FormValue("supporter_name")
supAvatar := r.FormValue("supporter_avatar")
ttl, err := strconv.ParseInt(ttlStr, 10, 64)
if err != nil {
api.WriteBadRequestJSON(rw, fmt.Sprintf("invalid ttl %s: %s", id, err))
Expand All @@ -55,13 +59,13 @@ func (h *Handler) CreateSession(rw http.ResponseWriter, r *http.Request) {
return
}

mc, err := control.Connect(h.cfg, h.log)
mc, err := control.Connect(h.ccfg, h.log)
defer mc.Close()
if err != nil {
api.WriteBadGatewayJSON(rw, fmt.Sprintf("could not connect to mesh control: %s", err))
return
}
mesh, err := mc.CreateMesh(h.cfg.MeshCentralGroupPrefix + "/" + id + "/" + token.New(5))
mesh, err := mc.CreateMesh(h.ccfg.MeshCentralGroupPrefix + "/" + id + "/" + token.New(5))
if err != nil {
api.WriteBadGatewayJSON(rw, fmt.Sprintf("could not create mesh: %s", err))
return
Expand All @@ -88,6 +92,32 @@ func (h *Handler) CreateSession(rw http.ResponseWriter, r *http.Request) {
MeshType: 2,
},
}

if h.pcfg.PairingURL != "" {
h.log.Infof("pairing...")
gudgl marked this conversation as resolved.
Show resolved Hide resolved

if supName == "" || supAvatar == "" {
api.WriteJSONError(rw, http.StatusBadRequest, "You need to provide supporter_name and supporter_avatar for pairing")
return
}
pr, err := h.pcPair(r.Context(), h.pcfg.PairingURL, &Request{
Url: h.pcfg.PairingURL,
gudgl marked this conversation as resolved.
Show resolved Hide resolved
})
if err != nil {
api.WriteJSONError(rw, http.StatusBadGateway, "Unable to create session, failed to pair")
gudgl marked this conversation as resolved.
Show resolved Hide resolved
return
}

h.log.Infof("pairing succeeded")
gudgl marked this conversation as resolved.
Show resolved Hide resolved

session.PairingCode = pr.Code
session.PairingURL = pr.PairingURL
session.SupporterName = supName
session.SupporterAvatar = supAvatar

h.codes[pr.Code] = id
}

h.sessions[id] = session

go func() {
Expand All @@ -107,8 +137,10 @@ func (h *Handler) CreateSession(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusCreated)
_ = json.NewEncoder(rw).Encode(&api.Session{
ID: id,
AgentMSH: fmt.Sprintf("https://%s%s", r.Host, path.Join(h.prefix, "config", fmt.Sprintf("%s:%s", id, sessionToken))),
SessionURL: fmt.Sprintf("https://%s%s", r.Host, path.Join(h.prefix, "session", id)),
AgentMSH: fmt.Sprintf("https://%s%s", r.Host, path.Join(h.prefix, "config", fmt.Sprintf("%s:%s", id, sessionToken))),
PairingCode: session.PairingCode,
PairingURL: session.PairingURL,
AgentConfig: session.AgentConfig,
ExpiresAt: session.ExpiresAt,
})
Expand Down
2 changes: 1 addition & 1 deletion handler/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (h *Handler) deleteInternal(s *Session) error {
s.ProxyClose()
}

mc, err := control.Connect(h.cfg, h.log)
mc, err := control.Connect(h.ccfg, h.log)
if err != nil {
return err
}
Expand Down
16 changes: 13 additions & 3 deletions handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import (
"github.com/cloudradar-monitoring/plexus/api"
"github.com/cloudradar-monitoring/plexus/control"
"github.com/cloudradar-monitoring/plexus/logger"
"github.com/cloudradar-monitoring/plexus/pcpairing"
)

type AuthChecker func(rw http.ResponseWriter, r *http.Request) bool

type Options struct {
Config *control.Config
ControlConfig *control.Config
PairingConfig *pcpairing.Config
Log logger.Logger
Auth AuthChecker
Prefix string
Expand All @@ -31,7 +33,8 @@ func Register(r *mux.Router, opt *Options) {
h := &Handler{
log: opt.Log,
auth: opt.Auth,
cfg: opt.Config,
ccfg: opt.ControlConfig,
pcfg: opt.PairingConfig,
prefix: opt.Prefix,
sessionCredentials: opt.AllowSessionCredentials,
sessions: make(map[string]*Session),
Expand All @@ -45,16 +48,19 @@ func Register(r *mux.Router, opt *Options) {
plexus.HandleFunc("/config/{id}:{token}", h.GetAgentMsh).Methods(http.MethodGet)
plexus.HandleFunc("/agent/{id}:{token}", h.ProxyAgent).Methods(http.MethodGet)
plexus.HandleFunc("/meshrelay.ashx", h.ProxyRelay).Methods(http.MethodGet)
plexus.HandleFunc("/pairing/{code}", h.Pair).Methods(http.MethodGet)
r.PathPrefix(h.ProxyMeshCentralURL()).Handler(h.ProxyMeshCentral())
}

type Handler struct {
log logger.Logger
cfg *control.Config
ccfg *control.Config
pcfg *pcpairing.Config
auth AuthChecker
lock sync.RWMutex
sessionCredentials bool
sessions map[string]*Session
codes map[string]string
prefix string
}
type Session struct {
Expand All @@ -64,5 +70,9 @@ type Session struct {
Token string
AgentConfig api.AgentConfig
ShareURL string
SupporterName string
SupporterAvatar string
PairingCode string
PairingURL string
ProxyClose func()
}
47 changes: 47 additions & 0 deletions handler/pair.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package handler

import (
"fmt"
"net/http"
"path"

"github.com/gorilla/mux"

"github.com/cloudradar-monitoring/plexus/api"
)

// Pair godoc
// @Summary Gets the pairing code info.
// @Tags session
// @Produce application/json
// @Param code path string true "code"
// @Security BasicAuth
// @Success 200 {object} api.URLResponse
// @Failure 401 {object} api.Error
// @Failure 404 {object} api.Error
// @Router /session/{code} [get]
gudgl marked this conversation as resolved.
Show resolved Hide resolved
func (h *Handler) Pair(rw http.ResponseWriter, r *http.Request) {
h.lock.Lock()
defer h.lock.Unlock()
id, ok := h.codes[mux.Vars(r)["id"]]
gudgl marked this conversation as resolved.
Show resolved Hide resolved
if !ok {
api.WriteJSONResponse(rw, http.StatusNotFound, map[string]interface{}{
gudgl marked this conversation as resolved.
Show resolved Hide resolved
"message": "Code not found",
})
return
}

session, ok := h.checkSessionAuthentication(rw, r, id)
if !ok {
return
}
gudgl marked this conversation as resolved.
Show resolved Hide resolved

api.WriteJSONResponse(rw, http.StatusNotFound, api.PairedSession{
gudgl marked this conversation as resolved.
Show resolved Hide resolved
AgentMSH: fmt.Sprintf("https://%s%s", r.Host, path.Join(h.prefix, "config", fmt.Sprintf("%s:%s", id, session.Token))),
SupporterName: session.SupporterName,
SupporterAvatar: session.SupporterAvatar,
CompanyName: h.pcfg.CompanyName,
CompanyLogo: h.pcfg.CompanyLogo,
ExpiresAt: session.ExpiresAt,
})
}
63 changes: 63 additions & 0 deletions handler/pcpair.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package handler

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"time"

"golang.org/x/net/context/ctxhttp"
)

const (
defaultTimeout = 5 * time.Second
contentType = "application/json"
)

var ErrUnableToPair = errors.New("unable to pair")

type Request struct {
Url string `json:"url"`
}

type Response struct {
Success bool `json:"success"`
Code string `json:"code"`
PairingURL string `json:"pairing_url"`
RedirectURL string `json:"redirect_url"`
}

func (h *Handler) pcPair(ctx context.Context, url string, req *Request) (*Response, error) {
jsonRequest, _ := json.Marshal(req)
client := &http.Client{
Timeout: defaultTimeout,
}

response, err := ctxhttp.Post(ctx, client, url, contentType, bytes.NewBuffer(jsonRequest))
if err != nil {
return nil, fmt.Errorf("post failed: %w", err)
}

defer response.Body.Close()
jsonResponse, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, fmt.Errorf("reading body failed: %w", err)
gudgl marked this conversation as resolved.
Show resolved Hide resolved
}

if response.StatusCode != http.StatusOK {
h.log.Errorf("pairing request failed: status(%d) response(%s)", response.StatusCode, string(jsonResponse))
return nil, ErrUnableToPair
}

resp := Response{}
err = json.Unmarshal(jsonResponse, &resp)
if err != nil {
return nil, fmt.Errorf("unmarshaling response failed: %w", err)
}

return &resp, nil
}
8 changes: 4 additions & 4 deletions handler/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

func (h *Handler) ProxyMeshCentralURL() string {
return fmt.Sprintf("/%s/", h.cfg.MeshCentralDomain)
return fmt.Sprintf("/%s/", h.ccfg.MeshCentralDomain)
}
func (h *Handler) ProxyMeshCentral() http.Handler {
director := func(req *http.Request) {
Expand All @@ -21,7 +21,7 @@ func (h *Handler) ProxyMeshCentral() http.Handler {
} else {
req.URL.Scheme = "http"
}
req.URL.Host = h.cfg.MeshCentralURL.Host
req.URL.Host = h.ccfg.MeshCentralURL.Host
req.Header.Set("User-Agent", "plexus")
}
return &httputil.ReverseProxy{
Expand All @@ -35,7 +35,7 @@ func (h *Handler) ProxyMeshCentral() http.Handler {
// @Success 200 {object} string
// @Router /meshrelay.ashx [get]
func (h *Handler) ProxyRelay(rw http.ResponseWriter, r *http.Request) {
proxyURL := fmt.Sprintf("%s?%s", h.cfg.MeshRelayURL(), r.URL.RawQuery)
proxyURL := fmt.Sprintf("%s?%s", h.ccfg.MeshRelayURL(), r.URL.RawQuery)
_, ok := proxy.Proxy(h.log, rw, r, proxyURL)
if ok {
h.log.Debugf("MeshRelay Connected: %s", r.URL.String())
Expand All @@ -62,7 +62,7 @@ func (h *Handler) ProxyAgent(rw http.ResponseWriter, r *http.Request) {
return
}

agentClose, ok := proxy.Proxy(h.log, rw, r, h.cfg.MeshCentralAgentURL())
agentClose, ok := proxy.Proxy(h.log, rw, r, h.ccfg.MeshCentralAgentURL())
if ok {
session.ProxyClose = agentClose
}
Expand Down
2 changes: 1 addition & 1 deletion handler/share.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (h *Handler) ShareSessionURL(rw http.ResponseWriter, r *http.Request) {
}

func (h *Handler) tryGetURL(rw http.ResponseWriter, session *Session) (string, bool) {
mc, err := control.Connect(h.cfg, h.log)
mc, err := control.Connect(h.ccfg, h.log)
if err != nil {
api.WriteBadGatewayJSON(rw, fmt.Sprintf("could not connect: %s", err))
return "", true
Expand Down
8 changes: 8 additions & 0 deletions pcpairing/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package pcpairing

type Config struct {
PairingURL string
PairingTTL int
CompanyName string
CompanyLogo string
}
Loading