Initial commit
commit
f9194fcb29
|
@ -0,0 +1,14 @@
|
||||||
|
commands := $(wildcard cmd/*.go)
|
||||||
|
|
||||||
|
all: build
|
||||||
|
|
||||||
|
|
||||||
|
build:
|
||||||
|
@GO111Module=on go build -v -ldflags="-s -w" -buildmode=pie -o csctl ${commands}
|
||||||
|
@sudo chmod 2751 ./csctl
|
||||||
|
@file ./csctl
|
||||||
|
|
||||||
|
install:
|
||||||
|
@sudo cp cscli /usr/local/bin/csctl
|
||||||
|
@sudo chown root:cscli /usr/local/hbin/csctl
|
||||||
|
@sudo chmod 2751 /usr/local/bin/csctl
|
|
@ -0,0 +1,107 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"csctl/config"
|
||||||
|
"csctl/internal"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
// "os/user"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kataras/golog"
|
||||||
|
"github.com/logrusorgru/aurora/v3"
|
||||||
|
"github.com/olekukonko/tablewriter"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Decision string `json:"decision"`
|
||||||
|
ApplicationID string `json:"id"`
|
||||||
|
ProcessedBy string `json:"processedBy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyHard(ctx *cli.Context) error {
|
||||||
|
|
||||||
|
account, err := internal.FetchAccount("matthew")
|
||||||
|
if err != nil {
|
||||||
|
internal.Error(err, "Unable to read account information.")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Get(fmt.Sprintf("https://eds.libraryofcode.org/cs/t2/?userID=%s&auth=%s", account.UserID, config.FetchInternalKey()))
|
||||||
|
if err != nil {
|
||||||
|
internal.Error(err, "Unable to read information from EDS.")
|
||||||
|
}
|
||||||
|
|
||||||
|
tableString := &strings.Builder{}
|
||||||
|
table := tablewriter.NewWriter(tableString)
|
||||||
|
table.SetHeader([]string{"Status", "Application ID"})
|
||||||
|
|
||||||
|
if resp.StatusCode == 404 || resp.StatusCode == 400 || resp.StatusCode == 401 {
|
||||||
|
dataErr := [][]string{
|
||||||
|
{aurora.Yellow("PRE-DECLINED").String(), "N/A"},
|
||||||
|
}
|
||||||
|
table.AppendBulk(dataErr)
|
||||||
|
table.Render()
|
||||||
|
fmt.Println(tableString.String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var response Response
|
||||||
|
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
|
||||||
|
internal.Error(err, "Unable to parse JSON from CSD.")
|
||||||
|
}
|
||||||
|
|
||||||
|
data := [][]string{
|
||||||
|
{response.Decision, response.ApplicationID},
|
||||||
|
}
|
||||||
|
table.AppendBulk(data)
|
||||||
|
table.Render()
|
||||||
|
fmt.Println(tableString.String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplySoft(ctx *cli.Context) error {
|
||||||
|
golog.Debug("Fetching account information.")
|
||||||
|
account, err := internal.FetchAccount("matthew")
|
||||||
|
if err != nil {
|
||||||
|
internal.Error(err, "Unable to read account information.")
|
||||||
|
}
|
||||||
|
|
||||||
|
golog.Info("Processing application, do not exit the CLI...")
|
||||||
|
|
||||||
|
resp, err := http.Get(fmt.Sprintf("https://eds.libraryofcode.org/cs/t2pre/?userID=%s&auth=%s", account.UserID, config.FetchInternalKey()))
|
||||||
|
if err != nil {
|
||||||
|
internal.Error(err, "Unable to read response from EDS.")
|
||||||
|
}
|
||||||
|
|
||||||
|
tableString := &strings.Builder{}
|
||||||
|
table := tablewriter.NewWriter(tableString)
|
||||||
|
table.SetHeader([]string{"Status", "Application ID"})
|
||||||
|
|
||||||
|
if resp.StatusCode == 404 || resp.StatusCode == 400 || resp.StatusCode == 401 {
|
||||||
|
dataErr := [][]string{
|
||||||
|
{aurora.Yellow("PRE-DECLINED").String(), "N/A"},
|
||||||
|
}
|
||||||
|
table.AppendBulk(dataErr)
|
||||||
|
table.Render()
|
||||||
|
fmt.Println(tableString.String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var response Response
|
||||||
|
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
|
||||||
|
internal.Error(err, "Unable to parse JSON from CSD.")
|
||||||
|
}
|
||||||
|
|
||||||
|
data := [][]string{
|
||||||
|
{response.Decision, response.ApplicationID},
|
||||||
|
}
|
||||||
|
table.AppendBulk(data)
|
||||||
|
table.Render()
|
||||||
|
fmt.Println(tableString.String())
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FetchHMACKey is a configuration function that fetches the system HMAC key to interact with CSD
|
||||||
|
func FetchHMACKey() []byte {
|
||||||
|
read, err := ioutil.ReadFile("/etc/cscli.conf")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return []byte(strings.TrimSpace(string(read)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchConfigKey is a configuration function that fetches the ConfigCat SDK key
|
||||||
|
func FetchConfigKey() string {
|
||||||
|
read, err := ioutil.ReadFile("/etc/cscli-sdk.conf")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(read))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchIPKey is a configuration function that fetches the API key for IP address information
|
||||||
|
func FetchIPKey() string {
|
||||||
|
read, err := ioutil.ReadFile("/etc/cscli-ip.conf")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(read))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchVendorKey is a configuration function that fetches the Vendor key for Community Report data
|
||||||
|
func FetchVendorKey() string {
|
||||||
|
read, err := ioutil.ReadFile("/etc/cscli-vend.conf")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(read))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchInternalKey is a configuration function that fetches the inter-process internal communication key
|
||||||
|
func FetchInternalKey() string {
|
||||||
|
read, err := ioutil.ReadFile("/etc/csctl-internal.conf")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(read))
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
module csctl
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/kataras/golog v0.1.6
|
||||||
|
github.com/logrusorgru/aurora/v3 v3.0.0
|
||||||
|
github.com/olekukonko/tablewriter v0.0.4
|
||||||
|
github.com/urfave/cli/v2 v2.3.0
|
||||||
|
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742 // indirect
|
||||||
|
gopkg.in/hlandau/service.v1 v1.0.7
|
||||||
|
)
|
|
@ -0,0 +1,28 @@
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/kataras/golog v0.1.6 h1:jEqEQCm+4B4M4/CgzrEWNj9iUST0hGZXDqAPMVnxWMw=
|
||||||
|
github.com/kataras/golog v0.1.6/go.mod h1:jOSQ+C5fUqsNSwurB/oAHq1IFSb0KI3l6GMa7xB6dZA=
|
||||||
|
github.com/kataras/pio v0.0.10 h1:b0qtPUqOpM2O+bqa5wr2O6dN4cQNwSmFd6HQqgVae0g=
|
||||||
|
github.com/kataras/pio v0.0.10/go.mod h1:gS3ui9xSD+lAUpbYnjOGiQyY7sUMJO+EHpiRzhtZ5no=
|
||||||
|
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||||
|
github.com/logrusorgru/aurora/v3 v3.0.0 h1:R6zcoZZbvVcGMvDCKo45A9U/lzYyzl5NfYIvznmDfE4=
|
||||||
|
github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc=
|
||||||
|
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||||
|
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
|
||||||
|
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||||
|
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
|
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742 h1:+CBz4km/0KPU3RGTwARGh/noP3bEwtHcq+0YcBQM2JQ=
|
||||||
|
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/hlandau/service.v1 v1.0.7 h1:16G5AJ1Cp8Vr65QItJXpyAIzf/FWAWCZBsTgsc6eyA8=
|
||||||
|
gopkg.in/hlandau/service.v1 v1.0.7/go.mod h1:sZw6ksxcoafC04GoZtw32UeqqEuPSABX35lVBaJP/bE=
|
||||||
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
@ -0,0 +1,29 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kataras/golog"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Error(err error, msg string) {
|
||||||
|
currentUser, _ := user.Current()
|
||||||
|
path := fmt.Sprintf("%s/.csctl.err.log", currentUser.HomeDir)
|
||||||
|
file, errf := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
|
||||||
|
if errf != nil {
|
||||||
|
golog.Error("An error has occurred while handling another error. Please alert a technician immediately.")
|
||||||
|
fmt.Printf("==ERROR==\nOccurred at: %s || Message: %s\n\n%s\n\n\n", time.Now().String(), "nil", errf.Error())
|
||||||
|
panic(errf)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
contents := fmt.Sprintf("==ERROR==\nOccurred at: %s || Message: %s\n\n%s\n\n\n", time.Now().String(), msg, err.Error())
|
||||||
|
_, errf = file.WriteString(contents)
|
||||||
|
if errf != nil {
|
||||||
|
golog.Error("An error has occurred while handling another error. Please alert a technician immediately.")
|
||||||
|
fmt.Printf("==ERROR==\nOccurred at: %s || Message: %s\n\n%s\n\n\n", time.Now().String(), "nil", errf.Error())
|
||||||
|
panic(errf)
|
||||||
|
}
|
||||||
|
golog.Fatal("An error has occurred while processing the executable. A log file has been created in %s, please alert a Technician and provide the contents of this file if needed.\n", path)
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"csctl/config"
|
||||||
|
|
||||||
|
// "csctl/config"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TCPData is a struct of information to be sent over to the CS daemon
|
||||||
|
type TCPData struct {
|
||||||
|
Username, Type, Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account struct for user account information
|
||||||
|
type Account struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
UserID string `json:"userID"`
|
||||||
|
EmailAddress string `json:"emailAddress"`
|
||||||
|
CreatedBy string `json:"createdBy"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
Locked bool `json:"locked"`
|
||||||
|
Tier int `json:"tier"`
|
||||||
|
SupportKey string `json:"supportKey"`
|
||||||
|
ReferralCode string `json:"referralCode"`
|
||||||
|
TotalReferrals int `json:"totalReferrals"`
|
||||||
|
Root bool `json:"root"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchAccount is a helper function that fetches account information from CSD
|
||||||
|
func FetchAccount(username string) (*Account, error) {
|
||||||
|
data, err := ContactSR(&TCPData{
|
||||||
|
Username: username,
|
||||||
|
Type: "userinfo",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var account Account
|
||||||
|
err = json.Unmarshal([]byte(data), &account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &account, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contact one way contact function, sends message to CSD. Doesn't expect response back.
|
||||||
|
func Contact(data *TCPData) error {
|
||||||
|
conn, err := net.Dial("tcp", "localhost:8124")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newHmac := hmac.New(sha256.New, config.FetchHMACKey())
|
||||||
|
jsonMarshal, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newHmac.Write(jsonMarshal)
|
||||||
|
encoded := hex.EncodeToString(newHmac.Sum(nil))
|
||||||
|
write := string(jsonMarshal) + "$" + encoded
|
||||||
|
conn.Write([]byte(write))
|
||||||
|
conn.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContactSR two-way communication with CSD
|
||||||
|
func ContactSR(data *TCPData) (string, error) {
|
||||||
|
conn, err := net.Dial("tcp", "localhost:8124")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
newHmac := hmac.New(sha256.New, config.FetchHMACKey())
|
||||||
|
jsonMarshal, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
newHmac.Write(jsonMarshal)
|
||||||
|
encoded := hex.EncodeToString(newHmac.Sum(nil))
|
||||||
|
write := string(jsonMarshal) + "$" + encoded
|
||||||
|
conn.Write([]byte(write))
|
||||||
|
message, err := bufio.NewReader(conn).ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
return strings.ReplaceAll(message, "\n", ""), nil
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"csctl/cmd"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"gopkg.in/hlandau/service.v1/daemon/setuid"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := setuid.Setresgid(115, 115, 115)
|
||||||
|
|
||||||
|
app := &cli.App{
|
||||||
|
Authors: []*cli.Author{
|
||||||
|
{Name: "Matthew R", Email: "matthew@staff.libraryofcode.org"},
|
||||||
|
},
|
||||||
|
Copyright: "Copyright (c) 2020 Library of Code sp-us | Board of Directors",
|
||||||
|
Description: "Command line interface containing various functions relating to your CS Account.",
|
||||||
|
Usage: "Cloud Services Account Control (csctl)",
|
||||||
|
|
||||||
|
Commands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "apply",
|
||||||
|
Usage: "",
|
||||||
|
Description: "Submits various applications to EDS for a decision.",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "p-t2",
|
||||||
|
Description: "Application for Tier 2 pre-approval.",
|
||||||
|
Action: cmd.ApplySoft,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "t2",
|
||||||
|
Description: "Application for Tier 2.",
|
||||||
|
Action: cmd.ApplyHard,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app.Run(os.Args)
|
||||||
|
}
|
Loading…
Reference in New Issue