Skip to content

Commit

Permalink
adding token login support and fixed identifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
techBeck03 committed Jan 19, 2023
1 parent d238874 commit 81c6c48
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 185 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ lint:

test: lint
go test -count=1 -v -cover --race -tags="unittests" ./

test_specific: lint
go test -count=1 -v -cover --race -tags="specific" ./
86 changes: 56 additions & 30 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"

Expand All @@ -24,6 +25,9 @@ type Config struct {
Username string
DisableTLSVerification bool
DisableCookies bool
Token string
DataSource string
Cookies map[string]string
}

// Client - base client for guacamole interactions
Expand Down Expand Up @@ -54,31 +58,56 @@ func New(config Config) Client {

// Connect - function for establishing connection to guacamole
func (c *Client) Connect() error {
resp, err := c.client.PostForm(fmt.Sprintf("%s/%s", c.config.URL, tokenPath),
url.Values{
"username": {c.config.Username},
"password": {c.config.Password},
})
if err != nil {
return err
}
if resp.StatusCode == 403 {
return fmt.Errorf("Invalid Credentials")
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
var tokenresp types.AuthenticationResponse
// check if token and dataSource are provided
if c.config.Token != "" && c.config.DataSource != "" {
// test supplied token and dataSource are valid
c.baseURL = fmt.Sprintf("%s/api/session/data/%s", c.config.URL, c.config.DataSource)
req, _ := c.CreateJSONRequest("GET", fmt.Sprintf("%s/schema/userAttributes", c.baseURL), nil)

c.token = c.config.Token
for k, v := range c.config.Cookies {
cookie := &http.Cookie{
Name: k,
Value: v,
}
c.cookies = append(c.cookies, cookie)
}
var result interface{}
err := c.Call(req, &result)
if err != nil {
log.Printf("%s", err)
return err
}
if result == nil {
return fmt.Errorf("unable to connect using supplied token and dataSource")
}
} else {
resp, err := c.client.PostForm(fmt.Sprintf("%s/%s", c.config.URL, tokenPath),
url.Values{
"username": {c.config.Username},
"password": {c.config.Password},
})
if err != nil {
return err
}
if resp.StatusCode == 403 {
return fmt.Errorf("invalid Credentials")
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
var tokenresp types.AuthenticationResponse

err = json.Unmarshal(body, &tokenresp)
if err != nil {
return err
}
c.token = tokenresp.AuthToken
c.baseURL = fmt.Sprintf("%s/api/session/data/%s", c.config.URL, tokenresp.DataSource)
if !(c.config.DisableCookies) {
c.cookies = resp.Cookies()
err = json.Unmarshal(body, &tokenresp)
if err != nil {
return err
}
c.token = tokenresp.AuthToken
c.baseURL = fmt.Sprintf("%s/api/session/data/%s", c.config.URL, tokenresp.DataSource)
if !(c.config.DisableCookies) {
c.cookies = resp.Cookies()
}
}
return nil
}
Expand Down Expand Up @@ -115,11 +144,8 @@ func (c *Client) CreateJSONRequest(method string, path string, params interface{

// Call - function for handling http requests
func (c *Client) Call(request *http.Request, result interface{}) error {
// Add authentication token to query params
q := request.URL.Query()
q.Add("token", c.token)

request.URL.RawQuery = q.Encode()
// Add authentication token to request Header
request.Header.Set("Guacamole-Token", c.token)

// Add cookies if configured
if !(c.config.DisableCookies) {
Expand All @@ -141,7 +167,7 @@ func (c *Client) Call(request *http.Request, result interface{}) error {
body := io.TeeReader(response.Body, &rawBodyBuffer)
var responseBody interface{}
json.NewDecoder(body).Decode(&responseBody)
return fmt.Errorf("Request %+v\n failed with status code %d\n response %+v\n%+v", request,
return fmt.Errorf("request %+v\n failed with status code %d\n response %+v\n%+v", request,
response.StatusCode, responseBody,
response)
}
Expand Down
95 changes: 49 additions & 46 deletions connection_groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"net/http"
"net/url"
"strings"

"github.com/techBeck03/guacamole-api-client/types"
)
Expand All @@ -30,31 +29,30 @@ func (c *Client) GetConnectionTree(identifier string) (types.GuacConnectionGroup
return ret, nil
}

// flatten Flattens the
func flatten(nested []types.GuacConnectionGroup) ([]types.GuacConnection, []types.GuacConnectionGroup, error) {
flatConns := []types.GuacConnection{}
flatGrps := []types.GuacConnectionGroup{}
for _, groups := range nested {
flatGrps = append(flatGrps, groups)
if len(groups.ChildGroups) > 0 {
conns, subgrps, err := flatten(groups.ChildGroups)
if err != nil {
return nil, nil, err
}
for _, c := range conns {
flatConns = append(flatConns, c)
}
for _, g := range subgrps {
flatGrps = append(flatGrps, g)
}
// getPathTree generates a map of all connection paths
func (c *Client) getPathTree(nested types.GuacConnectionGroup, results *types.GuacConnectionGroupPathTree) error {
for _, group := range nested.ChildGroups {
if nested.Path != "" {
group.Path = fmt.Sprintf("%s/%s", nested.Path, group.Name)
} else {
group.Path = group.Name
}
if len(groups.ChildConnections) > 0 {
for _, c := range groups.ChildConnections {
flatConns = append(flatConns, c)
}
results.Groups[group.Identifier] = group.Path
err := c.getPathTree(group, results)
if err != nil {
return err
}
}
return flatConns, flatGrps, nil

for _, connection := range nested.ChildConnections {
if nested.Name == "ROOT" {
results.Connections[connection.Identifier] = connection.Name
} else {
results.Connections[connection.Identifier] = fmt.Sprintf("%s/%s", nested.Path, connection.Name)
}
}

return nil
}

// CreateConnectionGroup creates a guacamole connection group
Expand Down Expand Up @@ -108,46 +106,51 @@ func (c *Client) ReadConnectionGroup(identifier string) (types.GuacConnectionGro
// ReadConnectionGroupByPath gets a connection group by path (Parent/Name)
func (c *Client) ReadConnectionGroupByPath(path string) (types.GuacConnectionGroup, error) {
var ret types.GuacConnectionGroup
var parentIdentifier string

splitPath := strings.Split(path, "/")
groups, err := c.ListConnectionGroups()
groups, err := c.GetConnectionTree("ROOT")

if err != nil {
return ret, err
}

if strings.ToUpper(splitPath[0]) == "ROOT" {
parentIdentifier = "ROOT"
} else {
for group := range groups {
if groups[group].Name == splitPath[0] {
parentIdentifier = groups[group].Identifier
break
}
}
}
var tree types.GuacConnectionGroupPathTree
tree.Connections = make(map[string]string)
tree.Groups = make(map[string]string)
err = c.getPathTree(groups, &tree)

if parentIdentifier == "" {
return ret, fmt.Errorf("No connection group found for parent with name: %s", splitPath[0])
if err != nil {
return ret, err
}

for _, group := range groups {
if (group.ParentIdentifier == parentIdentifier) && (group.Name == splitPath[1]) {
readGroup, err := c.ReadConnectionGroup(group.Identifier)
for i, p := range tree.Groups {
if p == path {
grp, err := c.ReadConnectionGroup(i)
grp.Path = path
if err != nil {
return ret, err
}
ret = readGroup
break
return grp, nil
}
}

if ret.Identifier == "" {
return ret, fmt.Errorf("No connection group found with parentIdentifier = %s\tname = %s", parentIdentifier, splitPath[1])
return ret, fmt.Errorf("no connection group found with path: %s", path)
}

// GetConnectionGroupPathById gets a connection group path by identifier
func (c *Client) GetConnectionGroupPathById(identifier string) (string, error) {
groups, err := c.GetConnectionTree("ROOT")
if err != nil {
return "", err
}

return ret, nil
var tree types.GuacConnectionGroupPathTree
tree.Connections = make(map[string]string)
tree.Groups = make(map[string]string)
err = c.getPathTree(groups, &tree)
if err != nil {
return "", err
}
return tree.Groups[identifier], nil
}

// UpdateConnectionGroup updates a connection group by identifier
Expand Down
Loading

0 comments on commit 81c6c48

Please sign in to comment.