Skip to content

Commit

Permalink
aesthetical fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mazzz1y committed Dec 24, 2024
1 parent 03a3892 commit b5bf9eb
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 98 deletions.
2 changes: 1 addition & 1 deletion internal/device/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func NewDeviceManager(cfg []config.DeviceConfig, auth bool) (*DeviceManager, err
for _, cfgDevice := range cfg {
users, err := initClients(cfgDevice, auth)
if err != nil {
return nil, err
return nil, fmt.Errorf("%s: %v", cfgDevice.URL, err)
}

deviceManager.Devices[cfgDevice.Tag] = Device{
Expand Down
9 changes: 5 additions & 4 deletions internal/entrypoint/entrypoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,19 @@ func (e *Entrypoint) handleRequest(w http.ResponseWriter, r *http.Request) {
}

func (e *Entrypoint) handleProxyRequest(w http.ResponseWriter, r *http.Request, c device.ClientWrapper) {
uri := r.URL.RequestURI()
proxyBody, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
e.log.Error().Err(err).Str("uri", r.URL.RequestURI()).Msg("failed to read request body")
e.log.Error().Err(err).Str("uri", uri).Msg("failed to read request body")
return
}
defer r.Body.Close()

resp, err := c.RequestWithAuth(r.Method, r.URL.RequestURI(), string(proxyBody))
resp, err := c.RequestWithAuth(r.Method, uri, string(proxyBody))
if err != nil {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
e.log.Error().Err(err).Str("uri", r.URL.RequestURI()).Msg("request to backend failed")
e.log.Error().Err(err).Str("uri", uri).Msg("request to backend failed")
return
}
defer resp.Body.Close()
Expand All @@ -84,7 +85,7 @@ func (e *Entrypoint) handleProxyRequest(w http.ResponseWriter, r *http.Request,
w.WriteHeader(resp.StatusCode)

if _, err := io.Copy(w, resp.Body); err != nil {
e.log.Error().Err(err).Str("uri", r.URL.RequestURI()).Msg("failed to write response body")
e.log.Error().Err(err).Str("uri", uri).Msg("failed to write response body")
return
}
}
Expand Down
160 changes: 78 additions & 82 deletions pkg/glinet/glinet.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

type Client struct {
URL string
RPCUrl string
Username string
Password string
Client *http.Client
Expand All @@ -27,9 +28,10 @@ type Client struct {

func NewClient(baseUrl, proxyURL, username, password string) *Client {
jar, _ := cookiejar.New(nil)

url := strings.TrimRight(baseUrl, "/")
return &Client{
URL: strings.TrimRight(baseUrl, "/"),
RPCUrl: url + "/rpc",
Username: username,
Password: password,
Client: &http.Client{
Expand All @@ -46,10 +48,9 @@ func (kc *Client) Auth() error {
return err
}

loginHash := kc.generateLoginHash(salt, nonce)
authPayload := kc.buildAuthPayload(loginHash)
authPayload := buildAuthPayload(kc.Username, kc.Password, salt, nonce)

res, err := kc.request("POST", kc.URL+"/rpc", authPayload)
res, err := kc.request("POST", kc.RPCUrl, authPayload)
if err != nil {
return err
}
Expand All @@ -59,16 +60,12 @@ func (kc *Client) Auth() error {
return errors.New("auth failed")
}

return kc.extractSessionID(res)
return kc.extractSid(res)
}

func (kc *Client) RequestWithAuth(method, endpoint, body string) (*http.Response, error) {
urlParsed, err := kc.prepareURL(endpoint)
if err != nil {
return nil, err
}

res, err := kc.request(method, urlParsed.String(), body)
func (kc *Client) RequestWithAuth(method, path, body string) (*http.Response, error) {
url := kc.URL + path
res, err := kc.request(method, url, body)
if err != nil {
return nil, err
}
Expand All @@ -77,88 +74,43 @@ func (kc *Client) RequestWithAuth(method, endpoint, body string) (*http.Response
if err = kc.Auth(); err != nil {
return nil, err
}
return kc.request(method, urlParsed.String(), body)
return kc.request(method, url, body)
}

cleanResponseHeaders(res)
return res, nil
}

func (kc *Client) getSaltAndNonce() (string, string, error) {
payload := kc.buildChallengePayload()
response, err := kc.request("POST", kc.URL+"/rpc", payload)
if err != nil {
return "", "", err
func (kc *Client) request(method, url, body string) (*http.Response, error) {
if url == kc.RPCUrl {
body = kc.replaceSid(body)
}
defer response.Body.Close()

return kc.parseSaltAndNonce(response)
}

func (kc *Client) generateLoginHash(salt, nonce string) string {
hashValue := kc.generateHash(salt, kc.Password)
loginData := fmt.Sprintf("root:%s:%s", hashValue, nonce)
hash := md5.Sum([]byte(loginData))
return hex.EncodeToString(hash[:])
}

func (kc *Client) generateHash(salt, pass string) string {
return password.MD5.Crypt([]byte(pass), []byte(salt), nil)
}

func (kc *Client) request(method, urlStr, body string) (*http.Response, error) {
if strings.Contains(urlStr, "/rpc") {
body = kc.updateRequestBodyWithSessionID(body)
}

req, err := http.NewRequest(method, urlStr, bytes.NewBuffer([]byte(body)))
req, err := http.NewRequest(method, url, bytes.NewBuffer([]byte(body)))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")

resp, err := kc.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to make request: %v", err)
}

return resp, nil
}

func (kc *Client) updateRequestBodyWithSessionID(body string) string {
var payload map[string]interface{}
if err := json.Unmarshal([]byte(body), &payload); err == nil {
if paramsArray, ok := payload["params"].([]interface{}); ok && len(paramsArray) > 0 {
paramsArray[0] = kc.SessionID
payload["params"] = paramsArray
} else if paramsMap, ok := payload["params"].(map[string]interface{}); ok {
if _, ok := paramsMap["sid"]; ok {
paramsMap["sid"] = kc.SessionID
payload["params"] = paramsMap
}
}

if updatedBody, err := json.Marshal(payload); err == nil {
return string(updatedBody)
}
func (kc *Client) getSaltAndNonce() (string, string, error) {
payload := buildChallengePayload(kc.Username)
response, err := kc.request("POST", kc.RPCUrl, payload)
if err != nil {
return "", "", err
}
return body
}
defer response.Body.Close()

func (kc *Client) buildAuthPayload(loginHash string) string {
authPayload := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"method": "login",
"params": map[string]string{
"username": kc.Username,
"hash": loginHash,
},
}
payloadBytes, _ := json.Marshal(authPayload)
return string(payloadBytes)
return parseSaltAndNonce(response)
}

func (kc *Client) extractSessionID(res *http.Response) error {
func (kc *Client) extractSid(res *http.Response) error {
var response map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
return err
Expand All @@ -173,16 +125,27 @@ func (kc *Client) extractSessionID(res *http.Response) error {
return nil
}

func (kc *Client) prepareURL(endpoint string) (*url.URL, error) {
endpoint = strings.TrimLeft(endpoint, "/")
return url.Parse(kc.URL + "/" + endpoint)
}
func (kc *Client) replaceSid(body string) string {
var payload map[string]interface{}
if err := json.Unmarshal([]byte(body), &payload); err == nil {
if paramsArray, ok := payload["params"].([]interface{}); ok && len(paramsArray) > 0 {
paramsArray[0] = kc.SessionID
payload["params"] = paramsArray
} else if paramsMap, ok := payload["params"].(map[string]interface{}); ok {
if _, ok := paramsMap["sid"]; ok {
paramsMap["sid"] = kc.SessionID
payload["params"] = paramsMap
}
}

func (kc *Client) buildChallengePayload() string {
return fmt.Sprintf(`{"jsonrpc":"2.0","id":1,"method":"challenge","params":{"username":"%s"}}`, kc.Username)
if updatedBody, err := json.Marshal(payload); err == nil {
return string(updatedBody)
}
}
return body
}

func (kc *Client) parseSaltAndNonce(response *http.Response) (string, string, error) {
func parseSaltAndNonce(response *http.Response) (string, string, error) {
var result map[string]interface{}
json.NewDecoder(response.Body).Decode(&result)
res, ok := result["result"].(map[string]interface{})
Expand All @@ -195,13 +158,40 @@ func (kc *Client) parseSaltAndNonce(response *http.Response) (string, string, er
if !saltOk || !nonceOk {
return "", "", errors.New("missing salt or nonce in response")
}

return salt, nonce, nil
}

func cleanResponseHeaders(res *http.Response) {
res.Header.Del("Set-Cookie")
// This is required by the frontend. Otherwise, it will loop until the cookie is set.
res.Header.Set("Set-Cookie", "Admin-Token=1337")
func buildChallengePayload(user string) string {
authPayload := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"method": "challenge",
"params": map[string]string{
"username": user,
},
}
payloadBytes, _ := json.Marshal(authPayload)
return string(payloadBytes)
}

func buildAuthPayload(user, pass, salt, nonce string) string {
passwd := password.MD5.Crypt([]byte(pass), []byte(salt), nil)
loginData := fmt.Sprintf("%s:%s:%s", user, passwd, nonce)
hash := md5.Sum([]byte(loginData))
loginHash := hex.EncodeToString(hash[:])

authPayload := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"method": "login",
"params": map[string]string{
"username": user,
"hash": loginHash,
},
}
payloadBytes, _ := json.Marshal(authPayload)
return string(payloadBytes)
}

func isAccessDenied(res *http.Response) bool {
Expand Down Expand Up @@ -237,3 +227,9 @@ func createTransport(proxyURL string) *http.Transport {

return &http.Transport{Proxy: proxyFunc}
}

func cleanResponseHeaders(res *http.Response) {
res.Header.Del("Set-Cookie")
// This is required by the frontend. Otherwise, it will loop until the cookie is set.
res.Header.Set("Set-Cookie", "Admin-Token=1337")
}
2 changes: 1 addition & 1 deletion pkg/glinet/glinet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

const (
mockUser = "username"
mockUser = "root"
mockPass = "password"
mockSession = "mock-session-id"
mockSalt = "mock-salt"
Expand Down
19 changes: 9 additions & 10 deletions pkg/keenetic/keenetic.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,8 @@ func (kc *Client) Auth() error {
return err
}

passwordHash := kc.generatePasswordHash(challenge, realm)
authPayload := map[string]string{"login": kc.Username, "password": passwordHash}

payloadBytes, _ := json.Marshal(authPayload)

res, err := kc.request("POST", kc.URL+"/auth", string(payloadBytes))
payload := buildAuthPayload(kc.Username, kc.Password, challenge, realm)
res, err := kc.request("POST", kc.URL+"/auth", payload)
if err != nil {
return err
}
Expand Down Expand Up @@ -126,12 +122,15 @@ func (kc *Client) request(method, url, body string) (*http.Response, error) {
return resp, nil
}

func (kc *Client) generatePasswordHash(challenge, realm string) string {
md5Hash := md5.Sum([]byte(fmt.Sprintf("%s:%s:%s", kc.Username, realm, kc.Password)))
func buildAuthPayload(user, pass, challenge, realm string) string {
md5Hash := md5.Sum([]byte(fmt.Sprintf("%s:%s:%s", user, realm, pass)))
md5Hex := hex.EncodeToString(md5Hash[:])

shaHash := sha256.Sum256([]byte(challenge + md5Hex))
return hex.EncodeToString(shaHash[:])
passwordHash := hex.EncodeToString(shaHash[:])

authPayload := map[string]string{"login": user, "password": passwordHash}
payload, _ := json.Marshal(authPayload)
return string(payload)
}

func createTransport(proxyURL string) *http.Transport {
Expand Down

0 comments on commit b5bf9eb

Please sign in to comment.