session_manager.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /*
  2. Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package simulator
  14. import (
  15. "context"
  16. "fmt"
  17. "net/http"
  18. "strings"
  19. "time"
  20. "github.com/google/uuid"
  21. "github.com/vmware/govmomi/object"
  22. "github.com/vmware/govmomi/session"
  23. "github.com/vmware/govmomi/vim25/methods"
  24. "github.com/vmware/govmomi/vim25/mo"
  25. "github.com/vmware/govmomi/vim25/soap"
  26. "github.com/vmware/govmomi/vim25/types"
  27. )
  28. type SessionManager struct {
  29. mo.SessionManager
  30. ServiceHostName string
  31. sessions map[string]Session
  32. }
  33. func NewSessionManager(ref types.ManagedObjectReference) object.Reference {
  34. s := &SessionManager{
  35. sessions: make(map[string]Session),
  36. }
  37. s.Self = ref
  38. return s
  39. }
  40. func createSession(ctx *Context, name string, locale string) types.UserSession {
  41. now := time.Now().UTC()
  42. if locale == "" {
  43. locale = session.Locale
  44. }
  45. session := Session{
  46. UserSession: types.UserSession{
  47. Key: uuid.New().String(),
  48. UserName: name,
  49. FullName: name,
  50. LoginTime: now,
  51. LastActiveTime: now,
  52. Locale: locale,
  53. MessageLocale: locale,
  54. },
  55. Registry: NewRegistry(),
  56. }
  57. ctx.SetSession(session, true)
  58. return session.UserSession
  59. }
  60. func (s *SessionManager) Login(ctx *Context, req *types.Login) soap.HasFault {
  61. body := new(methods.LoginBody)
  62. if req.UserName == "" || req.Password == "" || ctx.Session != nil {
  63. body.Fault_ = invalidLogin
  64. } else {
  65. body.Res = &types.LoginResponse{
  66. Returnval: createSession(ctx, req.UserName, req.Locale),
  67. }
  68. }
  69. return body
  70. }
  71. func (s *SessionManager) LoginExtensionByCertificate(ctx *Context, req *types.LoginExtensionByCertificate) soap.HasFault {
  72. body := new(methods.LoginExtensionByCertificateBody)
  73. if ctx.req.TLS == nil || len(ctx.req.TLS.PeerCertificates) == 0 {
  74. body.Fault_ = Fault("", new(types.NoClientCertificate))
  75. return body
  76. }
  77. if req.ExtensionKey == "" || ctx.Session != nil {
  78. body.Fault_ = invalidLogin
  79. } else {
  80. body.Res = &types.LoginExtensionByCertificateResponse{
  81. Returnval: createSession(ctx, req.ExtensionKey, req.Locale),
  82. }
  83. }
  84. return body
  85. }
  86. func (s *SessionManager) LoginByToken(ctx *Context, req *types.LoginByToken) soap.HasFault {
  87. body := new(methods.LoginByTokenBody)
  88. if ctx.Session != nil {
  89. body.Fault_ = invalidLogin
  90. } else {
  91. var subject struct {
  92. ID string `xml:"Assertion>Subject>NameID"`
  93. }
  94. if s, ok := ctx.Header.Security.(*Element); ok {
  95. _ = s.Decode(&subject)
  96. }
  97. if subject.ID == "" {
  98. body.Fault_ = invalidLogin
  99. return body
  100. }
  101. body.Res = &types.LoginByTokenResponse{
  102. Returnval: createSession(ctx, subject.ID, req.Locale),
  103. }
  104. }
  105. return body
  106. }
  107. func (s *SessionManager) Logout(ctx *Context, _ *types.Logout) soap.HasFault {
  108. session := ctx.Session
  109. delete(s.sessions, session.Key)
  110. pc := Map.content().PropertyCollector
  111. for ref, obj := range ctx.Session.Registry.objects {
  112. if ref == pc {
  113. continue // don't unregister the PropertyCollector singleton
  114. }
  115. if _, ok := obj.(RegisterObject); ok {
  116. ctx.Map.Remove(ref) // Remove RegisterObject handlers
  117. }
  118. }
  119. ctx.postEvent(&types.UserLogoutSessionEvent{
  120. IpAddress: session.IpAddress,
  121. UserAgent: session.UserAgent,
  122. SessionId: session.Key,
  123. LoginTime: &session.LoginTime,
  124. })
  125. return &methods.LogoutBody{Res: new(types.LogoutResponse)}
  126. }
  127. func (s *SessionManager) TerminateSession(ctx *Context, req *types.TerminateSession) soap.HasFault {
  128. body := new(methods.TerminateSessionBody)
  129. for _, id := range req.SessionId {
  130. if id == ctx.Session.Key {
  131. body.Fault_ = Fault("", new(types.InvalidArgument))
  132. return body
  133. }
  134. delete(s.sessions, id)
  135. }
  136. body.Res = new(types.TerminateSessionResponse)
  137. return body
  138. }
  139. func (s *SessionManager) SessionIsActive(ctx *Context, req *types.SessionIsActive) soap.HasFault {
  140. body := new(methods.SessionIsActiveBody)
  141. if ctx.Map.IsESX() {
  142. body.Fault_ = Fault("", new(types.NotImplemented))
  143. return body
  144. }
  145. body.Res = new(types.SessionIsActiveResponse)
  146. if session, exists := s.sessions[req.SessionID]; exists {
  147. body.Res.Returnval = session.UserName == req.UserName
  148. }
  149. return body
  150. }
  151. func (s *SessionManager) AcquireCloneTicket(ctx *Context, _ *types.AcquireCloneTicket) soap.HasFault {
  152. session := *ctx.Session
  153. session.Key = uuid.New().String()
  154. s.sessions[session.Key] = session
  155. return &methods.AcquireCloneTicketBody{
  156. Res: &types.AcquireCloneTicketResponse{
  157. Returnval: session.Key,
  158. },
  159. }
  160. }
  161. func (s *SessionManager) CloneSession(ctx *Context, ticket *types.CloneSession) soap.HasFault {
  162. body := new(methods.CloneSessionBody)
  163. session, exists := s.sessions[ticket.CloneTicket]
  164. if exists {
  165. delete(s.sessions, ticket.CloneTicket) // A clone ticket can only be used once
  166. session.Key = uuid.New().String()
  167. ctx.SetSession(session, true)
  168. body.Res = &types.CloneSessionResponse{
  169. Returnval: session.UserSession,
  170. }
  171. } else {
  172. body.Fault_ = invalidLogin
  173. }
  174. return body
  175. }
  176. func (s *SessionManager) AcquireGenericServiceTicket(ticket *types.AcquireGenericServiceTicket) soap.HasFault {
  177. return &methods.AcquireGenericServiceTicketBody{
  178. Res: &types.AcquireGenericServiceTicketResponse{
  179. Returnval: types.SessionManagerGenericServiceTicket{
  180. Id: uuid.New().String(),
  181. HostName: s.ServiceHostName,
  182. },
  183. },
  184. }
  185. }
  186. // internalContext is the session for use by the in-memory client (Service.RoundTrip)
  187. var internalContext = &Context{
  188. Context: context.Background(),
  189. Session: &Session{
  190. UserSession: types.UserSession{
  191. Key: uuid.New().String(),
  192. },
  193. Registry: NewRegistry(),
  194. },
  195. Map: Map,
  196. }
  197. var invalidLogin = Fault("Login failure", new(types.InvalidLogin))
  198. // Context provides per-request Session management.
  199. type Context struct {
  200. req *http.Request
  201. res http.ResponseWriter
  202. svc *Service
  203. context.Context
  204. Session *Session
  205. Header soap.Header
  206. Caller *types.ManagedObjectReference
  207. Map *Registry
  208. }
  209. // mapSession maps an HTTP cookie to a Session.
  210. func (c *Context) mapSession() {
  211. if cookie, err := c.req.Cookie(soap.SessionCookieName); err == nil {
  212. if val, ok := c.svc.sm.sessions[cookie.Value]; ok {
  213. c.SetSession(val, false)
  214. }
  215. }
  216. }
  217. // SetSession should be called after successful authentication.
  218. func (c *Context) SetSession(session Session, login bool) {
  219. session.UserAgent = c.req.UserAgent()
  220. session.IpAddress = strings.Split(c.req.RemoteAddr, ":")[0]
  221. session.LastActiveTime = time.Now()
  222. c.svc.sm.sessions[session.Key] = session
  223. c.Session = &session
  224. if login {
  225. http.SetCookie(c.res, &http.Cookie{
  226. Name: soap.SessionCookieName,
  227. Value: session.Key,
  228. })
  229. c.postEvent(&types.UserLoginSessionEvent{
  230. SessionId: session.Key,
  231. IpAddress: session.IpAddress,
  232. UserAgent: session.UserAgent,
  233. Locale: session.Locale,
  234. })
  235. }
  236. }
  237. // WithLock holds a lock for the given object while then given function is run.
  238. func (c *Context) WithLock(obj mo.Reference, f func()) {
  239. if c.Caller != nil && *c.Caller == obj.Reference() {
  240. // Internal method invocation, obj is already locked
  241. f()
  242. return
  243. }
  244. Map.WithLock(obj, f)
  245. }
  246. // postEvent wraps EventManager.PostEvent for internal use, with a lock on the EventManager.
  247. func (c *Context) postEvent(events ...types.BaseEvent) {
  248. m := Map.EventManager()
  249. c.WithLock(m, func() {
  250. for _, event := range events {
  251. m.PostEvent(c, &types.PostEvent{EventToPost: event})
  252. }
  253. })
  254. }
  255. // Session combines a UserSession and a Registry for per-session managed objects.
  256. type Session struct {
  257. types.UserSession
  258. *Registry
  259. }
  260. // Put wraps Registry.Put, setting the moref value to include the session key.
  261. func (s *Session) Put(item mo.Reference) mo.Reference {
  262. ref := item.Reference()
  263. if ref.Value == "" {
  264. ref.Value = fmt.Sprintf("session[%s]%s", s.Key, uuid.New())
  265. }
  266. s.Registry.setReference(item, ref)
  267. return s.Registry.Put(item)
  268. }
  269. // Get wraps Registry.Get, session-izing singleton objects such as SessionManager and the root PropertyCollector.
  270. func (s *Session) Get(ref types.ManagedObjectReference) mo.Reference {
  271. obj := s.Registry.Get(ref)
  272. if obj != nil {
  273. return obj
  274. }
  275. // Return a session "view" of certain singleton objects
  276. switch ref.Type {
  277. case "SessionManager":
  278. // Clone SessionManager so the PropertyCollector can properly report CurrentSession
  279. m := *Map.SessionManager()
  280. m.CurrentSession = &s.UserSession
  281. // TODO: we could maintain SessionList as part of the SessionManager singleton
  282. for _, session := range m.sessions {
  283. m.SessionList = append(m.SessionList, session.UserSession)
  284. }
  285. return &m
  286. case "PropertyCollector":
  287. if ref == Map.content().PropertyCollector {
  288. return s.Put(NewPropertyCollector(ref))
  289. }
  290. }
  291. return Map.Get(ref)
  292. }