diff --git a/src/client_cli/cmdTemplate/helper.go b/src/client_cli/cmdTemplate/helper.go
new file mode 100644
index 0000000000000000000000000000000000000000..5628b814966d00e8d1f8b27991f30344a5200bf9
--- /dev/null
+++ b/src/client_cli/cmdTemplate/helper.go
@@ -0,0 +1,128 @@
+package cmdTemplate
+
+import (
+	"strings"
+	"regexp"
+	"encoding/json"
+	"nexus-client/cmd"
+	u "nexus-client/utils"
+	g "nexus-client/globals"
+	"github.com/google/uuid"
+	"github.com/go-resty/resty/v2"
+)
+
+// Make sure these types MATCH their counterparts in nexus-server codebase!
+type (
+	Template struct {
+		ID uuid.UUID    `json:"id"      validate:"required"`
+		Name string     `json:"name"    validate:"required,min=2,max=256"`
+		Owner string    `json:"owner"   validate:"required,email"`
+		Access string   `json:"access"  validate:"required,min=4,max=16"`  // private or public
+	}
+)
+
+// Converts a Template structure into a pretty string.
+func (tpl *Template)String() string {
+	output, err := json.MarshalIndent(tpl, "", "    ")
+    if err != nil {
+        return err.Error()
+    }
+	return string(output)
+}
+
+func printUsage(c cmd.Command) {
+	u.PrintlnErr(c.GetDesc())
+	u.PrintlnErr("Usage: ",c.GetName()," [ID] [regex]")
+	u.PrintlnErr("Only templates matching either the specified ID or regex will be listed.")
+	u.PrintlnErr("Any number of ID or regex can be specified.")
+	u.PrintlnErr("The regex matches the template's name only and is case-insensitive.")
+	u.PrintlnErr("Regex examples:")
+	u.PrintlnErr("    \"\"    -> matches any name")
+	u.PrintlnErr("    \".\"   -> matches any name")
+	u.PrintlnErr("    \"bla\" -> matches any name containing \"bla\"")
+}
+
+// Call the given route and filter the results based on ID and regex.
+// Arguments (args) can either be:
+// - any number of "template IDs" (UUID)
+// - any number of "template names"
+// - any combination of "template IDs" and "template names"
+// - any regular expression (it applies to the template's name only)
+// Remark: the matching is case-insensitive
+// Regular expression examples:
+//   ""    -> matches everything
+//   "."   -> matches everything
+//   "bla" -> matches any template name containing "bla"
+func getFilteredTemplates(c cmd.Command, args []string, route string) int {
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
+	argc := len(args)
+	if argc < 1 {
+		c.PrintUsage()
+		return 1
+	}
+
+	var ids []string
+	var namePatterns []string
+
+	for _, arg := range args {
+		_, err := uuid.Parse(arg)
+		if err != nil {
+			namePatterns = append(namePatterns, arg)
+		} else {
+			ids = append(ids, arg)
+		}
+	}
+
+	resp, err := client.R().Get(host+route)
+	if err != nil {
+		u.PrintlnErr("Error: "+err.Error())
+		return 1
+	}
+
+	if resp.IsSuccess() {
+		templates, err := getTemplates(resp)
+		if err != nil {
+			u.PrintlnErr("Error: "+err.Error())
+			return 1
+		}
+
+		for _, template := range *templates {
+			found := false
+			for _, id := range ids {
+				if id == template.ID.String() {
+					u.Println(template.String())
+					found = true
+					break
+				}
+			}
+			if found {
+				continue
+			}
+			for _, namePattern := range namePatterns {
+				match, err := regexp.MatchString(strings.ToLower(namePattern), strings.ToLower(template.Name))
+				if err != nil {
+					u.PrintlnErr("Error matching \""+namePattern+"\": "+err.Error())
+				} else {
+					if match {
+						u.Println(template.String())
+						break
+					}
+				}
+			}
+		}
+		return 0
+	} else {
+		u.PrintlnErr("Error: "+resp.Status()+": "+resp.String())
+		return 1
+	}
+}
+
+func getTemplates(resp *resty.Response) (*[]Template, error) {
+	var templates []Template
+	if err := json.Unmarshal(resp.Body(), &templates); err != nil {
+		return nil, err
+	}
+	return &templates, nil
+}
\ No newline at end of file
diff --git a/src/client_cli/cmdTemplate/templateList.go b/src/client_cli/cmdTemplate/templateList.go
index 4d9a8a75e481768a41d43b4a362b71dd5523f810..bf9e5176e44c6ec4b0d82c566aaf793cbf9d0803 100644
--- a/src/client_cli/cmdTemplate/templateList.go
+++ b/src/client_cli/cmdTemplate/templateList.go
@@ -1,10 +1,5 @@
 package cmdTemplate
 
-import (
-	u "nexus-client/utils"
-	g "nexus-client/globals"
-)
-
 type List struct {
     Name string
 }
@@ -14,43 +9,13 @@ func (cmd *List)GetName() string {
 }
  
 func (cmd *List)GetDesc() string {
-	return "List one or more templates"
+	return "List templates that can be listed by the user"
 }
 
 func (cmd *List)PrintUsage() {
-	u.PrintlnErr(cmd.GetDesc())
-	u.PrintlnErr("Usage: "+cmd.Name+" [id]")
+	printUsage(cmd)
 }
 
 func (cmd *List)Run(args []string) int {
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
-	argc := len(args)
-	if !(argc == 0 || argc == 1) {
-		cmd.PrintUsage()
-		return 1
-	}
-
-	var url string
-	if argc == 0 {
-		url = host+"/templates"
-	} else {
-		id := args[0]
-		url = host+"/templates/"+id
-	}
-
-	resp, err := client.R().Get(url)
-	if err != nil {
-		u.PrintlnErr("Error: "+err.Error())
-		return 1
-	}
-
-	if resp.IsSuccess() {
-		u.Println(resp)
-		return 0
-	} else {
-		u.PrintlnErr("Error: "+resp.Status()+": "+resp.String())
-		return 1
-	}
+	return getFilteredTemplates(cmd, args, "/templates")
 }
diff --git a/src/client_cli/cmdUser/helper.go b/src/client_cli/cmdUser/helper.go
new file mode 100644
index 0000000000000000000000000000000000000000..ad853da5fd950fa70f5c19b2de64e38ff0b98958
--- /dev/null
+++ b/src/client_cli/cmdUser/helper.go
@@ -0,0 +1,103 @@
+package cmdUser
+
+import (
+	"strings"
+	"regexp"
+	"encoding/json"
+	"nexus-client/cmd"
+	u "nexus-client/utils"
+	g "nexus-client/globals"
+	"github.com/go-resty/resty/v2"
+)
+
+// Make sure these types MATCH their counterparts in nexus-server codebase!
+type (
+	Capabilities map[string]int
+
+	User struct {
+		Email string           `json:"email"     validate:"required,email"`
+		FirstName string       `json:"firstname" validate:"required,min=2,max=32"`
+		LastName string        `json:"lastname"  validate:"required,min=2,max=32"`
+		Pwd string             `json:"pwd"       validate:"required,min=8"`
+		Caps Capabilities      `json:"caps"      validate:"required"`
+	}
+)
+
+// Converts a User structure into a pretty string.
+func (user *User)String() string {
+	output, err := json.MarshalIndent(user, "", "    ")
+    if err != nil {
+        return err.Error()
+    }
+	return string(output)
+}
+
+func printUsage(c cmd.Command) {
+	u.PrintlnErr(c.GetDesc())
+	u.PrintlnErr("Usage: ",c.GetName()," [regex]")
+	u.PrintlnErr("Only users matching the specified regex will be listed.")
+	u.PrintlnErr("Any number of regex can be specified.")
+	u.PrintlnErr("The regex matches the user email, first name and last name and is case-insensitive.")
+	u.PrintlnErr("Regex examples:")
+	u.PrintlnErr("    \"\"    -> matches any user")
+	u.PrintlnErr("    \".\"   -> matches any user")
+	u.PrintlnErr("    \"bla\" -> matches any user containing \"bla\"")
+}
+
+// Call the given route and filter the results based on a regex.
+// Remark: the regex matches the user email, first name and last name and is case-insensitive.
+// Regular expression examples:
+//   ""    -> matches everything
+//   "."   -> matches everything
+//   "bla" -> matches any user containing "bla"
+func getFilteredUsers(c cmd.Command, args []string, route string) int {
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
+	argc := len(args)
+	if argc < 1 {
+		c.PrintUsage()
+		return 1
+	}
+
+	resp, err := client.R().Get(host+route)
+	if err != nil {
+		u.PrintlnErr("Error: "+err.Error())
+		return 1
+	}
+
+	if resp.IsSuccess() {
+		users, err := getUsers(resp)
+		if err != nil {
+			u.PrintlnErr("Error: "+err.Error())
+			return 1
+		}
+
+		for _, user := range *users {
+			for _, pattern := range args {
+				fieldsToMatch := user.Email+" "+user.FirstName+" "+user.LastName
+				match, err := regexp.MatchString(strings.ToLower(pattern), strings.ToLower(fieldsToMatch))
+				if err != nil {
+					u.PrintlnErr("Error matching \""+pattern+"\": "+err.Error())
+				} else {
+					if match {
+						u.Println(user.String())
+						break
+					}
+				}
+			}
+		}
+		return 0
+	} else {
+		u.PrintlnErr("Error: "+resp.Status()+": "+resp.String())
+		return 1
+	}
+}
+
+func getUsers(resp *resty.Response) (*[]User, error) {
+	var users []User
+	if err := json.Unmarshal(resp.Body(), &users); err != nil {
+		return nil, err
+	}
+	return &users, nil
+}
\ No newline at end of file
diff --git a/src/client_cli/cmdUser/userList.go b/src/client_cli/cmdUser/userList.go
index f1cefedd5ebcc49e1f1eb7da17b0c141c936e554..8adbf62ab45038542038e4770da6219eaf417103 100644
--- a/src/client_cli/cmdUser/userList.go
+++ b/src/client_cli/cmdUser/userList.go
@@ -1,10 +1,5 @@
 package cmdUser
 
-import (
-	u "nexus-client/utils"
-	g "nexus-client/globals"
-)
-
 type List struct {
     Name string
 }
@@ -14,43 +9,13 @@ func (cmd *List)GetName() string {
 }
  
 func (cmd *List)GetDesc() string {
-	return "List one or all users"
+	return "List users"
 }
 
 func (cmd *List)PrintUsage() {
-	u.PrintlnErr(cmd.GetDesc())
-	u.PrintlnErr("Usage: "+cmd.Name+" [email]")
+	printUsage(cmd)
 }
 
 func (cmd *List)Run(args []string) int {
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
-	argc := len(args)
-	if !(argc == 0 || argc == 1) {
-		cmd.PrintUsage()
-		return 1
-	}
-
-	var url string
-	if argc == 0 {
-		url = host+"/users"
-	} else {
-		email := args[0]
-		url = host+"/users/"+email
-	}
-
-	resp, err := client.R().Get(url)
-	if err != nil {
-		u.PrintlnErr("Error: "+err.Error())
-		return 1
-	}
-
-	if resp.IsSuccess() {
-		u.Println(resp)
-		return 0
-	} else {
-		u.PrintlnErr("Error: "+resp.Status()+": "+resp.String())
-		return 1
-	}
+	return getFilteredUsers(cmd, args, "/users")
 }
diff --git a/src/client_cli/cmdUser/userWhoami.go b/src/client_cli/cmdUser/userWhoami.go
index ede314cd5a8d7049eb94f2412a94dae43ea24f4b..4efd4b668ad17e026108a60a028839f0e98f08d6 100644
--- a/src/client_cli/cmdUser/userWhoami.go
+++ b/src/client_cli/cmdUser/userWhoami.go
@@ -14,7 +14,7 @@ func (cmd *Whoami)GetName() string {
 }
  
 func (cmd *Whoami)GetDesc() string {
-	return "Display the currently authenticated user's details"
+	return "Display the user's details"
 }
 
 func (cmd *Whoami)PrintUsage() {
diff --git a/src/client_cli/cmdVM/helper.go b/src/client_cli/cmdVM/helper.go
index 2178733964bda3b92a9cf10ee59270b6f5f271ae..9a97aac6ccfa4a12e9675e14d702567786d8b54f 100644
--- a/src/client_cli/cmdVM/helper.go
+++ b/src/client_cli/cmdVM/helper.go
@@ -53,9 +53,9 @@ func (vm *VM)String() string {
 
 func printUsage(c cmd.Command) {
 	u.PrintlnErr(c.GetDesc())
-	u.PrintlnErr("Usage: ",c.GetName()," [vmID] [regex]")
-	u.PrintlnErr("Only VMs matching either the specified vmID or regex will be listed.")
-	u.PrintlnErr("Any number of vmID or regex can be specified.")
+	u.PrintlnErr("Usage: ",c.GetName()," [ID] [regex]")
+	u.PrintlnErr("Only VMs matching either the specified ID or regex will be listed.")
+	u.PrintlnErr("Any number of ID or regex can be specified.")
 	u.PrintlnErr("The regex matches the VM's name only and is case-insensitive.")
 	u.PrintlnErr("Regex examples:")
 	u.PrintlnErr("    \"\"    -> matches any name")
@@ -63,7 +63,7 @@ func printUsage(c cmd.Command) {
 	u.PrintlnErr("    \"bla\" -> matches any name containing \"bla\"")
 }
 
-// Call the given route and filter the results based on vmID and regex.
+// Call the given route and filter the results based on ID and regex.
 // Arguments (args) can either be:
 // - any number of "VM IDs" (UUID)
 // - any number of "VM names"
diff --git a/src/client_cli/cmdVM/vmList.go b/src/client_cli/cmdVM/vmList.go
index f5e13d4f8f2858543702ff59d66ddf7b0915494a..6aed8213d48021da984b16072fe6b05b49f61afa 100644
--- a/src/client_cli/cmdVM/vmList.go
+++ b/src/client_cli/cmdVM/vmList.go
@@ -9,7 +9,7 @@ func (cmd *List)GetName() string {
 }
  
 func (cmd *List)GetDesc() string {
-	return "List all VMs that can be listed"
+	return "List VMs that can be listed by the user"
 }
 
 func (cmd *List)PrintUsage() {
@@ -17,5 +17,5 @@ func (cmd *List)PrintUsage() {
 }
 
 func (cmd *List)Run(args []string) int {
-	return getFilteredVMs(cmd, args, "/vms/list")
+	return getFilteredVMs(cmd, args, "/vms")
 }
diff --git a/src/client_cli/cmdVM/vmListAttach.go b/src/client_cli/cmdVM/vmListAttach.go
index c10ccfcb1a9cffe5aa0bf80b6344a9c26e73b42d..8567df47d6c539310123ab97ef4265b267c2af4c 100644
--- a/src/client_cli/cmdVM/vmListAttach.go
+++ b/src/client_cli/cmdVM/vmListAttach.go
@@ -9,7 +9,7 @@ func (cmd *ListAttach)GetName() string {
 }
  
 func (cmd *ListAttach)GetDesc() string {
-	return "List VMs that can be attached to"
+	return "List VMs the user can attach to"
 }
 
 func (cmd *ListAttach)PrintUsage() {
diff --git a/src/client_cli/cmdVM/vmListDel.go b/src/client_cli/cmdVM/vmListDel.go
index 7701bac75a73e548d525762cd0aa2c77846db7dd..3d27107d38f3cee54597074cff82eb5943daa56a 100644
--- a/src/client_cli/cmdVM/vmListDel.go
+++ b/src/client_cli/cmdVM/vmListDel.go
@@ -9,7 +9,7 @@ func (cmd *ListDel)GetName() string {
 }
  
 func (cmd *ListDel)GetDesc() string {
-	return "List VMs that can be deleted"
+	return "List VMs the user can delete"
 }
 
 func (cmd *ListDel)PrintUsage() {
diff --git a/src/client_cli/cmdVM/vmListEdit.go b/src/client_cli/cmdVM/vmListEdit.go
index 47b3356692cc41919cc6a95c656602b40e8f7a67..2fe522bb9923621891a9341743de3b881587cb0c 100644
--- a/src/client_cli/cmdVM/vmListEdit.go
+++ b/src/client_cli/cmdVM/vmListEdit.go
@@ -9,7 +9,7 @@ func (cmd *ListEdit)GetName() string {
 }
  
 func (cmd *ListEdit)GetDesc() string {
-	return "List VMs that can be edited"
+	return "List VMs the user can edit"
 }
 
 func (cmd *ListEdit)PrintUsage() {
diff --git a/src/client_cli/cmdVM/vmListEditAccess.go b/src/client_cli/cmdVM/vmListEditAccess.go
index d8fa17daecf020ecb230ef5585e66c124c4c1b11..c7db4da3d781713708c35f34ad84c2e369de7630 100644
--- a/src/client_cli/cmdVM/vmListEditAccess.go
+++ b/src/client_cli/cmdVM/vmListEditAccess.go
@@ -9,7 +9,7 @@ func (cmd *ListEditAccess)GetName() string {
 }
  
 func (cmd *ListEditAccess)GetDesc() string {
-	return "List VMs that can have their VM access edited"
+	return "List VMs the user can edit the VM access for"
 }
 
 func (cmd *ListEditAccess)PrintUsage() {
diff --git a/src/client_cli/cmdVM/vmListStart.go b/src/client_cli/cmdVM/vmListStart.go
index 66a486b7194b72c2eced633d69546b04517a0690..96a34bfc17ddeecb243effe5e751f696751d54ff 100644
--- a/src/client_cli/cmdVM/vmListStart.go
+++ b/src/client_cli/cmdVM/vmListStart.go
@@ -9,7 +9,7 @@ func (cmd *ListStart)GetName() string {
 }
  
 func (cmd *ListStart)GetDesc() string {
-	return "List VMs that can be started"
+	return "List VMs the user can start"
 }
 
 func (cmd *ListStart)PrintUsage() {
diff --git a/src/client_cli/cmdVM/vmListStop.go b/src/client_cli/cmdVM/vmListStop.go
index e7e8a2e1b172037d39cabd29807aa78bec1c1c52..534be4070f91123530f6d58246babf2d6cca19b8 100644
--- a/src/client_cli/cmdVM/vmListStop.go
+++ b/src/client_cli/cmdVM/vmListStop.go
@@ -9,7 +9,7 @@ func (cmd *ListStop)GetName() string {
 }
  
 func (cmd *ListStop)GetDesc() string {
-	return "List VMs that can be stopped"
+	return "List VMs the user can stop"
 }
 
 func (cmd *ListStop)PrintUsage() {
diff --git a/src/client_cli/globals/Globals.go b/src/client_cli/globals/Globals.go
index 4985794b399583152969b664bc9e03674e72844b..582c9a9a4e5affe2f302261ed3dbb0941c89d8e2 100644
--- a/src/client_cli/globals/Globals.go
+++ b/src/client_cli/globals/Globals.go
@@ -5,8 +5,8 @@ import (
 )
 
 type Globals struct {
-	Host string
 	Hostname string
+	Host string
 	PubCert string
 	Client *resty.Client
 }
@@ -17,7 +17,7 @@ func GetInstance() *Globals {
 	return globals
 }
 
-func Init(hostname string, host string, pubCert string, client *resty.Client) *Globals {
-	return &Globals{hostname, host, pubCert, client}
+func Init(hostname string, host string, pubCert string, client *resty.Client) {
+	globals = &Globals{hostname, host, pubCert, client}
 }