mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-09-23 00:16:54 +00:00
128 lines
3.0 KiB
Go
128 lines
3.0 KiB
Go
package bitrix
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
func (c *Client) LoadCookieString(cookieStr string) error {
|
|
cookieStr = strings.TrimSpace(cookieStr)
|
|
if cookieStr == "" {
|
|
return fmt.Errorf("empty cookie string")
|
|
}
|
|
u, err := url.Parse(c.portal)
|
|
if err != nil {
|
|
return fmt.Errorf("parse portal %s: %w", c.portal, err)
|
|
}
|
|
var cookies []*http.Cookie
|
|
for piece := range strings.SplitSeq(cookieStr, ";") {
|
|
piece = strings.TrimSpace(piece)
|
|
if piece == "" {
|
|
continue
|
|
}
|
|
eq := strings.IndexByte(piece, '=')
|
|
if eq <= 0 {
|
|
continue
|
|
}
|
|
cookies = append(cookies, &http.Cookie{Name: piece[:eq], Value: piece[eq+1:], Path: "/"})
|
|
}
|
|
c.HTTP.Jar.SetCookies(u, cookies)
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) EnsureLogin() error {
|
|
if c.checkSession() {
|
|
c.Logger.Debug("[auth] session valid, reusing saved cookies")
|
|
return nil
|
|
}
|
|
if c.email == "" || c.password == "" {
|
|
return fmt.Errorf("ensureLogin: no email/password")
|
|
}
|
|
c.Logger.Debug("[auth] session dead, re-logging in")
|
|
return c.loginWithPassword(c.email, c.password)
|
|
}
|
|
|
|
func (c *Client) checkSession() bool {
|
|
body, final, err := c.getPage(c.portal + "/")
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if strings.Contains(final, "auth2.bitrix24.net") {
|
|
return false
|
|
}
|
|
if m := reSessid.FindStringSubmatch(body); m != nil {
|
|
c.sessid = m[1]
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *Client) withRelogin(do func() ([]byte, int, error)) ([]byte, int, error) {
|
|
body, status, err := do()
|
|
if err != nil {
|
|
return body, status, err
|
|
}
|
|
if token := csrfTokenFromBody(body); token != "" && token != c.sessid {
|
|
c.Logger.Debug("[auth] stale csrf token, retrying with the one from the response")
|
|
c.sessid = token
|
|
body, status, err = do()
|
|
if err != nil {
|
|
return body, status, err
|
|
}
|
|
}
|
|
if !looksUnauth(body, status) {
|
|
return body, status, err
|
|
}
|
|
if c.email == "" || c.password == "" {
|
|
return body, status, err
|
|
}
|
|
c.Logger.Debug(fmt.Sprintf("[auth] unauthenticated response (status %d), re-logging in and retrying", status))
|
|
if lerr := c.loginWithPassword(c.email, c.password); lerr != nil {
|
|
return body, status, fmt.Errorf("relogin: %w", lerr)
|
|
}
|
|
return do()
|
|
}
|
|
|
|
func csrfTokenFromBody(body []byte) string {
|
|
var out struct {
|
|
Errors []struct {
|
|
Code any `json:"code"`
|
|
CustomData json.RawMessage `json:"customData"`
|
|
} `json:"errors"`
|
|
}
|
|
if err := json.Unmarshal(body, &out); err != nil {
|
|
return ""
|
|
}
|
|
for _, e := range out.Errors {
|
|
if e.Code != "invalid_csrf" {
|
|
continue
|
|
}
|
|
var customData struct {
|
|
CSRF string `json:"csrf"`
|
|
}
|
|
if err := json.Unmarshal(e.CustomData, &customData); err != nil {
|
|
continue
|
|
}
|
|
return customData.CSRF
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func looksUnauth(body []byte, status int) bool {
|
|
if status == 401 || status == 403 {
|
|
return true
|
|
}
|
|
s := strings.TrimSpace(string(body))
|
|
if s == "" || s[0] != '{' {
|
|
return true
|
|
}
|
|
low := strings.ToLower(s)
|
|
return strings.Contains(low, "not_authorized") ||
|
|
strings.Contains(low, "unauthorized") ||
|
|
strings.Contains(low, "invalid_token") ||
|
|
strings.Contains(low, "user_not_authorized")
|
|
}
|