mirror of
https://gitgud.io/yats/libkiwi.git
synced 2026-06-18 00:55:23 -04:00
140 lines
3.0 KiB
Go
140 lines
3.0 KiB
Go
package libkiwi
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
type Credentials struct {
|
|
Username string
|
|
Password string
|
|
Remember bool
|
|
}
|
|
|
|
func loginForm(xfToken string, creds Credentials) url.Values {
|
|
rem := []byte{'0'}
|
|
if creds.Remember {
|
|
rem[0] = '1'
|
|
}
|
|
|
|
return url.Values{
|
|
"_xfToken": {xfToken},
|
|
"login": {creds.Username},
|
|
"password": {creds.Password},
|
|
"remember": {string(rem)},
|
|
}
|
|
}
|
|
|
|
func (kf *KF) newLoginRequest(ctx context.Context, creds Credentials) (*http.Request, error) {
|
|
u := kf.urlFromPath("login")
|
|
|
|
resp, err := kf.Get(ctx, u)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
xfToken, err := XFToken(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
form := loginForm(xfToken, creds)
|
|
|
|
// i.e. https://kiwifarms.net/login/login
|
|
reqURL := fmt.Sprintf("%s/login", u.String())
|
|
req, err := http.NewRequestWithContext(ctx, "POST", reqURL, strings.NewReader(form.Encode()))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
return req, nil
|
|
}
|
|
|
|
func (kf *KF) Login(ctx context.Context, creds Credentials) (*http.Response, error) {
|
|
req, err := kf.newLoginRequest(ctx, creds)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp, err := kf.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
// TODO
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func twoFactorForm(xfToken string, code uint32, u *url.URL) url.Values {
|
|
return url.Values{
|
|
"code": {fmt.Sprint(code)},
|
|
"provider": {"totp"},
|
|
"trust": {"1"},
|
|
"confirm": {"1"},
|
|
"remember": {"1"},
|
|
"_xfRedirect": {fmt.Sprintf("https://%s/", u.Hostname())},
|
|
"_xfResponseType": {"json"},
|
|
"_xfToken": {xfToken},
|
|
"_xfWithData": {"1"},
|
|
"_xfRequestUri": {u.RequestURI()},
|
|
}
|
|
}
|
|
|
|
func (kf *KF) newTwoFactorRequest(ctx context.Context, resp *http.Response, code uint32) (*http.Request, error) {
|
|
u := resp.Request.URL
|
|
if !strings.Contains(u.Path, "two-step") {
|
|
// TODO: move err to proper type with url included.
|
|
return nil, errors.New("Did not redirect to two-step page.")
|
|
}
|
|
|
|
xfToken, err := XFToken(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
form := twoFactorForm(xfToken, code, u)
|
|
reqURL := fmt.Sprintf("https://%s/login/two-step", u.Hostname())
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", reqURL, strings.NewReader(form.Encode()))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
return req, nil
|
|
}
|
|
|
|
func (kf *KF) TwoFactorAuth(ctx context.Context, resp *http.Response, code uint32) (*http.Response, error) {
|
|
req, err := kf.newTwoFactorRequest(ctx, resp, code)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return kf.Do(req)
|
|
}
|
|
|
|
func (kf *KF) RefreshSession(ctx context.Context) error {
|
|
// Clear any existing session token to request a new one.
|
|
setCookie(kf.domain, kf.client.Jar, &http.Cookie{
|
|
Name: "xf_session",
|
|
Value: "",
|
|
})
|
|
|
|
resp, err := kf.Get(ctx, kf.domain)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
}
|