diff --git a/controllers/account.go b/controllers/account.go index 4e75ab2..135cac5 100644 --- a/controllers/account.go +++ b/controllers/account.go @@ -63,7 +63,7 @@ func (c *ApiController) Signin() { func (c *ApiController) Signout() { claims := c.GetSessionClaims() if claims != nil { - clearUserDuplicated(claims) + DeleteSession(claims.Name) } c.SetSessionClaims(nil) @@ -77,12 +77,13 @@ func (c *ApiController) GetAccount() { } claims := c.GetSessionClaims() - - if isUserDuplicated(claims) { + if IsSessionDuplicated(claims.Name, c.Ctx.Input.CruSession.SessionID()) { if !claims.IsAdmin { c.ResponseError("you have signed in from another place, this session has been ended") return } + } else { + AddSession(claims.Name, c.Ctx.Input.CruSession.SessionID()) } c.ResponseOk(claims) diff --git a/controllers/session.go b/controllers/session.go index 12117b7..32bb1fe 100644 --- a/controllers/session.go +++ b/controllers/session.go @@ -15,31 +15,232 @@ package controllers import ( + "bytes" + "encoding/json" "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "github.com/astaxie/beego" "github.com/casdoor/casdoor-go-sdk/auth" ) -var sessionMap = map[string]int64{} +type Session struct { + Owner string `xorm:"varchar(100) notnull pk" json:"owner"` + Name string `xorm:"varchar(100) notnull pk" json:"name"` + Application string `xorm:"varchar(100) notnull pk" json:"application"` + CreatedTime string `xorm:"varchar(100)" json:"createdTime"` -func clearUserDuplicated(claims *auth.Claims) { - userId := fmt.Sprintf("%s/%s", claims.Owner, claims.Name) - delete(sessionMap, userId) + SessionId []string `json:"sessionId"` } -func isUserDuplicated(claims *auth.Claims) bool { - userId := fmt.Sprintf("%s/%s", claims.Owner, claims.Name) - unixTimestamp := claims.IssuedAt.Unix() +func GetSessions() ([]*Session, error) { + queryMap := map[string]string{ + "owner": getOrg(), + } + + url := auth.GetUrl("get-sessions", queryMap) + + bytes, err := auth.DoGetBytesRaw(url) + if err != nil { + return nil, err + } + + var sessions []*Session + err = json.Unmarshal(bytes, &sessions) + if err != nil { + return nil, err + } + return sessions, nil +} + +func GetSession(userName string) (*Session, error) { + queryMap := map[string]string{ + "sessionPkId": fmt.Sprintf("%s/%s/%s", getOrg(), userName, getApp()), + } + + url := auth.GetUrl("get-session", queryMap) + + bytes, err := auth.DoGetBytesRaw(url) + if err != nil { + return nil, err + } + + var session *Session + err = json.Unmarshal(bytes, &session) + if err != nil { + return nil, err + } + return session, nil +} + +func UpdateSession(userName string, sessionId string) (bool, error) { + session := &Session{ + Owner: getOrg(), + Name: userName, + Application: getApp(), + SessionId: []string{sessionId}, + } + + postBytes, _ := json.Marshal(session) + + resp, err := doPost("update-session", nil, postBytes, false) + + if err != nil { + return false, err + } + + return resp.Data == "Affected", nil +} + +func AddSession(userName string, sessionId string) (bool, error) { + session := &Session{ + Owner: getOrg(), + Name: userName, + Application: getApp(), + SessionId: []string{sessionId}, + } + + postBytes, _ := json.Marshal(session) + + resp, err := doPost("add-session", nil, postBytes, false) + + if err != nil { + return false, err + } + + return resp.Data == "Affected", nil +} + +func DeleteSession(userName string) (bool, error) { + session := &Session{ + Owner: getOrg(), + Name: userName, + Application: getApp(), + } + + postBytes, _ := json.Marshal(session) + + resp, err := doPost("delete-session", nil, postBytes, false) + + if err != nil { + return false, err + } + + return resp.Data == "Affected", nil +} - sessionUnixTimestamp, ok := sessionMap[userId] - if !ok { - sessionMap[userId] = unixTimestamp - return false +func IsSessionDuplicated(userName string, sessionId string) bool { + queryMap := map[string]string{ + "sessionPkId": fmt.Sprintf("%s/%s/%s", getOrg(), userName, getApp()), + "sessionId": sessionId, + } + + url := auth.GetUrl("is-session-duplicated", queryMap) + + resp, _ := doGetResponse(url) + + return resp.Data == true +} + +func getOrg() string { + return beego.AppConfig.String("casdoorOrganization") +} + +func getApp() string { + return beego.AppConfig.String("casdoorApplication") +} + +func doPost(action string, queryMap map[string]string, postBytes []byte, isFile bool) (*Response, error) { + client := &http.Client{} + url := auth.GetUrl(action, queryMap) + + var resp *http.Response + var err error + var contentType string + var body io.Reader + if isFile { + contentType, body, err = createForm(map[string][]byte{"file": postBytes}) + if err != nil { + return nil, err + } } else { - if unixTimestamp == sessionUnixTimestamp { - return false - } else { - return true + contentType = "text/plain;charset=UTF-8" + body = bytes.NewReader(postBytes) + } + + req, err := http.NewRequest("POST", url, body) + if err != nil { + return nil, err + } + + clientId := beego.AppConfig.String("clientId") + clientSecret := beego.AppConfig.String("clientSecret") + + req.SetBasicAuth(clientId, clientSecret) + req.Header.Set("Content-Type", contentType) + + resp, err = client.Do(req) + if err != nil { + return nil, err + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + return } + }(resp.Body) + + respByte, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err } + + var response Response + err = json.Unmarshal(respByte, &response) + if err != nil { + return nil, err + } + + return &response, nil +} + +func createForm(formData map[string][]byte) (string, io.Reader, error) { + // https://tonybai.com/2021/01/16/upload-and-download-file-using-multipart-form-over-http/ + + body := new(bytes.Buffer) + w := multipart.NewWriter(body) + defer w.Close() + + for k, v := range formData { + pw, err := w.CreateFormFile(k, "file") + if err != nil { + panic(err) + } + + _, err = pw.Write(v) + if err != nil { + panic(err) + } + } + + return w.FormDataContentType(), body, nil +} + +func doGetResponse(url string) (*Response, error) { + respBytes, err := auth.DoGetBytesRaw(url) + + var response Response + err = json.Unmarshal(respBytes, &response) + if err != nil { + return nil, err + } + + if response.Status != "ok" { + return nil, fmt.Errorf(response.Msg) + } + + return &response, nil }