292 lines
7.2 KiB
Go
292 lines
7.2 KiB
Go
package supplier
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"strings"
|
|
|
|
"github.com/go-resty/resty/v2"
|
|
"github.com/tidwall/gjson"
|
|
)
|
|
|
|
const setting_AppFrameworkDevelopementMode = "Apps_Framework_Development_Mode"
|
|
const setting_AppFrameworkEnabled = "Apps_Framework_enabled"
|
|
const rocketchat_app_id = "53fd430f-3924-4c6c-8774-b23fabfeb0e5"
|
|
|
|
type rocketchat struct {
|
|
*resty.Client
|
|
spConfig
|
|
ctx context.Context
|
|
sideLoading bool
|
|
previousAppDevMode bool
|
|
}
|
|
|
|
func (rc *rocketchat) StartSession(ctx context.Context) error {
|
|
rc.ctx = ctx
|
|
if err := rc.login(); err != nil {
|
|
return err
|
|
}
|
|
err := rc.setSetting(setting_AppFrameworkEnabled, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if rc.sideLoading {
|
|
appDevMode, err := rc.getSetting(setting_AppFrameworkDevelopementMode)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rc.previousAppDevMode = appDevMode.Bool()
|
|
if !rc.previousAppDevMode {
|
|
rc.setSetting(setting_AppFrameworkDevelopementMode, true)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (rc *rocketchat) CloseSession() error {
|
|
if rc.sideLoading && !rc.previousAppDevMode {
|
|
if err := rc.setSetting(setting_AppFrameworkDevelopementMode, rc.previousAppDevMode); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (rc *rocketchat) InstallOrUpdate() error {
|
|
installedVersion, err := rc.getInstalledVersion()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
version, err := rc.getVersion()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if rc.sideLoading && installedVersion != version {
|
|
uri := "/api/apps"
|
|
if installedVersion != "" {
|
|
uri = uri + "/update"
|
|
}
|
|
if err := rc.download(version); err != nil {
|
|
return err
|
|
}
|
|
resp, err := rc.R().SetFile("app", "scim.zip").Post(uri)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp.IsError() {
|
|
return fmt.Errorf("fail to upload app - %s", resp.Body())
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (rc *rocketchat) Enable() error {
|
|
enabled, err := rc.isEnabled()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !enabled {
|
|
body := map[string]string{
|
|
"status": "manually_enabled",
|
|
}
|
|
resp, err := rc.R().SetBody(body).Post(fmt.Sprintf("/api/apps/%s/status", rocketchat_app_id))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp.IsError() {
|
|
return fmt.Errorf("fail to enable app - %s", resp.Body())
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (rc *rocketchat) GenerateCredentials() (string, string, error) {
|
|
username, err := rc.userId()
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
exists, err := rc.accessTokenExists()
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
var token string
|
|
if exists {
|
|
token, err = rc.regenerateAccessToken()
|
|
} else {
|
|
token, err = rc.generateAccessToken()
|
|
}
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
return username, token, nil
|
|
}
|
|
|
|
func (rc *rocketchat) ScimEndpoint() string {
|
|
return fmt.Sprintf("%s/api/apps/public/53fd430f-3924-4c6c-8774-b23fabfeb0e5/", rc.Host)
|
|
}
|
|
|
|
func (rc *rocketchat) Name() string {
|
|
if rc.spConfig.Name == "" {
|
|
return "rocketchat"
|
|
}
|
|
return rc.spConfig.Name
|
|
}
|
|
|
|
func (rc *rocketchat) login() error {
|
|
body := map[string]string{
|
|
"user": rc.Username,
|
|
"password": rc.Password,
|
|
}
|
|
resp, err := rc.R().SetBody(body).Post("/api/v1/login")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp.IsError() {
|
|
return errors.New(string(resp.Body()))
|
|
}
|
|
data := gjson.GetMany(string(resp.Body()), "data.userId", "data.authToken")
|
|
hashedPass := sha256.Sum256([]byte(rc.Password))
|
|
rc.Client = rc.Client.
|
|
SetHeader("X-User-Id", data[0].String()).
|
|
SetHeader("X-Auth-Token", data[1].String()).
|
|
SetHeader("X-2fa-code", hex.EncodeToString(hashedPass[:])).
|
|
SetHeader("X-2fa-method", "password")
|
|
|
|
return nil
|
|
}
|
|
|
|
func (rc *rocketchat) getSetting(key string) (gjson.Result, error) {
|
|
resp, err := rc.R().Get(fmt.Sprintf("/api/v1/settings/%s", key))
|
|
if err != nil {
|
|
return gjson.Result{}, err
|
|
}
|
|
if resp.IsError() {
|
|
return gjson.Result{}, fmt.Errorf("fail to get setting %s - %s", key, resp.Body())
|
|
}
|
|
return gjson.Get(string(resp.Body()), "value"), nil
|
|
}
|
|
|
|
func (rc *rocketchat) setSetting(key string, value interface{}) error {
|
|
body := map[string]interface{}{}
|
|
body["value"] = value
|
|
resp, err := rc.R().
|
|
SetContext(rc.ctx).
|
|
SetBody(body).
|
|
Post(fmt.Sprintf("/api/v1/settings/%s", key))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp.IsError() {
|
|
return fmt.Errorf("fail to set setting %s - %s", key, resp.Body())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (rc *rocketchat) getVersion() (string, error) {
|
|
if rc.Version != "" {
|
|
return rc.Version, nil
|
|
}
|
|
resp, err := resty.New().R().SetContext(rc.ctx).Get("https://lab.libreho.st/api/v4/projects/207/releases")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if resp.IsError() {
|
|
return "", fmt.Errorf("fail to get releases - %s", resp.Body())
|
|
}
|
|
rc.Version = gjson.Get(string(resp.Body()), "0.tag_name").String()
|
|
return rc.Version, nil
|
|
}
|
|
|
|
func (rc *rocketchat) download(version string) error {
|
|
resp, err := resty.New().R().SetContext(rc.ctx).Get(fmt.Sprintf("https://lab.libreho.st/api/v4/projects/207/packages/generic/scim/%s/scim.zip", version))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp.IsError() {
|
|
return fmt.Errorf("fail to download package - %s", resp.Body())
|
|
}
|
|
rc.Version = gjson.Get(string(resp.Body()), "0.tag_name").String()
|
|
return ioutil.WriteFile("scim.zip", resp.Body(), 0644)
|
|
}
|
|
|
|
func (rc *rocketchat) getAppInfo() (string, error) {
|
|
resp, err := rc.R().Get(fmt.Sprintf("/api/apps/%s", rocketchat_app_id))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(resp.Body()), nil
|
|
}
|
|
|
|
func (rc *rocketchat) getInstalledVersion() (string, error) {
|
|
raw, err := rc.getAppInfo()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return gjson.Get(raw, "app.version").String(), nil
|
|
}
|
|
|
|
func (rc *rocketchat) isEnabled() (bool, error) {
|
|
raw, err := rc.getAppInfo()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
status := gjson.Get(raw, "app.status").String()
|
|
return strings.Contains(status, "enabled"), nil
|
|
}
|
|
|
|
func (rc *rocketchat) accessTokenExists() (bool, error) {
|
|
resp, err := rc.R().Get("/api/v1/users.getPersonalAccessTokens")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if resp.IsError() {
|
|
return false, fmt.Errorf("failed to get access tokens - %s", resp.Body())
|
|
}
|
|
return gjson.Get(string(resp.Body()), `tokens.#(name=="scim")`).Exists(), nil
|
|
}
|
|
|
|
func (rc *rocketchat) regenerateAccessToken() (string, error) {
|
|
body := map[string]string{"tokenName": "scim"}
|
|
resp, err := rc.R().SetBody(body).Post("/api/v1/users.removePersonalAccessToken")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if resp.IsError() {
|
|
return "", fmt.Errorf("failed to remove access token - %s", resp.Body())
|
|
}
|
|
return rc.generateAccessToken()
|
|
}
|
|
|
|
func (rc *rocketchat) generateAccessToken() (string, error) {
|
|
body := map[string]interface{}{
|
|
"tokenName": "scim",
|
|
"bypassTwoFactor": true,
|
|
}
|
|
resp, err := rc.R().SetBody(body).Post("/api/v1/users.generatePersonalAccessToken")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if resp.IsError() {
|
|
return "", fmt.Errorf("failed to generate access token - %s", resp.Body())
|
|
}
|
|
return gjson.Get(string(resp.Body()), "token").String(), nil
|
|
}
|
|
|
|
func (rc *rocketchat) R() *resty.Request {
|
|
return rc.Client.R().SetContext(rc.ctx)
|
|
}
|
|
|
|
func (rc *rocketchat) userId() (string, error) {
|
|
resp, err := rc.R().Get("/api/v1/me")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if resp.IsError() {
|
|
return "", fmt.Errorf("failed to get user - %s", resp.Body())
|
|
}
|
|
return gjson.Get(string(resp.Body()), "_id").String(), nil
|
|
}
|