From b5aa4f9a7d82d463f4f064c63b7770fc27b7a4a7 Mon Sep 17 00:00:00 2001 From: Liang Ding Date: Thu, 16 May 2019 23:17:25 +0800 Subject: [PATCH] :building_construction: #347 --- .gitignore | 2 - conf/user.go | 60 ++++------- conf/users/admin.json | 55 ---------- conf/wide.go | 33 +++--- editor/editors.go | 20 ++-- file/files.go | 48 ++++----- go.mod | 7 ++ go.sum | 26 +++++ main.go | 32 +++--- notification/notifications.go | 4 +- output/build.go | 8 +- output/cross.go | 10 +- output/get.go | 12 +-- output/install.go | 10 +- output/outputs.go | 4 +- output/processes.go | 4 +- output/run.go | 2 +- output/test.go | 12 +-- output/vet.go | 12 +-- playground/playgrounds.go | 6 +- playground/run.go | 2 +- session/oauthctl.go | 143 ++++++++++++++++++++++++++ session/sessions.go | 38 +++---- session/users.go | 185 ++++------------------------------ static/user/admin/README.md | 1 - util/ret.go | 6 +- 26 files changed, 348 insertions(+), 394 deletions(-) delete mode 100644 conf/users/admin.json create mode 100644 session/oauthctl.go delete mode 100644 static/user/admin/README.md diff --git a/.gitignore b/.gitignore index 3e433b3..7681c88 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,6 @@ /wide.exe /wide -/static/user/admin/style.css - /header /header.exe diff --git a/conf/user.go b/conf/user.go index f54d478..cdcf89e 100644 --- a/conf/user.go +++ b/conf/user.go @@ -15,9 +15,6 @@ package conf import ( - "crypto/md5" - "crypto/sha1" - "encoding/hex" "encoding/json" "io/ioutil" "os" @@ -25,8 +22,6 @@ import ( "regexp" "strings" "time" - - "github.com/b3log/wide/util" ) // Panel represents a UI panel. @@ -52,11 +47,9 @@ type LatestSessionContent struct { // User configuration. type User struct { + Id string Name string - Password string - Salt string - Email string - Gravatar string // see http://gravatar.com + Avatar string Workspace string // the GOPATH of this user (maybe contain several paths splitted by os.PathListSeparator) Locale string GoFormat string @@ -83,27 +76,6 @@ type editor struct { TabSize string } -// NewUser creates a user with the specified username, password, email and workspace. -func NewUser(username, password, email, workspace string) *User { - md5hash := md5.New() - md5hash.Write([]byte(email)) - gravatar := hex.EncodeToString(md5hash.Sum(nil)) - - salt := util.Rand.String(16) - password = Salt(password, salt) - - now := time.Now().UnixNano() - - return &User{Name: username, Password: password, Salt: salt, Email: email, Gravatar: gravatar, Workspace: workspace, - Locale: Wide.Locale, GoFormat: "gofmt", - GoBuildArgsForLinux: "-i", GoBuildArgsForWindows: "-i", GoBuildArgsForDarwin: "-i", - FontFamily: "Helvetica", FontSize: "13px", Theme: "default", - Keymap: "wide", - Created: now, Updated: now, Lived: now, - Editor: &editor{FontFamily: "Consolas, 'Courier New', monospace", FontSize: "inherit", LineHeight: "17px", - Theme: "wide", TabSize: "4"}} -} - // Save saves the user's configurations in conf/users/{username}.json. func (u *User) Save() bool { bytes, err := json.MarshalIndent(u, "", " ") @@ -115,12 +87,12 @@ func (u *User) Save() bool { } if "" == string(bytes) { - logger.Error("Truncated user [" + u.Name + "]") + logger.Error("Truncated user [" + u.Id + "]") return false } - if err = ioutil.WriteFile(filepath.Join(Wide.Users, u.Name+".json"), bytes, 0644); nil != err { + if err = ioutil.WriteFile(filepath.Join(Wide.Users, u.Id+".json"), bytes, 0644); nil != err { logger.Error(err) return false @@ -129,6 +101,20 @@ func (u *User) Save() bool { return true } +// NewUser creates a user with the specified username and workspace. +func NewUser(id, name, avatar, workspace string) *User { + now := time.Now().UnixNano() + + return &User{Id: id, Name: name, Avatar: avatar, Workspace: workspace, + Locale: Wide.Locale, GoFormat: "gofmt", + GoBuildArgsForLinux: "-i", GoBuildArgsForWindows: "-i", GoBuildArgsForDarwin: "-i", + FontFamily: "Helvetica", FontSize: "13px", Theme: "default", + Keymap: "wide", + Created: now, Updated: now, Lived: now, + Editor: &editor{FontFamily: "Consolas, 'Courier New', monospace", FontSize: "inherit", LineHeight: "17px", + Theme: "wide", TabSize: "4"}} +} + // WorkspacePath gets workspace path of the user. // // Compared to the use of Wide.Workspace, this function will be processed as follows: @@ -168,17 +154,9 @@ func (u *User) BuildArgs(os string) []string { func GetOwner(path string) string { for _, user := range Users { if strings.HasPrefix(path, user.WorkspacePath()) { - return user.Name + return user.Id } } return "" } - -// Salt salts the specified password with the specified salt. -func Salt(password, salt string) string { - sha1hash := sha1.New() - sha1hash.Write([]byte(password + salt)) - - return hex.EncodeToString(sha1hash.Sum(nil)) -} diff --git a/conf/users/admin.json b/conf/users/admin.json deleted file mode 100644 index 99e0f79..0000000 --- a/conf/users/admin.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "Name": "admin", - "Password": "d1bfca21893c908e64fabda01d71294b1ccdcaa7", - "Salt": "dnoyeb", - "Email": "", - "Gravatar": "d41d8cd98f00b204e9800998ecf8427e", - "Workspace": "${GOPATH}", - "Locale": "en_US", - "GoFormat": "gofmt", - "GoBuildArgsForLinux": "-i", - "GoBuildArgsForWindows": "-i", - "GoBuildArgsForDarwin": "-i", - "FontFamily": "Helvetica", - "FontSize": "13px", - "Theme": "default", - "Keymap": "wide", - "Created": 1414080000000000000, - "Updated": 1492963306222604966, - "Lived": 1492963306222604966, - "Editor": { - "FontFamily": "Consolas, 'Courier New', monospace", - "FontSize": "13px", - "LineHeight": "17px", - "Theme": "wide", - "TabSize": "4" - }, - "LatestSessionContent": { - "fileTree": [ - "/Users/Vanessa/Work/Code/GoGoGo/src", - "/Users/Vanessa/Work/Code/GoGoGo/src/github.com", - "/Users/Vanessa/Work/Code/GoGoGo/src/github.com/b3log", - "/Users/Vanessa/Work/Code/GoGoGo/src/github.com/b3log/wide", - "/Users/Vanessa/Work/Code/GoGoGo/src/github.com/b3log/wide/.idea", - "/Users/Vanessa/Work/Code/GoGoGo/src/github.com/b3log/wide/static", - "/Users/Vanessa/Work/Code/GoGoGo/src/github.com/b3log/wide/static/css", - "/Users/Vanessa/Work/Code/GoGoGo/src/github.com/b3log/wide/static/js" - ], - "files": [], - "currentFile": "", - "layout": { - "side": { - "state": "normal", - "size": 200 - }, - "sideRight": { - "state": "normal", - "size": 200 - }, - "bottom": { - "state": "normal", - "size": 100 - } - } - } -} \ No newline at end of file diff --git a/conf/wide.go b/conf/wide.go index 6b89d7e..88d33cd 100644 --- a/conf/wide.go +++ b/conf/wide.go @@ -42,6 +42,8 @@ const ( WideVersion = "1.5.3" // CodeMirrorVer holds the current editor version. CodeMirrorVer = "5.1" + // UserAgent represents HTTP client user agent. + UserAgent = "Wide/" + WideVersion + "; +https://github.com/b3log/wide" HelloWorld = `package main @@ -70,7 +72,7 @@ type conf struct { Locale string // default locale Playground string // playground directory Users string // users directory - UsersWorkspaces string // users' workspaces directory (admin defaults to ${GOPATH}, others using this) + UsersWorkspaces string // users' workspaces directory AllowRegister bool // allow register or not Autocomplete bool // default autocomplete } @@ -335,10 +337,10 @@ func checkEnv() { } } -// GetUserWorkspace gets workspace path with the specified username, returns "" if not found. -func GetUserWorkspace(username string) string { +// GetUserWorkspace gets workspace path with the specified user id, returns "" if not found. +func GetUserWorkspace(userId string) string { for _, user := range Users { - if user.Name == username { + if user.Id == userId { return user.WorkspacePath() } } @@ -347,9 +349,9 @@ func GetUserWorkspace(username string) string { } // GetGoFmt gets the path of Go format tool, returns "gofmt" if not found "goimports". -func GetGoFmt(username string) string { +func GetGoFmt(userId string) string { for _, user := range Users { - if user.Name == username { + if user.Id == userId { switch user.GoFormat { case "gofmt": return "gofmt" @@ -365,15 +367,14 @@ func GetGoFmt(username string) string { return "gofmt" } -// GetUser gets configuration of the user specified by the given username, returns nil if not found. -func GetUser(username string) *User { - if "playground" == username { // reserved user for Playground - // mock it - return NewUser("playground", "", "", "") +// GetUser gets configuration of the user specified by the given user id, returns nil if not found. +func GetUser(id string) *User { + if "playground" == id { // reserved user for Playground + return NewUser("playground", "playground", "", "") } for _, user := range Users { - if user.Name == username { + if user.Id == id { return user } } @@ -384,17 +385,17 @@ func GetUser(username string) *User { // initCustomizedConfs initializes the user customized configurations. func initCustomizedConfs() { for _, user := range Users { - UpdateCustomizedConf(user.Name) + UpdateCustomizedConf(user.Id) } } // UpdateCustomizedConf creates (if not exists) or updates user customized configuration files. // // 1. /static/user/{username}/style.css -func UpdateCustomizedConf(username string) { +func UpdateCustomizedConf(userId string) { var u *User for _, user := range Users { // maybe it is a beauty of the trade-off of the another world between design and implementation - if user.Name == username { + if user.Id == userId { u = user } } @@ -413,7 +414,7 @@ func UpdateCustomizedConf(username string) { } wd := util.OS.Pwd() - dir := filepath.Clean(wd + "/static/user/" + u.Name) + dir := filepath.Clean(wd + "/static/user/" + u.Id) if err := os.MkdirAll(dir, 0755); nil != err { logger.Error(err) diff --git a/editor/editors.go b/editor/editors.go index 74ebd5a..0a0dcd8 100644 --- a/editor/editors.go +++ b/editor/editors.go @@ -117,7 +117,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) { return } - username := session.Values["username"].(string) + uid := session.Values["uid"].(string) path := args["path"].(string) @@ -147,7 +147,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) { logger.Tracef("offset: %d", offset) - userWorkspace := conf.GetUserWorkspace(username) + userWorkspace := conf.GetUserWorkspace(uid) workspaces := filepath.SplitList(userWorkspace) libPath := "" for _, workspace := range workspaces { @@ -183,7 +183,7 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) { defer util.RetResult(w, r, result) session, _ := session.HTTPSession.Get(r, "wide-session") - username := session.Values["username"].(string) + uid := session.Values["uid"].(string) var args map[string]interface{} if err := json.NewDecoder(r.Body).Decode(&args); err != nil { @@ -228,7 +228,7 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) { cmd := exec.Command(ideStub, argv...) cmd.Dir = curDir - setCmdEnv(cmd, username) + setCmdEnv(cmd, uid) output, err := cmd.CombinedOutput() if nil != err { @@ -259,7 +259,7 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) { return } - username := session.Values["username"].(string) + uid := session.Values["uid"].(string) var args map[string]interface{} if err := json.NewDecoder(r.Body).Decode(&args); err != nil { @@ -304,7 +304,7 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) { cmd := exec.Command(ideStub, argv...) cmd.Dir = curDir - setCmdEnv(cmd, username) + setCmdEnv(cmd, uid) output, err := cmd.CombinedOutput() if nil != err { @@ -347,7 +347,7 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) { return } - username := session.Values["username"].(string) + uid := session.Values["uid"].(string) var args map[string]interface{} @@ -392,7 +392,7 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) { cmd := exec.Command(ideStub, argv...) cmd.Dir = curDir - setCmdEnv(cmd, username) + setCmdEnv(cmd, uid) output, err := cmd.CombinedOutput() if nil != err { @@ -453,8 +453,8 @@ func getCursorOffset(code string, line, ch int) (offset int) { return offset } -func setCmdEnv(cmd *exec.Cmd, username string) { - userWorkspace := conf.GetUserWorkspace(username) +func setCmdEnv(cmd *exec.Cmd, userId string) { + userWorkspace := conf.GetUserWorkspace(userId) cmd.Env = append(cmd.Env, "GOPATH="+userWorkspace, diff --git a/file/files.go b/file/files.go index 64a0738..06b5c5c 100644 --- a/file/files.go +++ b/file/files.go @@ -80,12 +80,12 @@ func GetFilesHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) + uid := httpSession.Values["uid"].(string) result := util.NewResult() defer util.RetGzResult(w, r, result) - userWorkspace := conf.GetUserWorkspace(username) + userWorkspace := conf.GetUserWorkspace(uid) workspaces := filepath.SplitList(userWorkspace) root := Node{Name: "root", Path: "", IconSkin: "ico-ztree-dir ", Type: "d", IsParent: true, Children: []*Node{}} @@ -129,12 +129,12 @@ func RefreshDirectoryHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) + uid := httpSession.Values["uid"].(string) r.ParseForm() path := r.FormValue("path") - if !util.Go.IsAPI(path) && !session.CanAccess(username, path) { + if !util.Go.IsAPI(path) && !session.CanAccess(uid, path) { http.Error(w, "Forbidden", http.StatusForbidden) return @@ -162,7 +162,7 @@ func GetFileHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) + uid := httpSession.Values["uid"].(string) result := util.NewResult() defer util.RetResult(w, r, result) @@ -178,7 +178,7 @@ func GetFileHandler(w http.ResponseWriter, r *http.Request) { path := args["path"].(string) - if !util.Go.IsAPI(path) && !session.CanAccess(username, path) { + if !util.Go.IsAPI(path) && !session.CanAccess(uid, path) { http.Error(w, "Forbidden", http.StatusForbidden) return @@ -212,7 +212,7 @@ func GetFileHandler(w http.ResponseWriter, r *http.Request) { return } - user := conf.GetUser(username) + user := conf.GetUser(uid) data["path"] = "/workspace/" + user.Name + "/" + strings.Replace(path, user.WorkspacePath(), "", 1) @@ -238,7 +238,7 @@ func SaveFileHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) + uid := httpSession.Values["uid"].(string) result := util.NewResult() defer util.RetResult(w, r, result) @@ -255,7 +255,7 @@ func SaveFileHandler(w http.ResponseWriter, r *http.Request) { filePath := args["file"].(string) sid := args["sid"].(string) - if util.Go.IsAPI(filePath) || !session.CanAccess(username, filePath) { + if util.Go.IsAPI(filePath) || !session.CanAccess(uid, filePath) { http.Error(w, "Forbidden", http.StatusForbidden) return @@ -294,7 +294,7 @@ func NewFileHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) + uid := httpSession.Values["uid"].(string) result := util.NewResult() defer util.RetResult(w, r, result) @@ -310,7 +310,7 @@ func NewFileHandler(w http.ResponseWriter, r *http.Request) { path := args["path"].(string) - if util.Go.IsAPI(path) || !session.CanAccess(username, path) { + if util.Go.IsAPI(path) || !session.CanAccess(uid, path) { http.Error(w, "Forbidden", http.StatusForbidden) return @@ -331,9 +331,9 @@ func NewFileHandler(w http.ResponseWriter, r *http.Request) { } if "f" == fileType { - logger.Debugf("Created a file [%s] by user [%s]", path, wSession.Username) + logger.Debugf("Created a file [%s] by user [%s]", path, wSession.UserId) } else { - logger.Debugf("Created a dir [%s] by user [%s]", path, wSession.Username) + logger.Debugf("Created a dir [%s] by user [%s]", path, wSession.UserId) } } @@ -346,7 +346,7 @@ func RemoveFileHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) + uid := httpSession.Values["uid"].(string) result := util.NewResult() defer util.RetResult(w, r, result) @@ -362,7 +362,7 @@ func RemoveFileHandler(w http.ResponseWriter, r *http.Request) { path := args["path"].(string) - if util.Go.IsAPI(path) || !session.CanAccess(username, path) { + if util.Go.IsAPI(path) || !session.CanAccess(uid, path) { http.Error(w, "Forbidden", http.StatusForbidden) return @@ -381,7 +381,7 @@ func RemoveFileHandler(w http.ResponseWriter, r *http.Request) { return } - logger.Debugf("Removed a file [%s] by user [%s]", path, wSession.Username) + logger.Debugf("Removed a file [%s] by user [%s]", path, wSession.UserId) } // RenameFileHandler handles request of renaming file or directory. @@ -392,7 +392,7 @@ func RenameFileHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) + uid := httpSession.Values["uid"].(string) result := util.NewResult() defer util.RetResult(w, r, result) @@ -408,14 +408,14 @@ func RenameFileHandler(w http.ResponseWriter, r *http.Request) { oldPath := args["oldPath"].(string) if util.Go.IsAPI(oldPath) || - !session.CanAccess(username, oldPath) { + !session.CanAccess(uid, oldPath) { http.Error(w, "Forbidden", http.StatusForbidden) return } newPath := args["newPath"].(string) - if util.Go.IsAPI(newPath) || !session.CanAccess(username, newPath) { + if util.Go.IsAPI(newPath) || !session.CanAccess(uid, newPath) { http.Error(w, "Forbidden", http.StatusForbidden) return @@ -434,7 +434,7 @@ func RenameFileHandler(w http.ResponseWriter, r *http.Request) { return } - logger.Debugf("Renamed a file [%s] to [%s] by user [%s]", oldPath, newPath, wSession.Username) + logger.Debugf("Renamed a file [%s] to [%s] by user [%s]", oldPath, newPath, wSession.UserId) } // Use to find results sorting. @@ -457,7 +457,7 @@ func FindHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) + uid := httpSession.Values["uid"].(string) result := util.NewResult() defer util.RetResult(w, r, result) @@ -471,7 +471,7 @@ func FindHandler(w http.ResponseWriter, r *http.Request) { } path := args["path"].(string) // path of selected file in file tree - if !util.Go.IsAPI(path) && !session.CanAccess(username, path) { + if !util.Go.IsAPI(path) && !session.CanAccess(uid, path) { http.Error(w, "Forbidden", http.StatusForbidden) return @@ -479,7 +479,7 @@ func FindHandler(w http.ResponseWriter, r *http.Request) { name := args["name"].(string) - userWorkspace := conf.GetUserWorkspace(username) + userWorkspace := conf.GetUserWorkspace(uid) workspaces := filepath.SplitList(userWorkspace) if "" != path && !util.File.IsDir(path) { @@ -536,7 +536,7 @@ func SearchTextHandler(w http.ResponseWriter, r *http.Request) { dir := args["dir"].(string) if "" == dir { - userWorkspace := conf.GetUserWorkspace(wSession.Username) + userWorkspace := conf.GetUserWorkspace(wSession.UserId) workspaces := filepath.SplitList(userWorkspace) dir = workspaces[0] } diff --git a/go.mod b/go.mod index ebe4db5..7fb6aa7 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,19 @@ module github.com/b3log/wide go 1.12 require ( + github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect + github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect github.com/fsnotify/fsnotify v1.4.7 github.com/gorilla/context v0.0.0-20141217160251-215affda49ad // indirect github.com/gorilla/securecookie v0.0.0-20150327155805-8e98dd730fc4 // indirect github.com/gorilla/sessions v0.0.0-20150417174705-f61c3ec2cf65 github.com/gorilla/websocket v0.0.0-20150530030352-a3ec486e6a7a github.com/hashicorp/go-version v1.2.0 + github.com/moul/http2curl v1.0.0 // indirect + github.com/parnurzeal/gorequest v0.2.15 + github.com/pkg/errors v0.8.1 // indirect + github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect + golang.org/x/net v0.0.0-20190514140710-3ec191127204 // indirect golang.org/x/sys v0.0.0-20190515190549-87c872767d25 // indirect golang.org/x/text v0.3.2 ) diff --git a/go.sum b/go.sum index 6508426..d104a27 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,11 @@ +github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f h1:8GDPb0tCY8LQ+OJ3dbHb5sA6YZWXFORQYZx5sdsTlMs= +github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f h1:AUj1VoZUfhPhOPHULCQQDnGhRelpFWHMLhQVWDsS0v4= +github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v0.0.0-20141217160251-215affda49ad h1:wJwKN6X6iRRVnjdBgrkWjhBOvYm7yw5boqXwFUnBtbE= github.com/gorilla/context v0.0.0-20141217160251-215affda49ad/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/securecookie v0.0.0-20150327155805-8e98dd730fc4 h1:X0DbEdoaUJT+NZ8mLHRNMSLmogzqLhsA1Eh6gs7Y7Zg= @@ -10,8 +16,28 @@ github.com/gorilla/websocket v0.0.0-20150530030352-a3ec486e6a7a h1:p/PGT+3UGSK7e github.com/gorilla/websocket v0.0.0-20150530030352-a3ec486e6a7a/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= +github.com/parnurzeal/gorequest v0.2.15 h1:oPjDCsF5IkD4gUk6vIgsxYNaSgvAnIh1EJeROn3HdJU= +github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190514140710-3ec191127204 h1:4yG6GqBtw9C+UrLp6s2wtSniayy/Vd/3F7ffLE427XI= +golang.org/x/net v0.0.0-20190514140710-3ec191127204/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190515190549-87c872767d25 h1:SSKQq5sjDoW0L0NaDoVl7d7HmtTxM0ezm0Ef9azs4uQ= golang.org/x/sys v0.0.0-20190515190549-87c872767d25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= diff --git a/main.go b/main.go index 8f0332f..f0bb7aa 100644 --- a/main.go +++ b/main.go @@ -158,9 +158,7 @@ func main() { http.HandleFunc(conf.Wide.Context+"/notification/ws", handlerWrapper(notification.WSHandler)) // user - http.HandleFunc(conf.Wide.Context+"/login", handlerWrapper(session.LoginHandler)) http.HandleFunc(conf.Wide.Context+"/logout", handlerWrapper(session.LogoutHandler)) - http.HandleFunc(conf.Wide.Context+"/signup", handlerWrapper(session.SignUpUserHandler)) http.HandleFunc(conf.Wide.Context+"/preference", handlerWrapper(session.PreferenceHandler)) // playground @@ -197,8 +195,8 @@ func indexHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) - if "playground" == username { // reserved user for Playground + uid := httpSession.Values["uid"].(string) + if "playground" == uid { // reserved user for Playground http.Redirect(w, r, conf.Wide.Context+"/login", http.StatusFound) return @@ -210,9 +208,9 @@ func indexHandler(w http.ResponseWriter, r *http.Request) { } httpSession.Save(r, w) - user := conf.GetUser(username) + user := conf.GetUser(uid) if nil == user { - logger.Warnf("Not found user [%s]", username) + logger.Warnf("Not found user [%s]", uid) http.Redirect(w, r, conf.Wide.Context+"/login", http.StatusFound) @@ -221,14 +219,14 @@ func indexHandler(w http.ResponseWriter, r *http.Request) { locale := user.Locale - wideSessions := session.WideSessions.GetByUsername(username) + wideSessions := session.WideSessions.GetByUserId(uid) model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale, - "username": username, "sid": session.WideSessions.GenId(), "latestSessionContent": user.LatestSessionContent, + "uid": uid, "sid": session.WideSessions.GenId(), "latestSessionContent": user.LatestSessionContent, "pathSeparator": conf.PathSeparator, "codeMirrorVer": conf.CodeMirrorVer, "user": user, "editorThemes": conf.GetEditorThemes(), "crossPlatforms": util.Go.GetCrossPlatforms()} - logger.Debugf("User [%s] has [%d] sessions", username, len(wideSessions)) + logger.Debugf("User [%s] has [%d] sessions", uid, len(wideSessions)) t, err := template.ParseFiles("views/index.html") if nil != err { @@ -279,9 +277,9 @@ func startHandler(w http.ResponseWriter, r *http.Request) { } httpSession.Save(r, w) - username := httpSession.Values["username"].(string) - locale := conf.GetUser(username).Locale - userWorkspace := conf.GetUserWorkspace(username) + uid := httpSession.Values["uid"].(string) + locale := conf.GetUser(uid).Locale + userWorkspace := conf.GetUserWorkspace(uid) sid := r.URL.Query()["sid"][0] wSession := session.WideSessions.Get(sid) @@ -290,7 +288,7 @@ func startHandler(w http.ResponseWriter, r *http.Request) { } model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale, - "username": username, "workspace": userWorkspace, "ver": conf.WideVersion, "sid": sid} + "uid": uid, "workspace": userWorkspace, "ver": conf.WideVersion, "sid": sid} t, err := template.ParseFiles("views/start.html") @@ -319,8 +317,8 @@ func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) { } httpSession.Save(r, w) - username := httpSession.Values["username"].(string) - locale := conf.GetUser(username).Locale + uid := httpSession.Values["uid"].(string) + locale := conf.GetUser(uid).Locale model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale} @@ -351,8 +349,8 @@ func aboutHandler(w http.ResponseWriter, r *http.Request) { } httpSession.Save(r, w) - username := httpSession.Values["username"].(string) - locale := conf.GetUser(username).Locale + uid := httpSession.Values["uid"].(string) + locale := conf.GetUser(uid).Locale model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale, "ver": conf.WideVersion, "goos": runtime.GOOS, "goarch": runtime.GOARCH, "gover": runtime.Version()} diff --git a/notification/notifications.go b/notification/notifications.go index 527509e..d350cb1 100644 --- a/notification/notifications.go +++ b/notification/notifications.go @@ -63,8 +63,8 @@ func event2Notification(e *event.Event) { } httpSession, _ := session.HTTPSession.Get(wsChannel.Request, "wide-session") - username := httpSession.Values["username"].(string) - locale := conf.GetUser(username).Locale + uid := httpSession.Values["uid"].(string) + locale := conf.GetUser(uid).Locale var notification *Notification diff --git a/output/build.go b/output/build.go index 4475f58..442f20f 100644 --- a/output/build.go +++ b/output/build.go @@ -44,8 +44,8 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) - user := conf.GetUser(username) + uid := httpSession.Values["uid"].(string) + user := conf.GetUser(uid) locale := user.Locale var args map[string]interface{} @@ -61,7 +61,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) { filePath := args["file"].(string) - if util.Go.IsAPI(filePath) || !session.CanAccess(username, filePath) { + if util.Go.IsAPI(filePath) || !session.CanAccess(uid, filePath) { http.Error(w, "Forbidden", http.StatusForbidden) return @@ -101,7 +101,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) { cmd := exec.Command("go", goBuildArgs...) cmd.Dir = curDir - setCmdEnv(cmd, username) + setCmdEnv(cmd, uid) executable := filepath.Base(curDir) + suffix executable = filepath.Join(curDir, executable) diff --git a/output/cross.go b/output/cross.go index 07cf7cd..1db55b9 100644 --- a/output/cross.go +++ b/output/cross.go @@ -43,8 +43,8 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) - locale := conf.GetUser(username).Locale + uid := httpSession.Values["uid"].(string) + locale := conf.GetUser(uid).Locale var args map[string]interface{} @@ -58,7 +58,7 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) { sid := args["sid"].(string) filePath := args["path"].(string) - if util.Go.IsAPI(filePath) || !session.CanAccess(username, filePath) { + if util.Go.IsAPI(filePath) || !session.CanAccess(uid, filePath) { http.Error(w, "Forbidden", http.StatusForbidden) return @@ -75,7 +75,7 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) { suffix = ".exe" } - user := conf.GetUser(username) + user := conf.GetUser(uid) goBuildArgs := []string{} goBuildArgs = append(goBuildArgs, "build") goBuildArgs = append(goBuildArgs, user.BuildArgs(goos)...) @@ -83,7 +83,7 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) { cmd := exec.Command("go", goBuildArgs...) cmd.Dir = curDir - setCmdEnv(cmd, username) + setCmdEnv(cmd, uid) for i, env := range cmd.Env { if strings.HasPrefix(env, "GOOS=") { diff --git a/output/get.go b/output/get.go index 0aadea3..5585e2f 100644 --- a/output/get.go +++ b/output/get.go @@ -41,8 +41,8 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) - locale := conf.GetUser(username).Locale + uid := httpSession.Values["uid"].(string) + locale := conf.GetUser(uid).Locale var args map[string]interface{} if err := json.NewDecoder(r.Body).Decode(&args); err != nil { @@ -60,7 +60,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) { cmd := exec.Command("go", "get") cmd.Dir = curDir - setCmdEnv(cmd, username) + setCmdEnv(cmd, uid) stdout, err := cmd.StdoutPipe() if nil != err { @@ -114,7 +114,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) { defer util.Recover() defer cmd.Wait() - logger.Debugf("User [%s, %s] is running [go get] [runningId=%d]", username, sid, runningId) + logger.Debugf("User [%s, %s] is running [go get] [runningId=%d]", uid, sid, runningId) channelRet := map[string]interface{}{} channelRet["cmd"] = "go get" @@ -123,11 +123,11 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) { buf, _ := ioutil.ReadAll(reader) if 0 != len(buf) { - logger.Debugf("User [%s, %s] 's [go get] [runningId=%d] has done (with error)", username, sid, runningId) + logger.Debugf("User [%s, %s] 's [go get] [runningId=%d] has done (with error)", uid, sid, runningId) channelRet["output"] = "" + i18n.Get(locale, "get-error").(string) + "\n" + string(buf) } else { - logger.Debugf("User [%s, %s] 's running [go get] [runningId=%d] has done", username, sid, runningId) + logger.Debugf("User [%s, %s] 's running [go get] [runningId=%d] has done", uid, sid, runningId) channelRet["output"] = "" + i18n.Get(locale, "get-succ").(string) + "\n" } diff --git a/output/install.go b/output/install.go index 0a2db71..c789427 100644 --- a/output/install.go +++ b/output/install.go @@ -43,8 +43,8 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) - locale := conf.GetUser(username).Locale + uid := httpSession.Values["uid"].(string) + locale := conf.GetUser(uid).Locale var args map[string]interface{} @@ -63,7 +63,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) { cmd := exec.Command("go", "install") cmd.Dir = curDir - setCmdEnv(cmd, username) + setCmdEnv(cmd, uid) logger.Debugf("go install %s", curDir) @@ -119,7 +119,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) { defer util.Recover() defer cmd.Wait() - logger.Debugf("User [%s, %s] is running [go install] [id=%d, dir=%s]", username, sid, runningId, curDir) + logger.Debugf("User [%s, %s] is running [go install] [id=%d, dir=%s]", uid, sid, runningId, curDir) // read all buf, _ := ioutil.ReadAll(reader) @@ -183,7 +183,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) { } if nil != session.OutputWS[sid] { - logger.Debugf("User [%s, %s] 's running [go install] [id=%d, dir=%s] has done", username, sid, runningId, curDir) + logger.Debugf("User [%s, %s] 's running [go install] [id=%d, dir=%s] has done", uid, sid, runningId, curDir) wsChannel := session.OutputWS[sid] err := wsChannel.WriteJSON(&channelRet) diff --git a/output/outputs.go b/output/outputs.go index 6fcb593..e5c9a1d 100644 --- a/output/outputs.go +++ b/output/outputs.go @@ -104,8 +104,8 @@ func parsePath(curDir, outputLine string) string { return tagStart + text + tagEnd + msgPart } -func setCmdEnv(cmd *exec.Cmd, username string) { - userWorkspace := conf.GetUserWorkspace(username) +func setCmdEnv(cmd *exec.Cmd, uid string) { + userWorkspace := conf.GetUserWorkspace(uid) cache, err := os.UserCacheDir() if nil != err { logger.Warnf("Get user cache dir failed [" + err.Error() + "]") diff --git a/output/processes.go b/output/processes.go index 552eff8..6926c90 100644 --- a/output/processes.go +++ b/output/processes.go @@ -86,7 +86,7 @@ func (procs *procs) Kill(wSession *session.WideSession, pid int) { for i, p := range userProcesses { if p.Pid == pid { if err := p.Kill(); nil != err { - logger.Errorf("Kill a process [pid=%d] of user [%s, %s] failed [error=%v]", pid, wSession.Username, sid, err) + logger.Errorf("Kill a process [pid=%d] of user [%s, %s] failed [error=%v]", pid, wSession.UserId, sid, err) } else { var newProcesses []*os.Process @@ -96,7 +96,7 @@ func (procs *procs) Kill(wSession *session.WideSession, pid int) { // bind process with wide session wSession.SetProcesses(newProcesses) - logger.Debugf("Killed a process [pid=%d] of user [%s, %s]", pid, wSession.Username, sid) + logger.Debugf("Killed a process [pid=%d] of user [%s, %s]", pid, wSession.UserId, sid) } return diff --git a/output/run.go b/output/run.go index 036a844..ca4194e 100644 --- a/output/run.go +++ b/output/run.go @@ -106,7 +106,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) { go func(runningId int) { defer util.Recover() - logger.Debugf("User [%s, %s] is running [id=%d, file=%s]", wSession.Username, sid, runningId, filePath) + logger.Debugf("User [%s, %s] is running [id=%d, file=%s]", wSession.UserId, sid, runningId, filePath) go func() { defer util.Recover() diff --git a/output/test.go b/output/test.go index e57aab1..3b30de6 100644 --- a/output/test.go +++ b/output/test.go @@ -41,8 +41,8 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) - locale := conf.GetUser(username).Locale + uid := httpSession.Values["uid"].(string) + locale := conf.GetUser(uid).Locale var args map[string]interface{} @@ -61,7 +61,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) { cmd := exec.Command("go", "test", "-v") cmd.Dir = curDir - setCmdEnv(cmd, username) + setCmdEnv(cmd, uid) stdout, err := cmd.StdoutPipe() if nil != err { @@ -114,7 +114,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) { go func(runningId int) { defer util.Recover() - logger.Debugf("User [%s, %s] is running [go test] [runningId=%d]", username, sid, runningId) + logger.Debugf("User [%s, %s] is running [go test] [runningId=%d]", uid, sid, runningId) channelRet := map[string]interface{}{} channelRet["cmd"] = "go test" @@ -126,11 +126,11 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) { cmd.Wait() if !cmd.ProcessState.Success() { - logger.Debugf("User [%s, %s] 's running [go test] [runningId=%d] has done (with error)", username, sid, runningId) + logger.Debugf("User [%s, %s] 's running [go test] [runningId=%d] has done (with error)", uid, sid, runningId) channelRet["output"] = "" + i18n.Get(locale, "test-error").(string) + "\n" + string(buf) } else { - logger.Debugf("User [%s, %s] 's running [go test] [runningId=%d] has done", username, sid, runningId) + logger.Debugf("User [%s, %s] 's running [go test] [runningId=%d] has done", uid, sid, runningId) channelRet["output"] = "" + i18n.Get(locale, "test-succ").(string) + "\n" + string(buf) } diff --git a/output/vet.go b/output/vet.go index 750eaac..e199cac 100644 --- a/output/vet.go +++ b/output/vet.go @@ -41,8 +41,8 @@ func GoVetHandler(w http.ResponseWriter, r *http.Request) { return } - username := httpSession.Values["username"].(string) - locale := conf.GetUser(username).Locale + uid := httpSession.Values["uid"].(string) + locale := conf.GetUser(uid).Locale var args map[string]interface{} @@ -61,7 +61,7 @@ func GoVetHandler(w http.ResponseWriter, r *http.Request) { cmd := exec.Command("go", "vet", ".") cmd.Dir = curDir - setCmdEnv(cmd, username) + setCmdEnv(cmd, uid) stdout, err := cmd.StdoutPipe() if nil != err { @@ -114,7 +114,7 @@ func GoVetHandler(w http.ResponseWriter, r *http.Request) { go func(runningId int) { defer util.Recover() - logger.Debugf("User [%s, %s] is running [go vet] [runningId=%d]", username, sid, runningId) + logger.Debugf("User [%s, %s] is running [go vet] [runningId=%d]", uid, sid, runningId) channelRet := map[string]interface{}{} channelRet["cmd"] = "go vet" @@ -126,11 +126,11 @@ func GoVetHandler(w http.ResponseWriter, r *http.Request) { cmd.Wait() if !cmd.ProcessState.Success() { - logger.Debugf("User [%s, %s] 's running [go vet] [runningId=%d] has done (with error)", username, sid, runningId) + logger.Debugf("User [%s, %s] 's running [go vet] [runningId=%d] has done (with error)", uid, sid, runningId) channelRet["output"] = "" + i18n.Get(locale, "vet-error").(string) + "\n" + string(buf) } else { - logger.Debugf("User [%s, %s] 's running [go vet] [runningId=%d] has done", username, sid, runningId) + logger.Debugf("User [%s, %s] 's running [go vet] [runningId=%d] has done", uid, sid, runningId) channelRet["output"] = "" + i18n.Get(locale, "vet-succ").(string) + "\n" + string(buf) } diff --git a/playground/playgrounds.go b/playground/playgrounds.go index ef63a20..515ae4f 100644 --- a/playground/playgrounds.go +++ b/playground/playgrounds.go @@ -52,7 +52,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { } httpSession.Save(r, w) - username := httpSession.Values["username"].(string) + uid := httpSession.Values["uid"].(string) locale := conf.Wide.Locale @@ -92,9 +92,9 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { "code": template.HTML(code), "ver": conf.WideVersion, "year": time.Now().Year(), "embed": embed, "disqus": disqus, "fileName": fileName} - wideSessions := session.WideSessions.GetByUsername(username) + wideSessions := session.WideSessions.GetByUserId(uid) - logger.Debugf("User [%s] has [%d] sessions", username, len(wideSessions)) + logger.Debugf("User [%s] has [%d] sessions", uid, len(wideSessions)) t, err := template.ParseFiles("views/playground/index.html") diff --git a/playground/run.go b/playground/run.go index ab5ae54..d9b630e 100644 --- a/playground/run.go +++ b/playground/run.go @@ -108,7 +108,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) { go func(runningId int) { defer util.Recover() - logger.Debugf("User [%s, %s] is running [id=%d, file=%s]", wSession.Username, sid, runningId, filePath) + logger.Debugf("User [%s, %s] is running [id=%d, file=%s]", wSession.UserId, sid, runningId, filePath) go func() { defer util.Recover() diff --git a/session/oauthctl.go b/session/oauthctl.go new file mode 100644 index 0000000..9c43500 --- /dev/null +++ b/session/oauthctl.go @@ -0,0 +1,143 @@ +// Copyright (c) 2014-2019, b3log.org & hacpai.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package session + +import ( + "crypto/tls" + "github.com/b3log/wide/conf" + "math/rand" + "net/http" + "strconv" + "strings" + "time" + + "github.com/b3log/wide/util" + "github.com/parnurzeal/gorequest" +) + +var states = map[string]string{} + +// RedirectGitHubHandler redirects to GitHub auth page. +func RedirectGitHubHandler(w http.ResponseWriter, r *http.Request) { + requestResult := util.NewResult() + _, _, errs := gorequest.New().TLSClientConfig(&tls.Config{InsecureSkipVerify: true}). + Get("https://hacpai.com/oauth/wide/client"). + Set("user-agent", conf.UserAgent).Timeout(10 * time.Second).EndStruct(requestResult) + if nil != errs { + logger.Errorf("Get oauth client id failed: %+v", errs) + http.Error(w, "Get oauth info failed", http.StatusInternalServerError) + + return + } + if 0 != requestResult.Code { + logger.Errorf("get oauth client id failed [code=%d, msg=%s]", requestResult.Code, requestResult.Msg) + http.Error(w, "Get oauth info failed", http.StatusNotFound) + + return + } + data := requestResult.Data.(map[string]interface{}) + clientId := data["clientId"].(string) + loginAuthURL := data["loginAuthURL"].(string) + + referer := r.URL.Query().Get("referer") + if "" == referer || !strings.Contains(referer, "://") { + referer = conf.Wide.Server + referer + } + if strings.HasSuffix(referer, "/") { + referer = referer[:len(referer)-1] + } + state := util.Rand.String(16) + referer + states[state] = state + path := loginAuthURL + "?client_id=" + clientId + "&state=" + state + "&scope=public_repo,read:user,user:follow" + + logger.Infof("redirect to github [" + path + "]") + + http.Redirect(w, r, path, http.StatusSeeOther) +} + +func GithubCallbackHandler(w http.ResponseWriter, r *http.Request) { + logger.Infof("Github callback [" + r.URL.String() + "]") + + state := r.URL.Query().Get("state") + if _, exist := states[state]; !exist { + http.Error(w, "Get state param failed", http.StatusBadRequest) + + return + } + delete(states, state) + + referer := state[16:] + if strings.Contains(referer, "__0") || strings.Contains(referer, "__1") { + referer = referer[:len(referer)-len("__0")] + } + accessToken := r.URL.Query().Get("ak") + githubUser := GitHubUserInfo(accessToken) + if nil == githubUser { + logger.Warnf("Can not get user info with token [" + accessToken + "]") + http.Error(w, "Get user info failed", http.StatusUnauthorized) + + return + } + + githubId := githubUser["userId"].(string) + userName := githubUser["userName"].(string) + avatar := githubUser["userAvatar"].(string) + + result := util.NewResult() + defer util.RetResult(w, r, result) + + user := conf.GetUser(githubId) + if nil == user { + msg := addUser(githubId, userName, avatar) + if userCreated != msg { + result.Succ = false + result.Msg = msg + + return + } + } + + // create a HTTP session + httpSession, _ := HTTPSession.Get(r, "wide-session") + httpSession.Values["username"] = userName + + httpSession.Values["id"] = strconv.Itoa(rand.Int()) + httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge + if "" != conf.Wide.Context { + httpSession.Options.Path = conf.Wide.Context + } + httpSession.Save(r, w) + + logger.Debugf("Created a HTTP session [%s] for user [%s]", httpSession.Values["id"].(string), userName) +} + +// GitHubUserInfo returns GitHub user info specified by the given access token. +func GitHubUserInfo(accessToken string) (ret map[string]interface{}) { + result := map[string]interface{}{} + response, data, errors := gorequest.New().TLSClientConfig(&tls.Config{InsecureSkipVerify: true}). + Get("https://hacpai.com/github/user?ak="+accessToken).Timeout(7*time.Second). + Set("User-Agent", conf.UserAgent).EndStruct(&result) + if nil != errors || http.StatusOK != response.StatusCode { + logger.Errorf("Get github user info failed: %+v, %s", errors, data) + + return nil + } + + if 0 != result["sc"].(float64) { + return nil + } + + return result["data"].(map[string]interface{}) +} diff --git a/session/sessions.go b/session/sessions.go index a83f5f5..1813d05 100644 --- a/session/sessions.go +++ b/session/sessions.go @@ -46,7 +46,7 @@ import ( const ( sessionStateActive = iota - sessionStateClosed // (not used so far) + sessionStateClosed // (not used so far) ) // Logger. @@ -75,7 +75,7 @@ var HTTPSession = sessions.NewCookieStore([]byte("BEYOND")) // WideSession represents a session associated with a browser tab. type WideSession struct { ID string // id - Username string // username + UserId string // user id HTTPSession *sessions.Session // HTTP session related Processes []*os.Process // process set EventQueue *event.UserEventQueue // event queue @@ -111,7 +111,7 @@ func FixedTimeRelease() { for _, s := range WideSessions { if s.Updated.Before(threshold) { - logger.Debugf("Removes a invalid session [%s], user [%s]", s.ID, s.Username) + logger.Debugf("Removes a invalid session [%s], user [%s]", s.ID, s.UserId) WideSessions.Remove(s.ID) } @@ -139,7 +139,7 @@ func FixedTimeReport() { go func() { defer util.Recover() - for _ = range time.Tick(10 * time.Minute) { + for _ = range time.Tick(10*time.Minute) { users := userReports{} processSum := 0 @@ -147,7 +147,7 @@ func FixedTimeReport() { processCnt := len(s.Processes) processSum += processCnt - if report, exists := contains(users, s.Username); exists { + if report, exists := contains(users, s.UserId); exists { if s.Updated.After(report.updated) { report.updated = s.Updated } @@ -155,7 +155,7 @@ func FixedTimeReport() { report.sessionCnt++ report.processCnt += processCnt } else { - users = append(users, &userReport{username: s.Username, sessionCnt: 1, processCnt: processCnt, updated: s.Updated}) + users = append(users, &userReport{username: s.UserId, sessionCnt: 1, processCnt: processCnt, updated: s.Updated}) } } @@ -231,7 +231,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) { wSession = WideSessions.new(httpSession, sid) - logger.Tracef("Created a wide session [%s] for websocket reconnecting, user [%s]", sid, wSession.Username) + logger.Tracef("Created a wide session [%s] for websocket reconnecting, user [%s]", sid, wSession.UserId) } logger.Tracef("Open a new [Session Channel] with session [%s], %d", sid, len(SessionWS)) @@ -263,7 +263,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) { for { if err := wsChan.ReadJSON(&input); err != nil { - logger.Tracef("[Session Channel] of session [%s] disconnected, releases all resources with it, user [%s]", sid, wSession.Username) + logger.Tracef("[Session Channel] of session [%s] disconnected, releases all resources with it, user [%s]", sid, wSession.UserId) return } @@ -307,7 +307,7 @@ func SaveContentHandler(w http.ResponseWriter, r *http.Request) { wSession.Content = args.LatestSessionContent for _, user := range conf.Users { - if user.Name == wSession.Username { + if user.Id == wSession.UserId { // update the variable in-memory, session.FixedTimeSave() function will persist it periodically user.LatestSessionContent = wSession.Content @@ -376,9 +376,9 @@ func (sessions *wSessions) Remove(sid string) { // kill processes for _, p := range s.Processes { if err := p.Kill(); nil != err { - logger.Errorf("Can't kill process [%d] of session [%s], user [%s]", p.Pid, sid, s.Username) + logger.Errorf("Can't kill process [%d] of session [%s], user [%s]", p.Pid, sid, s.UserId) } else { - logger.Debugf("Killed a process [%d] of session [%s], user [%s]", p.Pid, sid, s.Username) + logger.Debugf("Killed a process [%d] of session [%s], user [%s]", p.Pid, sid, s.UserId) } } @@ -410,12 +410,12 @@ func (sessions *wSessions) Remove(sid string) { cnt := 0 // count wide sessions associated with HTTP session for _, ses := range *sessions { - if ses.Username == s.Username { + if ses.UserId == s.UserId { cnt++ } } - logger.Debugf("Removed a session [%s] of user [%s], it has [%d] sessions currently", sid, s.Username, cnt) + logger.Debugf("Removed a session [%s] of user [%s], it has [%d] sessions currently", sid, s.UserId, cnt) return } @@ -423,14 +423,14 @@ func (sessions *wSessions) Remove(sid string) { } // GetByUsername gets wide sessions. -func (sessions *wSessions) GetByUsername(username string) []*WideSession { +func (sessions *wSessions) GetByUserId(userId string) []*WideSession { mutex.Lock() defer mutex.Unlock() ret := []*WideSession{} for _, s := range *sessions { - if s.Username == username { + if s.UserId == userId { ret = append(ret, s) } } @@ -443,12 +443,12 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS mutex.Lock() defer mutex.Unlock() - username := httpSession.Values["username"].(string) + uid := httpSession.Values["uid"].(string) now := time.Now() ret := &WideSession{ ID: sid, - Username: username, + UserId: uid, HTTPSession: httpSession, EventQueue: nil, State: sessionStateActive, @@ -459,7 +459,7 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS *sessions = append(*sessions, ret) - if "playground" == username { + if "playground" == uid { return ret } @@ -526,7 +526,7 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS go func() { defer util.Recover() - workspaces := filepath.SplitList(conf.GetUserWorkspace(username)) + workspaces := filepath.SplitList(conf.GetUserWorkspace(uid)) for _, workspace := range workspaces { filepath.Walk(filepath.Join(workspace, "src"), func(dirPath string, f os.FileInfo, err error) error { if strings.HasPrefix(f.Name(), ".") || "node_modules" == f.Name() || "vendor" == f.Name() { diff --git a/session/users.go b/session/users.go index ac6c572..6f60255 100644 --- a/session/users.go +++ b/session/users.go @@ -15,15 +15,11 @@ package session import ( - "crypto/md5" - "encoding/hex" "encoding/json" - "math/rand" "net/http" "os" "path/filepath" "runtime" - "strconv" "strings" "sync" "text/template" @@ -38,7 +34,6 @@ const ( // TODO: i18n userExists = "user exists" - emailExists = "email exists" userCreated = "user created" userCreateError = "user create error" notAllowRegister = "not allow register" @@ -63,8 +58,8 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) { } httpSession.Save(r, w) - username := httpSession.Values["username"].(string) - user := conf.GetUser(username) + uid := httpSession.Values["uid"].(string) + user := conf.GetUser(uid) if "GET" == r.Method { tmpLinux := user.GoBuildArgsForLinux @@ -116,8 +111,6 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) { Keymap string Workspace string Username string - Password string - Email string Locale string Theme string EditorFontFamily string @@ -143,14 +136,6 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) { user.Keymap = args.Keymap // XXX: disallow change workspace at present // user.Workspace = args.Workspace - if user.Password != args.Password { - user.Password = conf.Salt(args.Password, user.Salt) - } - user.Email = args.Email - - hash := md5.New() - hash.Write([]byte(user.Email)) - user.Gravatar = hex.EncodeToString(hash.Sum(nil)) user.Locale = args.Locale user.Theme = args.Theme @@ -160,7 +145,7 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) { user.Editor.Theme = args.EditorTheme user.Editor.TabSize = args.EditorTabSize - conf.UpdateCustomizedConf(username) + conf.UpdateCustomizedConf(uid) now := time.Now().UnixNano() user.Lived = now @@ -169,66 +154,6 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) { result.Succ = user.Save() } -// LoginHandler handles request of user login. -func LoginHandler(w http.ResponseWriter, r *http.Request) { - if "GET" == r.Method { - // show the login page - - model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(conf.Wide.Locale), - "locale": conf.Wide.Locale, "ver": conf.WideVersion, "year": time.Now().Year()} - - t, err := template.ParseFiles("views/login.html") - - if nil != err { - logger.Error(err) - http.Error(w, err.Error(), 500) - - return - } - - t.Execute(w, model) - - return - } - - // non-GET request as login request - result := util.NewResult() - defer util.RetResult(w, r, result) - - args := struct { - Username string - Password string - }{} - - args.Username = r.FormValue("username") - args.Password = r.FormValue("password") - - result.Succ = false - for _, user := range conf.Users { - if user.Name == args.Username && user.Password == conf.Salt(args.Password, user.Salt) { - result.Succ = true - - break - } - } - - if !result.Succ { - return - } - - // create a HTTP session - httpSession, _ := HTTPSession.Get(r, "wide-session") - httpSession.Values["username"] = args.Username - httpSession.Values["id"] = strconv.Itoa(rand.Int()) - httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge - if "" != conf.Wide.Context { - httpSession.Options.Path = conf.Wide.Context - } - httpSession.Save(r, w) - - logger.Debugf("Created a HTTP session [%s] for user [%s]", httpSession.Values["id"].(string), args.Username) -} - // LogoutHandler handles request of user logout (exit). func LogoutHandler(w http.ResponseWriter, r *http.Request) { result := util.NewResult() @@ -240,66 +165,6 @@ func LogoutHandler(w http.ResponseWriter, r *http.Request) { httpSession.Save(r, w) } -// SignUpUserHandler handles request of registering user. -func SignUpUserHandler(w http.ResponseWriter, r *http.Request) { - if "GET" == r.Method { - // show the user sign up page - - model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(conf.Wide.Locale), - "locale": conf.Wide.Locale, "ver": conf.WideVersion, "dir": conf.Wide.UsersWorkspaces, - "pathSeparator": conf.PathSeparator, "year": time.Now().Year()} - - t, err := template.ParseFiles("views/sign_up.html") - - if nil != err { - logger.Error(err) - http.Error(w, err.Error(), 500) - - return - } - - t.Execute(w, model) - - return - } - - // non-GET request as add user request - - result := util.NewResult() - defer util.RetResult(w, r, result) - - var args map[string]interface{} - - if err := json.NewDecoder(r.Body).Decode(&args); err != nil { - logger.Error(err) - result.Succ = false - - return - } - - username := args["username"].(string) - password := args["password"].(string) - email := args["email"].(string) - - msg := addUser(username, password, email) - if userCreated != msg { - result.Succ = false - result.Msg = msg - - return - } - - // create a HTTP session - httpSession, _ := HTTPSession.Get(r, "wide-session") - httpSession.Values["username"] = username - httpSession.Values["id"] = strconv.Itoa(rand.Int()) - httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge - if "" != conf.Wide.Context { - httpSession.Options.Path = conf.Wide.Context - } - httpSession.Save(r, w) -} - // FixedTimeSave saves online users' configurations periodically (1 minute). // // Main goal of this function is to save user session content, for restoring session content while user open Wide next time. @@ -313,11 +178,11 @@ func FixedTimeSave() { }() } -// CanAccess determines whether the user specified by the given username can access the specified path. -func CanAccess(username, path string) bool { +// CanAccess determines whether the user specified by the given user id can access the specified path. +func CanAccess(userId, path string) bool { path = filepath.FromSlash(path) - userWorkspace := conf.GetUserWorkspace(username) + userWorkspace := conf.GetUserWorkspace(userId) workspaces := filepath.SplitList(userWorkspace) for _, workspace := range workspaces { @@ -342,20 +207,20 @@ func SaveOnlineUsers() { func getOnlineUsers() []*conf.User { ret := []*conf.User{} - usernames := map[string]string{} // distinct username + uids := map[string]string{} // distinct uid for _, s := range WideSessions { - usernames[s.Username] = s.Username + uids[s.UserId] = s.UserId } - for _, username := range usernames { - u := conf.GetUser(username) + for _, uid := range uids { + u := conf.GetUser(uid) - if "playground" == username { // user [playground] is a reserved mock user + if "playground" == uid { // user [playground] is a reserved mock user continue } if nil == u { - logger.Warnf("Not found user [%s]", username) + logger.Warnf("Not found user [%s]", uid) continue } @@ -366,7 +231,7 @@ func getOnlineUsers() []*conf.User { return ret } -// addUser add a user with the specified username, password and email. +// addUser add a user with the specified user id, username and avatar. // // 1. create the user's workspace // 2. generate 'Hello, 世界' demo code in the workspace (a console version and a HTTP version) @@ -374,12 +239,12 @@ func getOnlineUsers() []*conf.User { // 4. serve files of the user's workspace via HTTP // // Note: user [playground] is a reserved mock user -func addUser(username, password, email string) string { +func addUser(userId, userName, userAvatar string) string { if !conf.Wide.AllowRegister { return notAllowRegister } - if "playground" == username { + if "playground" == userId { return userExists } @@ -387,32 +252,26 @@ func addUser(username, password, email string) string { defer addUserMutex.Unlock() for _, user := range conf.Users { - if strings.ToLower(user.Name) == strings.ToLower(username) { + if strings.ToLower(user.Id) == strings.ToLower(userId) { return userExists } - - if strings.ToLower(user.Email) == strings.ToLower(email) { - return emailExists - } } - workspace := filepath.Join(conf.Wide.UsersWorkspaces, username) - - newUser := conf.NewUser(username, password, email, workspace) + workspace := filepath.Join(conf.Wide.UsersWorkspaces, userId) + newUser := conf.NewUser(userId, userName, userAvatar, workspace) conf.Users = append(conf.Users, newUser) - if !newUser.Save() { return userCreateError } conf.CreateWorkspaceDir(workspace) helloWorld(workspace) - conf.UpdateCustomizedConf(username) + conf.UpdateCustomizedConf(userId) - http.Handle("/workspace/"+username+"/", - http.StripPrefix("/workspace/"+username+"/", http.FileServer(http.Dir(newUser.WorkspacePath())))) + http.Handle("/workspace/"+userId+"/", + http.StripPrefix("/workspace/"+userId+"/", http.FileServer(http.Dir(newUser.WorkspacePath())))) - logger.Infof("Created a user [%s]", username) + logger.Infof("Created a user [%s]", userId) return userCreated } diff --git a/static/user/admin/README.md b/static/user/admin/README.md deleted file mode 100644 index 592dcdb..0000000 --- a/static/user/admin/README.md +++ /dev/null @@ -1 +0,0 @@ -This directory is used to hold user _admin_ customized style file `style.css`, it will be generated when wide startup automatically. diff --git a/util/ret.go b/util/ret.go index f9102af..735582f 100644 --- a/util/ret.go +++ b/util/ret.go @@ -29,16 +29,16 @@ var retLogger = log.NewLogger(os.Stdout) // Result. type Result struct { Succ bool `json:"succ"` // successful or not - Code string `json:"code"` // return code + Code int `json:"code"` // return code Msg string `json:"msg"` // message Data interface{} `json:"data"` // data object } -// NewResult creates a result with Succ=true, Code="0", Msg="", Data=nil. +// NewResult creates a result with Succ=true, Code=0, Msg="", Data=nil. func NewResult() *Result { return &Result{ Succ: true, - Code: "0", + Code: 0, Msg: "", Data: nil, }