session.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package session
  15. import (
  16. "context"
  17. "fmt"
  18. "math/rand"
  19. "net/url"
  20. "path/filepath"
  21. "reflect"
  22. "strings"
  23. "sync"
  24. "time"
  25. "yunion.io/x/log"
  26. "yunion.io/x/pkg/errors"
  27. "yunion.io/x/pkg/util/stringutils"
  28. "yunion.io/x/pkg/utils"
  29. "yunion.io/x/onecloud/pkg/webconsole/command"
  30. o "yunion.io/x/onecloud/pkg/webconsole/options"
  31. "yunion.io/x/onecloud/pkg/webconsole/recorder"
  32. )
  33. var (
  34. Manager *SSessionManager
  35. AES_KEY string
  36. AccessInterval time.Duration = 5 * time.Minute
  37. )
  38. func init() {
  39. Manager = NewSessionManager()
  40. AES_KEY = fmt.Sprintf("webconsole-%f", rand.Float32())
  41. }
  42. type SSessionManager struct {
  43. *sync.Map
  44. }
  45. func NewSessionManager() *SSessionManager {
  46. s := &SSessionManager{
  47. Map: &sync.Map{},
  48. }
  49. return s
  50. }
  51. func (man *SSessionManager) Save(data ISessionData) (*SSession, error) {
  52. idStr := data.GetId()
  53. if os, ok := man.Load(idStr); ok {
  54. oldSession := os.(*SSession)
  55. if oldSession.duplicateHook != nil {
  56. log.Warningf("session %s already exists, execute dupliate hook", idStr)
  57. oldSession.duplicateHook()
  58. }
  59. }
  60. token, err := utils.EncryptAESBase64Url(AES_KEY, idStr)
  61. if err != nil {
  62. return nil, err
  63. }
  64. session := &SSession{
  65. Id: idStr,
  66. ISessionData: data,
  67. AccessToken: token,
  68. }
  69. man.Store(idStr, session)
  70. return session, nil
  71. }
  72. func (man *SSessionManager) Get(accessToken string) (*SSession, bool) {
  73. id, err := utils.DescryptAESBase64Url(AES_KEY, accessToken)
  74. if err != nil {
  75. log.Errorf("DescryptAESBase64Url error: %v", err)
  76. return nil, false
  77. }
  78. obj, ok := man.Load(id)
  79. if !ok {
  80. return nil, false
  81. }
  82. s := obj.(*SSession)
  83. protocol := s.GetProtocol()
  84. if protocol != SPICE && time.Since(s.AccessedAt) < AccessInterval {
  85. if !(protocol == WS && o.Options.KeepWebsocketSession) {
  86. log.Warningf("Protol: %q, Token: %s, Session: %s can't be accessed during %s, last accessed at: %s", s.GetProtocol(), accessToken, s.Id, AccessInterval, s.AccessedAt)
  87. return nil, false
  88. }
  89. }
  90. s.AccessedAt = time.Now()
  91. return s, true
  92. }
  93. type ISessionData interface {
  94. command.ICommand
  95. IsNeedLogin() (bool, error)
  96. GetId() string
  97. GetDisplayInfo(ctx context.Context) (*SDisplayInfo, error)
  98. }
  99. type ISessionCommand interface {
  100. command.ICommand
  101. GetInstanceName() string
  102. GetIPs() []string
  103. }
  104. type RandomSessionData struct {
  105. command.ICommand
  106. id string
  107. }
  108. func WrapCommandSession(cmd command.ICommand) *RandomSessionData {
  109. return &RandomSessionData{
  110. ICommand: cmd,
  111. id: stringutils.UUID4(),
  112. }
  113. }
  114. func (s *RandomSessionData) GetId() string {
  115. return s.id
  116. }
  117. func (s *RandomSessionData) IsNeedLogin() (bool, error) {
  118. return false, nil
  119. }
  120. func (s *RandomSessionData) GetDisplayInfo(ctx context.Context) (*SDisplayInfo, error) {
  121. userInfo, err := fetchUserInfo(ctx, s.GetClientSession())
  122. if err != nil {
  123. return nil, errors.Wrap(err, "fetchUserInfo")
  124. }
  125. dispInfo := SDisplayInfo{}
  126. dispInfo.WaterMark = fetchWaterMark(userInfo)
  127. dispInfo.InstanceName = s.GetSafeCommandString()
  128. si, ok := s.ICommand.(ISessionCommand)
  129. if ok {
  130. iName := si.GetInstanceName()
  131. if iName != "" {
  132. dispInfo.InstanceName = iName
  133. }
  134. ips := si.GetIPs()
  135. if len(ips) > 0 {
  136. dispInfo.Ips = strings.Join(ips, ",")
  137. }
  138. }
  139. return &dispInfo, nil
  140. }
  141. type SSession struct {
  142. ISessionData
  143. Id string
  144. AccessToken string
  145. AccessedAt time.Time
  146. duplicateHook func()
  147. recorder recorder.Recoder
  148. }
  149. func (s *SSession) GetConnectParams(params url.Values, dispInfo *SDisplayInfo) (string, error) {
  150. if params == nil {
  151. params = url.Values{}
  152. }
  153. params = dispInfo.populateParams(params)
  154. apiUrl, err := url.Parse(o.Options.ApiServer)
  155. if err != nil {
  156. return "", errors.Errorf("invalid api_server url: %s", o.Options.ApiServer)
  157. }
  158. schemeHost := fmt.Sprintf("%s://%s", apiUrl.Scheme, apiUrl.Host)
  159. uPath := filepath.Join(strings.Split(apiUrl.Path, "/")...)
  160. var trimUrl string
  161. if uPath == "" {
  162. trimUrl = schemeHost
  163. } else {
  164. trimUrl = schemeHost + "/" + uPath
  165. }
  166. params.Set("api_server", trimUrl)
  167. params.Set("access_token", s.AccessToken)
  168. params.Set("protocol", s.GetProtocol())
  169. isNeedLogin, err := s.IsNeedLogin()
  170. if err != nil {
  171. params.Set("login_error_message", fmt.Sprintf("%v", err))
  172. }
  173. params.Set("is_need_login", fmt.Sprintf("%v", isNeedLogin))
  174. if len(o.Options.RefererWhitelist) > 0 {
  175. params.Set("referer_whitelist", strings.Join(o.Options.RefererWhitelist, ","))
  176. }
  177. return params.Encode(), nil
  178. }
  179. func (s *SSession) Close() error {
  180. if err := s.ISessionData.Cleanup(); err != nil {
  181. log.Errorf("Clean up command error: %v", err)
  182. }
  183. if curS, ok := Manager.Load(s.GetId()); ok {
  184. if reflect.DeepEqual(curS, s) {
  185. Manager.Delete(s.Id)
  186. }
  187. }
  188. return nil
  189. }
  190. func (s *SSession) RegisterDuplicateHook(f func()) {
  191. s.duplicateHook = f
  192. }
  193. func (s *SSession) GetRecorder() recorder.Recoder {
  194. if s.recorder == nil {
  195. s.recorder = recorder.NewCmdRecorder(s.GetClientSession(), s.GetRecordObject(), s.GetId(), s.AccessedAt)
  196. go s.recorder.Start()
  197. }
  198. return s.recorder
  199. }