Files
sing-box-extended-mirror/transport/call/bitrix/session.go
T

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")
}