Parcourir la source

Added resumable account on login interface

Toby Chui il y a 1 an
Parent
commit
5c01df8d09
5 fichiers modifiés avec 157 ajouts et 8 suppressions
  1. 21 0
      src/auth.go
  2. 63 8
      src/mod/auth/accountSwitch.go
  3. 3 0
      src/mod/auth/auth.go
  4. 11 0
      src/web/img/public/user.svg
  5. 59 0
      src/web/login.system

+ 21 - 0
src/auth.go

@@ -2,6 +2,7 @@ package main
 
 import (
 	"crypto/rand"
+	"encoding/json"
 	"net/http"
 
 	auth "imuslab.com/arozos/mod/auth"
@@ -125,4 +126,24 @@ func AuthSettingsInit() {
 	userRouter.HandleFunc("/system/auth/u/list", authAgent.SwitchableAccountManager.HandleSwitchableAccountListing)
 	userRouter.HandleFunc("/system/auth/u/switch", authAgent.SwitchableAccountManager.HandleAccountSwitch)
 	userRouter.HandleFunc("/system/auth/u/logoutAll", authAgent.SwitchableAccountManager.HandleLogoutAllAccounts)
+
+	//API for not logged in pool check
+	http.HandleFunc("/system/auth/u/p/list", func(w http.ResponseWriter, r *http.Request) {
+		type ResumableSessionAccount struct {
+			Username     string
+			ProfileImage string
+		}
+		resp := ResumableSessionAccount{}
+		sessionOwnerName := authAgent.SwitchableAccountManager.GetUnauthedSwitchableAccountCreatorList(w, r)
+		resp.Username = sessionOwnerName
+		if sessionOwnerName != "" {
+			u, err := userHandler.GetUserInfoFromUsername(sessionOwnerName)
+			if err == nil {
+				resp.ProfileImage = u.GetUserIcon()
+			}
+		}
+
+		js, _ := json.Marshal(resp)
+		utils.SendJSONResponse(w, string(js))
+	})
 }

+ 63 - 8
src/mod/auth/accountSwitch.go

@@ -105,8 +105,7 @@ func (m *SwitchableAccountPoolManager) HandleSwitchableAccountListing(w http.Res
 	targetPool, err := m.GetPoolByID(poolid)
 	if err != nil {
 		//Pool expired. Unset the session
-		session.Values["poolid"] = nil
-		session.Save(r, w)
+		unsetPoolidFromSession(session, w, r)
 		utils.SendErrorResponse(w, err.Error())
 		return
 	}
@@ -114,8 +113,7 @@ func (m *SwitchableAccountPoolManager) HandleSwitchableAccountListing(w http.Res
 	//Check if the user can access this pool
 	if !targetPool.IsAccessibleBy(currentUsername) {
 		//Unset the session
-		session.Values["poolid"] = nil
-		session.Save(r, w)
+		unsetPoolidFromSession(session, w, r)
 		utils.SendErrorResponse(w, "access denied")
 		return
 	}
@@ -140,6 +138,28 @@ func (m *SwitchableAccountPoolManager) HandleSwitchableAccountListing(w http.Res
 	utils.SendJSONResponse(w, string(js))
 }
 
+// Handle unauth account listing by cookie. You can use this without authRouter.
+func (m *SwitchableAccountPoolManager) GetUnauthedSwitchableAccountCreatorList(w http.ResponseWriter, r *http.Request) string {
+	resumeSessionOwnerName := ""
+	session, _ := m.SessionStore.Get(r, m.SessionName)
+	poolid, ok := session.Values["poolid"].(string)
+	if !ok {
+		//poolid not found. Return empty string
+		return ""
+	}
+
+	targetPool, err := m.GetPoolByID(poolid)
+	if err != nil {
+		//Target pool not found or all user expired
+		unsetPoolidFromSession(session, w, r)
+		return ""
+	}
+
+	//Get the creator name of the pool
+	resumeSessionOwnerName = targetPool.Creator
+	return resumeSessionOwnerName
+}
+
 // Handle logout of the current user, return the fallback user if any
 func (m *SwitchableAccountPoolManager) HandleLogoutforUser(w http.ResponseWriter, r *http.Request) (string, error) {
 	currentUsername, err := m.authAgent.GetUserName(w, r)
@@ -167,8 +187,7 @@ func (m *SwitchableAccountPoolManager) HandleLogoutforUser(w http.ResponseWriter
 		targetpool.Delete()
 
 		//Unset the session
-		session.Values["poolid"] = nil
-		session.Save(r, w)
+		unsetPoolidFromSession(session, w, r)
 
 		return "", nil
 	}
@@ -208,8 +227,7 @@ func (m *SwitchableAccountPoolManager) HandleLogoutAllAccounts(w http.ResponseWr
 	targetpool.Delete()
 
 	//Unset the session
-	session.Values["poolid"] = nil
-	session.Save(r, w)
+	unsetPoolidFromSession(session, w, r)
 
 	utils.SendOK(w)
 }
@@ -330,6 +348,37 @@ func (m *SwitchableAccountPoolManager) GetAllPools() ([]*SwitchableAccountsPool,
 	return results, nil
 }
 
+// This function shall be called when user logged in after login session expired.
+// This will see if the user is logging in as the pool creator.
+// If yes, they can continue to access the switchable account pools.
+// if the user is logging in as a sub-account (i.e. not the creator of the switchable account pool),
+// the account pool id will be reset to prevent hacking from sub-account to master account
+func (m *SwitchableAccountPoolManager) MatchPoolCreatorOrResetPoolID(username string, w http.ResponseWriter, r *http.Request) {
+	session, _ := m.SessionStore.Get(r, m.SessionName)
+	poolid, ok := session.Values["poolid"].(string)
+	if !ok {
+		//No pool. Continue
+		return
+	}
+
+	//Get switchable pool from manager
+	targetPool, err := m.GetPoolByID(poolid)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	if targetPool.Creator != username {
+		//Reset the pool id for this user
+		unsetPoolidFromSession(session, w, r)
+
+	} else {
+		//User logging in with master account after login session expired.
+		//Allow user to continue access sub-accounts in the pool
+		return
+	}
+}
+
 // Get a switchable account pool by its id
 func (m *SwitchableAccountPoolManager) GetPoolByID(uuid string) (*SwitchableAccountsPool, error) {
 	targetPool := SwitchableAccountsPool{}
@@ -486,3 +535,9 @@ func (p *SwitchableAccountsPool) Delete() error {
 func (p *SwitchableAccountsPool) IsAccountExpired(acc *SwitchableAccount) bool {
 	return time.Now().Unix() > acc.LastSwitch+p.parent.ExpireTime
 }
+
+func unsetPoolidFromSession(session *sessions.Session, w http.ResponseWriter, r *http.Request) {
+	//Unset the session
+	session.Values["poolid"] = nil
+	session.Save(r, w)
+}

+ 3 - 0
src/mod/auth/auth.go

@@ -229,6 +229,9 @@ func (a *AuthAgent) HandleLogin(w http.ResponseWriter, r *http.Request) {
 		//Reset user retry count if any
 		a.ExpDelayHandler.ResetUserRetryCount(username, r)
 
+		//Check if the current switchable account pool owner is this user.
+		a.SwitchableAccountManager.MatchPoolCreatorOrResetPoolID(username, w, r)
+
 		//Print the login message to console
 		log.Println(username + " logged in.")
 		a.Logger.LogAuth(r, true)

+ 11 - 0
src/web/img/public/user.svg

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<rect fill="#3E3A39" width="128" height="128"/>
+<circle fill="#FFFFFF" cx="64.001" cy="46.508" r="20.033"/>
+<path fill="#FFFFFF" d="M62.896,110.907c16.229,0,29.361-2.217,29.361-4.959c0-21.133-8.806-38.231-19.689-38.231
+	c0,3.252-3.989,5.884-8.922,5.884c-4.692,0-8.492-2.632-8.492-5.884c-10.728,0-19.41,17.1-19.41,38.231
+	c0,2.742,12.235,4.959,27.354,4.959"/>
+</svg>

+ 59 - 0
src/web/login.system

@@ -99,6 +99,20 @@
         margin-top:1em;
     }
 
+    .alternativeAccount:not(.disabled){
+        cursor: pointer;
+    }
+
+    .alternativeAccount:not(.disabled):hover{
+        background-color: rgb(245, 245, 245);
+    }
+
+    .alternativeAccount:disabled{
+        opacity: 0.6;
+        pointer-events: none !important;
+        user-select: none;
+        cursor: not-allowed;
+    }
     </style>
     </head>
     <body>
@@ -134,6 +148,23 @@
                 <div class="ldaponly" style="display:inline-block;">
                     <a class="ui button oauthbtn subthemecolor" href="ldapLogin.system">Sign In via LDAP</a><br>
                 </div>
+                <div class="resumableOnly" style="display:none;">
+                    <br>
+                    <div class="ui clickable segment alternativeAccount" style="margin-bottom: 0px; padding-bottom: 8px; width: 100%; padding-top: 0px;">
+                        <div style="margin-top: 0.6em;">
+                            <div class="ui header">
+                                <img class="usericon ui circular image" src="img/public/user.svg">
+                                <div class="content" style="font-size: 95% !important;">
+                                    <span class="username"><i class="ui loading spinner icon"></i></span> 
+                                    <div class="sub header usergroup"><i class="ui green check circle icon" style="margin-right: 0px;"></i> Resumable Session</div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <br>
+                    <button class="ui subthemecolor newResumableSession button" style="color: white; display:none;"><i class="ui add icon"></i> Create New Session</button>
+                </div>
+
                 <br>
                 <div class="ui breadcrumb" style="margin-top:12px;">
                     <a class="section signup" style="cursor:pointer; display:none;" href="public/register/register.system">Sign Up</a>
@@ -254,6 +285,34 @@
                 }
             });
 
+            //Switchable accounts related code, check if the user has a session to continue
+            $.getJSON("system/auth/u/p/list",function(data){
+                if (data.Username != ""){
+                    //There is a session to resume
+                    let resumeableAccountUsername = data.Username;
+                    $(".resumableOnly").show();
+                    $(".resumableOnly").find(".username").text(data.Username);
+                    console.log(data.ProfileImage, $('.resumableOnly').find(".usericon"));
+                    if (data.ProfileImage != ""){
+                        $('.resumableOnly').find(".usericon").attr("src", data.ProfileImage);
+                    }
+
+                    $(".alternativeAccount").on("click", function(event){
+                        $("#username").val(resumeableAccountUsername);
+                        $("#username").parent().addClass("disabled");
+                        $(".alternativeAccount").addClass("disabled");
+                        $(".newResumableSession").show();
+                    });
+
+                    $(".newResumableSession").on("click", function(event){
+                        $("#username").val("");
+                        $("#username").parent().removeClass("disabled");
+                        $(".alternativeAccount").removeClass("disabled");
+                        $(".newResumableSession").hide();
+                    })
+                }
+            });
+
             if(get('redirect') != undefined){
                 $(".section.signin").attr("href","system/auth/oauth/login?redirect=" + redirectionAddress);
             }