package lsf import ( "fmt" "io/ioutil" "net/http" "net/url" "regexp" "strings" "github.com/pkg/errors" ) // Error variables var ( ErrInvalSID error = errors.New("invalid session id") ) // Session contains the session id and whatever this obscure asi token is. type Session struct { SID string // ASI is a string you somehow have to pass to some endpoints. ASI string } // Valid checks if the session id of the session is valid func (s *Session) Valid() (bool, error) { client := &http.Client{} // GET the logged in mail page. // If correctly logged in there is a logout button. // Otherwise there is a login button. req, err := http.NewRequest("GET", "https://lsf.hs-worms.de/qisserver/rds?state=user&type=8&topitem=functions&breadCrumbSource=portal", nil) if err != nil { return false, errors.Wrap(err, "could not prepare the request") } req.Header.Add("Cookie", fmt.Sprintf("JSESSIONID=%s", s.SID)) resp, err := client.Do(req) if err != nil { return false, errors.Wrap(err, "could not do the request") } b, err := ioutil.ReadAll(resp.Body) if err != nil { return false, errors.Wrap(err, "could not read the response body") } if strings.Contains(string(b), "Logout") { return true, nil } if strings.Contains(string(b), "Login") { return false, nil } return false, errors.New("unexpected response body") } // Login tries to login with the given username and password. // If successful a new Session with the session id and asi // will be created and returned. func Login(username, password string) (*Session, error) { client := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { // don't follow redirects return http.ErrUseLastResponse }, } form := url.Values{} form.Add("asdf", username) // who wrote this backend? lol form.Add("fdsa", password) form.Add("submit", "Login") req, err := http.NewRequest("POST", "https://lsf.hs-worms.de/qisserver/rds?state=user&type=1&category=auth.login&startpage=portal.vm&breadCrumbSource=portal", strings.NewReader(form.Encode())) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") if err != nil { return nil, errors.Wrap(err, "could not prepare the login request") } resp, err := client.Do(req) if err != nil { return nil, errors.Wrap(err, "could not do the login request") } if resp.StatusCode == 302 { for _, c := range resp.Cookies() { if c.Name == "JSESSIONID" { asi, err := asi(c.Value) if err != nil { return nil, errors.Wrap(err, "could not get asi") } return &Session{ SID: c.Value, ASI: asi, }, nil } } return nil, errors.New("no session cookie found") } if resp.StatusCode == 200 { return nil, errors.New("wrong credentials") // TODO or other errors } return nil, errors.New("unexpected response status code") } // NewSessionBySID checks whether the given session id is valid and if so // a new Session with the session id and asi will be created and returned. func NewSessionBySID(sid string) (*Session, error) { s := &Session{ SID: sid, } valid, err := s.Valid() if err != nil { return nil, errors.Wrap(err, "could not check session") } if !valid { return nil, ErrInvalSID } asi, err := asi(sid) if err != nil { return nil, errors.Wrap(err, "could not get asi") } return &Session{ SID: sid, ASI: asi, }, nil } func asi(sid string) (string, error) { client := &http.Client{} // GET Request with JESSIONID cookie to sitemap endpoint req, err := http.NewRequest("GET", "https://lsf.hs-worms.de/qisserver/rds?state=sitemap&topitem=leer&breadCrumbSource=portal", nil) if err != nil { return "", errors.Wrap(err, "could not prepare the request") } req.Header.Add("Cookie", fmt.Sprintf("JSESSIONID=%s", sid)) resp, err := client.Do(req) if err != nil { return "", errors.Wrap(err, "could not do the request") } b, err := ioutil.ReadAll(resp.Body) if err != nil { return "", errors.Wrap(err, "could not read the response body") } // The asi is in some links as GET parameter. // Filter it with regexp re := regexp.MustCompile(`asi=([^"]+)`) match := re.FindSubmatch(b) if len(match) != 2 { return "", errors.New("no asi found") } return string(match[1]), nil }