diff --git a/internal/api/members/controler.go b/internal/api/members/controler.go index 282fe01c..40cba097 100644 --- a/internal/api/members/controler.go +++ b/internal/api/members/controler.go @@ -14,7 +14,7 @@ type MemberDataPayload struct { func (h *MembersHandler) membersList(w http.ResponseWriter, r *http.Request) { members := []MemberDataPayload{} - for _, session := range h.sessions.Members() { + for _, session := range h.sessions.List() { profile := session.GetProfile() members = append(members, MemberDataPayload{ ID: session.ID(), diff --git a/internal/api/session.go b/internal/api/session.go index c7678e14..d8792b12 100644 --- a/internal/api/session.go +++ b/internal/api/session.go @@ -30,12 +30,23 @@ func (api *ApiManagerCtx) Login(w http.ResponseWriter, r *http.Request) { return } + token := data.Secret + // TODO: Proper login. - //session, err := api.sessions.Authenticate(data.ID, data.Secret) - //if err != nil { - utils.HttpUnauthorized(w, "no authentication implemented") + session, err := api.sessions.Create(token, types.MemberProfile{ + Name: data.ID, + IsAdmin: true, + CanLogin: true, + CanConnect: true, + CanWatch: true, + CanHost: true, + CanAccessClipboard: true, + }) + + if err != nil { + utils.HttpUnauthorized(w, err) return - //} + } sameSite := http.SameSiteNoneMode if UnsecureCookies { @@ -43,17 +54,8 @@ func (api *ApiManagerCtx) Login(w http.ResponseWriter, r *http.Request) { } http.SetCookie(w, &http.Cookie{ - Name: "neko-id", - Value: session.ID(), - Expires: CookieExpirationDate, - Secure: !UnsecureCookies, - SameSite: sameSite, - HttpOnly: false, - }) - - http.SetCookie(w, &http.Cookie{ - Name: "neko-secret", - Value: data.Secret, + Name: "NEKO_SESSION", + Value: token, Expires: CookieExpirationDate, Secure: !UnsecureCookies, SameSite: sameSite, @@ -68,22 +70,22 @@ func (api *ApiManagerCtx) Login(w http.ResponseWriter, r *http.Request) { } func (api *ApiManagerCtx) Logout(w http.ResponseWriter, r *http.Request) { + session := auth.GetSession(r) + + // TODO: Proper logout. + err := api.sessions.Delete(session.ID()) + if err != nil { + utils.HttpUnauthorized(w, err) + return + } + sameSite := http.SameSiteNoneMode if UnsecureCookies { sameSite = http.SameSiteDefaultMode } http.SetCookie(w, &http.Cookie{ - Name: "neko-id", - Value: "", - Expires: time.Unix(0, 0), - Secure: !UnsecureCookies, - SameSite: sameSite, - HttpOnly: false, - }) - - http.SetCookie(w, &http.Cookie{ - Name: "neko-secret", + Name: "NEKO_SESSION", Value: "", Expires: time.Unix(0, 0), Secure: !UnsecureCookies, diff --git a/internal/config/session.go b/internal/config/session.go index e7472478..b7de9c84 100644 --- a/internal/config/session.go +++ b/internal/config/session.go @@ -6,46 +6,18 @@ import ( ) type Session struct { - Password string - AdminPassword string ImplicitHosting bool - DatabaseAdapter string - FilePath string } func (Session) Init(cmd *cobra.Command) error { - cmd.PersistentFlags().String("password", "neko", "password for connecting to stream") - if err := viper.BindPFlag("password", cmd.PersistentFlags().Lookup("password")); err != nil { - return err - } - - cmd.PersistentFlags().String("password_admin", "admin", "admin password for connecting to stream") - if err := viper.BindPFlag("password_admin", cmd.PersistentFlags().Lookup("password_admin")); err != nil { - return err - } - cmd.PersistentFlags().Bool("implicit_hosting", true, "allow implicit control switching") if err := viper.BindPFlag("implicit_hosting", cmd.PersistentFlags().Lookup("implicit_hosting")); err != nil { return err } - cmd.PersistentFlags().String("database_adapter", "file", "choose database adapter for members") - if err := viper.BindPFlag("database_adapter", cmd.PersistentFlags().Lookup("database_adapter")); err != nil { - return err - } - - cmd.PersistentFlags().String("file_path", "/home/neko/members.json", "file adapter: specify file path") - if err := viper.BindPFlag("file_path", cmd.PersistentFlags().Lookup("file_path")); err != nil { - return err - } - return nil } func (s *Session) Set() { - s.Password = viper.GetString("password") - s.AdminPassword = viper.GetString("password_admin") s.ImplicitHosting = viper.GetBool("implicit_hosting") - s.DatabaseAdapter = viper.GetString("database_adapter") - s.FilePath = viper.GetString("file_path") } diff --git a/internal/session/auth.go b/internal/session/auth.go index d875dc64..fe085318 100644 --- a/internal/session/auth.go +++ b/internal/session/auth.go @@ -24,7 +24,7 @@ func (manager *SessionManagerCtx) Authenticate(r *http.Request) (types.Session, func getToken(r *http.Request) (string, bool) { // get from Cookie - cookie, err := r.Cookie("neko-token") + cookie, err := r.Cookie("NEKO_SESSION") if err == nil { return cookie.Value, true } diff --git a/internal/session/manager.go b/internal/session/manager.go index d6cbd786..8208692b 100644 --- a/internal/session/manager.go +++ b/internal/session/manager.go @@ -9,37 +9,45 @@ import ( "github.com/rs/zerolog/log" "demodesk/neko/internal/config" - "demodesk/neko/internal/session/database" "demodesk/neko/internal/types" "demodesk/neko/internal/utils" ) func New(config *config.Session) *SessionManagerCtx { return &SessionManagerCtx{ - logger: log.With().Str("module", "session").Logger(), - host: nil, - hostMu: sync.Mutex{}, - config: config, - database: database.New(config), - members: make(map[string]*SessionCtx), - membersMu: sync.Mutex{}, - emmiter: events.New(), + logger: log.With().Str("module", "session").Logger(), + config: config, + host: nil, + hostMu: sync.Mutex{}, + sessions: make(map[string]*SessionCtx), + sessionsMu: sync.Mutex{}, + emmiter: events.New(), } } type SessionManagerCtx struct { - logger zerolog.Logger - host types.Session - hostMu sync.Mutex - config *config.Session - database types.MembersDatabase - members map[string]*SessionCtx - membersMu sync.Mutex - emmiter events.EventEmmiter + logger zerolog.Logger + config *config.Session + host types.Session + hostMu sync.Mutex + sessions map[string]*SessionCtx + sessionsMu sync.Mutex + emmiter events.EventEmmiter } -// TODO: Extract members map + primitives. -func (manager *SessionManagerCtx) add(id string, profile types.MemberProfile) types.Session { +// --- +// sessions +// --- + +func (manager *SessionManagerCtx) Create(id string, profile types.MemberProfile) (types.Session, error) { + manager.sessionsMu.Lock() + + _, ok := manager.sessions[id] + if ok { + manager.sessionsMu.Unlock() + return nil, fmt.Errorf("Session id already exists.") + } + session := &SessionCtx{ id: id, manager: manager, @@ -47,104 +55,24 @@ func (manager *SessionManagerCtx) add(id string, profile types.MemberProfile) ty profile: profile, } - manager.members[id] = session - return session -} - -// --- -// members -// --- -func (manager *SessionManagerCtx) Connect() error { - if err := manager.database.Connect(); err != nil { - return err - } - - profiles, err := manager.database.Select() - if err != nil { - return err - } - - for id, profile := range profiles { - _ = manager.add(id, profile) - } - - // TODO: Move to Database, or make `admin` as reserved ID. - - // create default admin account at startup - _ = manager.add("admin", types.MemberProfile{ - Secret: manager.config.AdminPassword, - Name: "Administrator", - IsAdmin: true, - CanLogin: true, - CanConnect: true, - CanWatch: true, - CanHost: true, - CanAccessClipboard: true, - }) - - // create default user account at startup - _ = manager.add("user", types.MemberProfile{ - Secret: manager.config.Password, - Name: "User", - IsAdmin: false, - CanLogin: true, - CanConnect: true, - CanWatch: true, - CanHost: true, - CanAccessClipboard: true, - }) - - return nil -} - -func (manager *SessionManagerCtx) Disconnect() error { - return manager.database.Disconnect() -} - -func (manager *SessionManagerCtx) Create(id string, profile types.MemberProfile) (types.Session, error) { - manager.membersMu.Lock() - - _, ok := manager.members[id] - if ok { - manager.membersMu.Unlock() - return nil, fmt.Errorf("Member ID already exists.") - } - - err := manager.database.Insert(id, profile) - if err != nil { - manager.membersMu.Unlock() - return nil, err - } - - session := manager.add(id, profile) - manager.membersMu.Unlock() + manager.sessions[id] = session + manager.sessionsMu.Unlock() manager.emmiter.Emit("created", session) return session, nil } func (manager *SessionManagerCtx) Update(id string, profile types.MemberProfile) error { - manager.membersMu.Lock() + manager.sessionsMu.Lock() - session, ok := manager.members[id] + session, ok := manager.sessions[id] if !ok { - manager.membersMu.Unlock() - return fmt.Errorf("Member not found.") - } - - // preserve secret if not updated - if profile.Secret == "" { - profile.Secret = session.profile.Secret - } - - err := manager.database.Update(id, profile) - if err != nil { - manager.membersMu.Unlock() - return err + manager.sessionsMu.Unlock() + return fmt.Errorf("Session id not found.") } session.profile = profile - manager.membersMu.Unlock() + manager.sessionsMu.Unlock() manager.emmiter.Emit("profile_changed", session) session.profileChanged() @@ -152,24 +80,19 @@ func (manager *SessionManagerCtx) Update(id string, profile types.MemberProfile) } func (manager *SessionManagerCtx) Delete(id string) error { - manager.membersMu.Lock() - session, ok := manager.members[id] + manager.sessionsMu.Lock() + session, ok := manager.sessions[id] if !ok { - manager.membersMu.Unlock() - return fmt.Errorf("Member not found.") + manager.sessionsMu.Unlock() + return fmt.Errorf("Session id not found.") } - err := manager.database.Delete(id) - if err != nil { - manager.membersMu.Unlock() - return err - } - - delete(manager.members, id) - manager.membersMu.Unlock() + delete(manager.sessions, id) + manager.sessionsMu.Unlock() + var err error if session.IsConnected() { - err = session.Disconnect("member deleted") + err = session.Disconnect("session deleted") } manager.emmiter.Emit("deleted", session) @@ -177,13 +100,25 @@ func (manager *SessionManagerCtx) Delete(id string) error { } func (manager *SessionManagerCtx) Get(id string) (types.Session, bool) { - manager.membersMu.Lock() - defer manager.membersMu.Unlock() + manager.sessionsMu.Lock() + defer manager.sessionsMu.Unlock() - session, ok := manager.members[id] + session, ok := manager.sessions[id] return session, ok } +func (manager *SessionManagerCtx) List() []types.Session { + manager.sessionsMu.Lock() + defer manager.sessionsMu.Unlock() + + var sessions []types.Session + for _, session := range manager.sessions { + sessions = append(sessions, session) + } + + return sessions +} + // --- // host // --- @@ -213,39 +148,14 @@ func (manager *SessionManagerCtx) ClearHost() { } // --- -// members list +// broadcasts // --- -func (manager *SessionManagerCtx) HasConnectedMembers() bool { - manager.membersMu.Lock() - defer manager.membersMu.Unlock() - - for _, session := range manager.members { - if session.IsConnected() { - return true - } - } - - return false -} - -func (manager *SessionManagerCtx) Members() []types.Session { - manager.membersMu.Lock() - defer manager.membersMu.Unlock() - - var sessions []types.Session - for _, session := range manager.members { - sessions = append(sessions, session) - } - - return sessions -} - func (manager *SessionManagerCtx) Broadcast(v interface{}, exclude interface{}) { - manager.membersMu.Lock() - defer manager.membersMu.Unlock() + manager.sessionsMu.Lock() + defer manager.sessionsMu.Unlock() - for id, session := range manager.members { + for id, session := range manager.sessions { if !session.IsConnected() { continue } @@ -263,10 +173,10 @@ func (manager *SessionManagerCtx) Broadcast(v interface{}, exclude interface{}) } func (manager *SessionManagerCtx) AdminBroadcast(v interface{}, exclude interface{}) { - manager.membersMu.Lock() - defer manager.membersMu.Unlock() + manager.sessionsMu.Lock() + defer manager.sessionsMu.Unlock() - for id, session := range manager.members { + for id, session := range manager.sessions { if !session.IsConnected() || !session.IsAdmin() { continue } diff --git a/internal/session/session.go b/internal/session/session.go index 8b7a2e52..0311cf6f 100644 --- a/internal/session/session.go +++ b/internal/session/session.go @@ -26,10 +26,6 @@ func (session *SessionCtx) ID() string { // profile // --- -func (session *SessionCtx) VerifySecret(secret string) bool { - return session.profile.Secret == secret -} - func (session *SessionCtx) Name() string { return session.profile.Name } diff --git a/internal/types/session.go b/internal/types/session.go index c909de8b..912bf760 100644 --- a/internal/types/session.go +++ b/internal/types/session.go @@ -32,7 +32,6 @@ type Session interface { ID() string // profile - VerifySecret(secret string) bool Name() string IsAdmin() bool CanLogin() bool @@ -60,20 +59,16 @@ type Session interface { } type SessionManager interface { - Connect() error - Disconnect() error - Create(id string, profile MemberProfile) (Session, error) Update(id string, profile MemberProfile) error Get(id string) (Session, bool) Delete(id string) error + List() []Session SetHost(host Session) GetHost() Session ClearHost() - HasConnectedMembers() bool - Members() []Session Broadcast(v interface{}, exclude interface{}) AdminBroadcast(v interface{}, exclude interface{}) @@ -88,6 +83,5 @@ type SessionManager interface { ImplicitHosting() bool - AuthenticateRequest(r *http.Request) (Session, error) - Authenticate(id string, secret string) (Session, error) + Authenticate(r *http.Request) (Session, error) } diff --git a/internal/websocket/handler/system.go b/internal/websocket/handler/system.go index ecff96c2..9b244545 100644 --- a/internal/websocket/handler/system.go +++ b/internal/websocket/handler/system.go @@ -24,7 +24,7 @@ func (h *MessageHandlerCtx) systemInit(session types.Session) error { } members := map[string]message.MemberData{} - for _, session := range h.sessions.Members() { + for _, session := range h.sessions.List() { members[session.ID()] = message.MemberData{ ID: session.ID(), Profile: session.GetProfile(), diff --git a/neko.go b/neko.go index ceaa9105..7b912471 100644 --- a/neko.go +++ b/neko.go @@ -132,9 +132,6 @@ func (neko *Neko) Start() { neko.sessionManager = session.New( neko.Configs.Session, ) - if err := neko.sessionManager.Connect(); err != nil { - neko.logger.Panic().Err(err).Msg("unable to connect to session manager") - } neko.desktopManager = desktop.New( neko.Configs.Desktop, @@ -184,12 +181,6 @@ func (neko *Neko) Start() { } func (neko *Neko) Shutdown() { - if err := neko.sessionManager.Disconnect(); err != nil { - neko.logger.Err(err).Msg("session manager disconnect with an error") - } else { - neko.logger.Debug().Msg("session manager disconnect") - } - if err := neko.desktopManager.Shutdown(); err != nil { neko.logger.Err(err).Msg("desktop manager shutdown with an error") } else {