Commit 8b06c4a1 authored by ibuler's avatar ibuler

[Update] 修改 认证

parent 93d1b3f1
package auth
import (
"cocogo/pkg/cctx"
"cocogo/pkg/i18n"
"strings"
"github.com/gliderlabs/ssh"
gossh "golang.org/x/crypto/ssh"
"cocogo/pkg/cctx"
"cocogo/pkg/common"
"cocogo/pkg/i18n"
"cocogo/pkg/logger"
"cocogo/pkg/service"
)
......@@ -16,34 +16,34 @@ import (
var mfaInstruction = i18n.T("Please enter 6 digits.")
var mfaQuestion = i18n.T("[MFA auth]: ")
var contentKeyMFASeed = "MFASeed"
const (
actionAccepted = "Accepted"
actionFailed = "Failed"
actionPartialAccepted = "Partial accepted"
)
func checkAuth(ctx ssh.Context, password, publicKey string) (res ssh.AuthResult) {
username := ctx.User()
remoteAddr := strings.Split(ctx.RemoteAddr().String(), ":")[0]
resp, err := service.Authenticate(username, password, publicKey, remoteAddr, "T")
authMethod := "publickey"
action := "Accepted"
action := actionAccepted
res = ssh.AuthFailed
if password != "" {
authMethod = "password"
}
if err != nil {
action = "Failed"
remoteAddr := strings.Split(ctx.RemoteAddr().String(), ":")[0]
} else if resp.Seed != "" && resp.Token == "" {
ctx.SetValue(contentKeyMFASeed, resp.Seed)
res = ssh.AuthPartiallySuccessful
} else {
res = ssh.AuthSuccessful
resp, err := service.Authenticate(username, password, publicKey, remoteAddr, "T")
if err != nil {
action = actionFailed
logger.Infof("%s %s for %s from %s", action, authMethod, username, remoteAddr)
return
}
if resp != nil {
switch resp.User.IsMFA {
switch resp.User.OTPLevel {
case 0:
res = ssh.AuthSuccessful
case 1:
res = ssh.AuthPartiallySuccessful
case 2:
case 1, 2:
action = actionPartialAccepted
res = ssh.AuthPartiallySuccessful
default:
}
......@@ -51,14 +51,12 @@ func checkAuth(ctx ssh.Context, password, publicKey string) (res ssh.AuthResult)
ctx.SetValue(cctx.ContextKeySeed, resp.Seed)
ctx.SetValue(cctx.ContextKeyToken, resp.Token)
}
logger.Infof("%s %s for %s from %s", action, authMethod, username, remoteAddr)
return res
}
func CheckUserPassword(ctx ssh.Context, password string) ssh.AuthResult {
res := checkAuth(ctx, password, "")
return res
return checkAuth(ctx, password, "")
}
func CheckUserPublicKey(ctx ssh.Context, key ssh.PublicKey) ssh.AuthResult {
......@@ -67,32 +65,40 @@ func CheckUserPublicKey(ctx ssh.Context, key ssh.PublicKey) ssh.AuthResult {
return checkAuth(ctx, "", publicKey)
}
func CheckMFA(ctx ssh.Context, challenger gossh.KeyboardInteractiveChallenge) ssh.AuthResult {
func CheckMFA(ctx ssh.Context, challenger gossh.KeyboardInteractiveChallenge) (res ssh.AuthResult) {
username := ctx.User()
remoteAddr := strings.Split(ctx.RemoteAddr().String(), ":")[0]
res = ssh.AuthFailed
defer func() {
authMethod := "MFA"
if res == ssh.AuthSuccessful {
action := actionAccepted
logger.Infof("%s %s for %s from %s", action, authMethod, username, remoteAddr)
} else {
action := actionFailed
logger.Errorf("%s %s for %s from %s", action, authMethod, username, remoteAddr)
}
}()
answers, err := challenger(username, mfaInstruction, []string{mfaQuestion}, []bool{true})
if err != nil {
return ssh.AuthFailed
}
if len(answers) != 1 {
return ssh.AuthFailed
if err != nil || len(answers) != 1 {
return
}
mfaCode := answers[0]
seed, ok := ctx.Value(contentKeyMFASeed).(string)
seed, ok := ctx.Value(cctx.ContextKeySeed).(string)
if !ok {
logger.Error("Mfa Auth failed, may be user password or publickey auth failed")
return ssh.AuthFailed
return
}
resp, err := service.CheckUserOTP(seed, mfaCode)
if err != nil {
logger.Error("Mfa Auth failed: ", err)
return ssh.AuthFailed
return
}
if resp.Token != "" {
return ssh.AuthSuccessful
res = ssh.AuthSuccessful
return
}
return ssh.AuthFailed
return
}
func CheckUserNeedMFA(ctx ssh.Context) (methods []string) {
......
......@@ -85,7 +85,7 @@ func (c *Client) parseUrlQuery(url string, params []map[string]string) string {
return url
}
func (c *Client) ParseUrl(url string, params []map[string]string) string {
func (c *Client) parseUrl(url string, params []map[string]string) string {
url = c.parseUrlQuery(url, params)
if c.BaseHost != "" {
url = strings.TrimRight(c.BaseHost, "/") + url
......@@ -126,7 +126,7 @@ func (c *Client) SetReqHeaders(req *http.Request) {
}
func (c *Client) NewRequest(method, url string, body interface{}, params []map[string]string) (req *http.Request, err error) {
url = c.ParseUrl(url, params)
url = c.parseUrl(url, params)
reader, err := c.marshalData(body)
if err != nil {
return
......
......@@ -59,19 +59,19 @@ type InteractiveHandler struct {
mu sync.RWMutex
}
func (i *InteractiveHandler) displayBanner() {
displayBanner(i.sess, i.user.Name)
func (h *InteractiveHandler) displayBanner() {
displayBanner(h.sess, h.user.Name)
}
func (i *InteractiveHandler) preDispatch() {
i.displayBanner()
i.onceLoad.Do(func() {
i.loadUserAssets()
i.loadUserAssetNodes()
func (h *InteractiveHandler) preDispatch() {
h.displayBanner()
h.onceLoad.Do(func() {
h.loadUserAssets()
h.loadUserAssetNodes()
})
}
func (i *InteractiveHandler) watchWinSizeChange(winCh <-chan ssh.Window, done <-chan struct{}) {
func (h *InteractiveHandler) watchWinSizeChange(winCh <-chan ssh.Window, done <-chan struct{}) {
for {
select {
case <-done:
......@@ -82,19 +82,19 @@ func (i *InteractiveHandler) watchWinSizeChange(winCh <-chan ssh.Window, done <-
return
}
logger.Debugf("Term change: %d*%d", win.Height, win.Width)
_ = i.term.SetSize(win.Width, win.Height)
_ = h.term.SetSize(win.Width, win.Height)
}
}
}
func (i *InteractiveHandler) Dispatch(ctx cctx.Context) {
i.preDispatch()
fmt.Println(i.user)
_, winCh, _ := i.sess.Pty()
func (h *InteractiveHandler) Dispatch(ctx cctx.Context) {
h.preDispatch()
fmt.Println(h.user)
_, winCh, _ := h.sess.Pty()
for {
doneChan := make(chan struct{})
go i.watchWinSizeChange(winCh, doneChan)
line, err := i.term.ReadLine()
go h.watchWinSizeChange(winCh, doneChan)
line, err := h.term.ReadLine()
close(doneChan)
if err != nil {
......@@ -110,37 +110,37 @@ func (i *InteractiveHandler) Dispatch(ctx cctx.Context) {
case 0, 1:
switch strings.ToLower(line) {
case "", "p":
i.Proxy(ctx)
h.Proxy(ctx)
case "g":
i.displayNodes(i.nodes)
h.displayNodes(h.nodes)
case "s":
i.changeLanguage()
h.changeLanguage()
case "h":
i.displayBanner()
h.displayBanner()
case "r":
i.refreshAssetsAndNodesData()
h.refreshAssetsAndNodesData()
case "q":
logger.Info("exit session")
return
default:
assets := i.searchAsset(line)
i.searchResult = assets
i.displayAssetsOrProxy(assets)
assets := h.searchAsset(line)
h.searchResult = assets
h.displayAssetsOrProxy(assets)
}
default:
switch {
case strings.Index(line, "/") == 0:
searchWord := strings.TrimSpace(line[1:])
assets := i.searchAsset(searchWord)
i.searchResult = assets
i.displayAssets(assets)
assets := h.searchAsset(searchWord)
h.searchResult = assets
h.displayAssets(assets)
case strings.Index(line, "g") == 0:
searchWord := strings.TrimSpace(strings.TrimPrefix(line, "g"))
if num, err := strconv.Atoi(searchWord); err == nil {
if num >= 0 {
assets := i.searchNodeAssets(num)
i.displayAssets(assets)
i.searchResult = assets
assets := h.searchNodeAssets(num)
h.displayAssets(assets)
h.searchResult = assets
continue
}
}
......@@ -149,15 +149,15 @@ func (i *InteractiveHandler) Dispatch(ctx cctx.Context) {
}
}
func (i *InteractiveHandler) chooseSystemUser(systemUsers []model.SystemUser) model.SystemUser {
table := tablewriter.NewWriter(i.sess)
func (h *InteractiveHandler) chooseSystemUser(systemUsers []model.SystemUser) model.SystemUser {
table := tablewriter.NewWriter(h.sess)
table.SetHeader([]string{"ID", "UserName"})
for i := 0; i < len(systemUsers); i++ {
table.Append([]string{strconv.Itoa(i + 1), systemUsers[i].UserName})
}
table.SetBorder(false)
count := 0
term := terminal.NewTerminal(i.sess, "num:")
term := terminal.NewTerminal(h.sess, "num:")
for count < 3 {
table.Render()
line, err := term.ReadLine()
......@@ -175,44 +175,44 @@ func (i *InteractiveHandler) chooseSystemUser(systemUsers []model.SystemUser) mo
}
// 当资产的数量为1的时候,就进行代理转化
func (i *InteractiveHandler) displayAssetsOrProxy(assets []model.Asset) {
func (h *InteractiveHandler) displayAssetsOrProxy(assets []model.Asset) {
//if len(assets) == 1 {
// var systemUser model.SystemUser
// switch len(assets[0].SystemUsers) {
// case 0:
// // 有授权的资产,但是资产用户信息,无法登陆
// i.displayAssets(assets)
// h.displayAssets(assets)
// return
// case 1:
// systemUser = assets[0].SystemUsers[0]
// default:
// systemUser = i.chooseSystemUser(assets[0].SystemUsers)
// systemUser = h.chooseSystemUser(assets[0].SystemUsers)
// }
//
// authInfo, err := model.GetSystemUserAssetAuthInfo(systemUser.Id, assets[0].Id)
// authInfo, err := model.GetSystemUserAssetAuthInfo(systemUser.ID, assets[0].ID)
// if err != nil {
// return
// }
// if ok := service.ValidateUserAssetPermission(i.user.Id, systemUser.Id, assets[0].Id); !ok {
// if ok := service.ValidateUserAssetPermission(h.user.ID, systemUser.ID, assets[0].ID); !ok {
// // 检查user 是否对该资产有权限
// return
// }
//
// err = i.Proxy(assets[0], authInfo)
// err = h.Proxy(assets[0], authInfo)
// if err != nil {
// logger.Info(err)
// }
// return
//} else {
// i.displayAssets(assets)
// h.displayAssets(assets)
//}
}
func (i *InteractiveHandler) displayAssets(assets model.AssetList) {
func (h *InteractiveHandler) displayAssets(assets model.AssetList) {
if len(assets) == 0 {
_, _ = io.WriteString(i.sess, "\r\n No Assets\r\n\r")
_, _ = io.WriteString(h.sess, "\r\n No Assets\r\n\r")
} else {
table := tablewriter.NewWriter(i.sess)
table := tablewriter.NewWriter(h.sess)
table.SetHeader([]string{"ID", "Hostname", "IP", "LoginAs", "Comment"})
for index, assetItem := range assets {
sysUserArray := make([]string, len(assetItem.SystemUsers))
......@@ -229,44 +229,44 @@ func (i *InteractiveHandler) displayAssets(assets model.AssetList) {
}
func (i *InteractiveHandler) displayNodes(nodes []model.Node) {
func (h *InteractiveHandler) displayNodes(nodes []model.Node) {
tree := ConstructAssetNodeTree(nodes)
tipHeaderMsg := "\r\nNode: [ ID.Name(Asset amount) ]"
tipEndMsg := "Tips: Enter g+NodeID to display the host under the node, such as g1\r\n\r"
_, err := io.WriteString(i.sess, tipHeaderMsg)
_, err = io.WriteString(i.sess, tree.String())
_, err = io.WriteString(i.sess, tipEndMsg)
_, err := io.WriteString(h.sess, tipHeaderMsg)
_, err = io.WriteString(h.sess, tree.String())
_, err = io.WriteString(h.sess, tipEndMsg)
if err != nil {
logger.Info("displayAssetNodes err:", err)
}
}
func (i *InteractiveHandler) refreshAssetsAndNodesData() {
_, err := io.WriteString(i.sess, "Refresh done\r\n")
func (h *InteractiveHandler) refreshAssetsAndNodesData() {
_, err := io.WriteString(h.sess, "Refresh done\r\n")
if err != nil {
logger.Error("refresh Assets Nodes err:", err)
}
}
func (i *InteractiveHandler) loadUserAssets() {
i.assets = service.GetUserAssets(i.user.Id, "1")
func (h *InteractiveHandler) loadUserAssets() {
h.assets = service.GetUserAssets(h.user.ID, "1")
}
func (i *InteractiveHandler) loadUserAssetNodes() {
i.nodes = service.GetUserNodes(i.user.Id, "1")
func (h *InteractiveHandler) loadUserAssetNodes() {
h.nodes = service.GetUserNodes(h.user.ID, "1")
}
func (i *InteractiveHandler) changeLanguage() {
func (h *InteractiveHandler) changeLanguage() {
}
func (i *InteractiveHandler) JoinShareRoom(roomID string) {
//sshConn := userhome.NewSSHConn(i.sess)
//ctx, cancelFuc := context.WithCancel(i.sess.Context())
func (h *InteractiveHandler) JoinShareRoom(roomID string) {
//sshConn := userhome.NewSSHConn(h.sess)
//ctx, cancelFuc := context.WithCancel(h.sess.Context())
//
//_, winCh, _ := i.sess.Pty()
//_, winCh, _ := h.sess.Pty()
//go func() {
// for {
// select {
......@@ -286,21 +286,21 @@ func (i *InteractiveHandler) JoinShareRoom(roomID string) {
}
func (i *InteractiveHandler) searchAsset(key string) (assets []model.Asset) {
func (h *InteractiveHandler) searchAsset(key string) (assets []model.Asset) {
//if indexNum, err := strconv.Atoi(key); err == nil {
// if indexNum > 0 && indexNum <= len(i.searchResult) {
// return []model.Asset{i.searchResult[indexNum-1]}
// if indexNum > 0 && indexNum <= len(h.searchResult) {
// return []model.Asset{h.searchResult[indexNum-1]}
// }
//}
//
//if assetsData, ok := i.assetData.Load(AssetsMapKey); ok {
//if assetsData, ok := h.assetData.Load(AssetsMapKey); ok {
// for _, assetValue := range assetsData.([]model.Asset) {
// if isSubstring([]string{assetValue.Ip, assetValue.Hostname, assetValue.Comment}, key) {
// assets = append(assets, assetValue)
// }
// }
//} else {
// assetsData, _ := Cached.Load(i.user.Id)
// assetsData, _ := Cached.Load(h.user.ID)
// for _, assetValue := range assetsData.([]model.Asset) {
// if isSubstring([]string{assetValue.Ip, assetValue.Hostname, assetValue.Comment}, key) {
// assets = append(assets, assetValue)
......@@ -311,9 +311,9 @@ func (i *InteractiveHandler) searchAsset(key string) (assets []model.Asset) {
return assets
}
func (i *InteractiveHandler) searchNodeAssets(num int) (assets []model.Asset) {
func (h *InteractiveHandler) searchNodeAssets(num int) (assets []model.Asset) {
//var assetNodesData []model.Node
//if assetNodes, ok := i.assetData.Load(AssetNodesMapKey); ok {
//if assetNodes, ok := h.assetData.Load(AssetNodesMapKey); ok {
// assetNodesData = assetNodes.([]model.Node)
// if num > len(assetNodesData) || num == 0 {
// return assets
......@@ -324,14 +324,14 @@ func (i *InteractiveHandler) searchNodeAssets(num int) (assets []model.Asset) {
}
func (i *InteractiveHandler) Proxy(ctx context.Context) {
i.assetSelect = &model.Asset{Hostname: "centos", Port: 32768, Ip: "127.0.0.1"}
i.systemUserSelect = &model.SystemUser{Name: "web", UserName: "root", Password: "screencast"}
func (h *InteractiveHandler) Proxy(ctx context.Context) {
h.assetSelect = &model.Asset{Hostname: "centos", Port: 32768, Ip: "127.0.0.1"}
h.systemUserSelect = &model.SystemUser{Name: "web", UserName: "root", Password: "screencast"}
p := proxy.ProxyServer{
Session: i.sess,
User: i.user,
Asset: i.assetSelect,
SystemUser: i.systemUserSelect,
Session: h.sess,
User: h.user,
Asset: h.assetSelect,
SystemUser: h.systemUserSelect,
}
p.Proxy()
}
......
......@@ -7,7 +7,7 @@ type Terminal struct {
Id string `json:"id"`
Name string `json:"name"`
AccessKey struct {
Id string `json:"id"`
ID string `json:"id"`
Secret string `json:"secret"`
} `json:"access_key"`
} `json:"service_account"`
......
......@@ -25,15 +25,14 @@ type AuthResponse struct {
}
type User struct {
Id string `json:"id"`
ID string `json:"id"`
Name string `json:"name"`
Username string `json:"username"`
Email string `json:"email"`
OTPLevel int `json:"otp_level"`
Role string `json:"role"`
IsValid bool `json:"is_valid"`
IsActive bool `json:"is_active"`
IsMFA int `json:"otp_level"`
OTPLevel int `json:"otp_level"`
}
type TokenUser struct {
......
......@@ -20,7 +20,7 @@ var (
)
type AccessKey struct {
Id string
ID string
Secret string
Path string
Value string
......@@ -29,7 +29,7 @@ type AccessKey struct {
func (ak AccessKey) Sign() (string, string) {
date := common.HTTPGMTDate()
signature := common.MakeSignature(ak.Secret, date)
return date, fmt.Sprintf("Sign %s:%s", ak.Id, signature)
return date, fmt.Sprintf("Sign %s:%s", ak.ID, signature)
}
func (ak *AccessKey) LoadAccessKeyFromStr(key string) error {
......@@ -40,7 +40,7 @@ func (ak *AccessKey) LoadAccessKeyFromStr(key string) error {
if len(keySlice) != 2 {
return AccessKeyInvalid
}
ak.Id = keySlice[0]
ak.ID = keySlice[0]
ak.Secret = keySlice[1]
return nil
}
......@@ -74,7 +74,7 @@ func (ak *AccessKey) SaveToFile() error {
if err != nil {
return err
}
_, err = f.WriteString(fmt.Sprintf("%s:%s", ak.Id, ak.Secret))
_, err = f.WriteString(fmt.Sprintf("%s:%s", ak.ID, ak.Secret))
if err != nil {
logger.Error(err)
}
......@@ -92,7 +92,7 @@ func (ak *AccessKey) Register(times int) error {
logger.Error(msg)
os.Exit(1)
}
ak.Id = res.ServiceAccount.AccessKey.Id
ak.ID = res.ServiceAccount.AccessKey.ID
ak.Secret = res.ServiceAccount.AccessKey.Secret
return nil
}
......
......@@ -7,12 +7,12 @@ import (
"cocogo/pkg/model"
)
func GetUserAssets(userId, cachePolicy string) (assets model.AssetList) {
func GetUserAssets(userID, cachePolicy string) (assets model.AssetList) {
if cachePolicy == "" {
cachePolicy = "0"
}
payload := map[string]string{"cache_policy": cachePolicy}
Url := fmt.Sprintf(UserAssetsURL, userId)
Url := fmt.Sprintf(UserAssetsURL, userID)
err := authClient.Get(Url, &assets, payload)
if err != nil {
logger.Error("GetUserAssets---err")
......
......@@ -23,8 +23,7 @@ func Authenticate(username, password, publicKey, remoteAddr, loginType string) (
"remote_addr": remoteAddr,
"login_type": loginType,
}
Url := client.ParseUrl(UserAuthURL, nil)
err = client.Post(Url, data, &resp)
err = client.Post(UserAuthURL, data, &resp)
if err != nil {
logger.Error(err)
......@@ -32,33 +31,8 @@ func Authenticate(username, password, publicKey, remoteAddr, loginType string) (
return
}
func AuthenticateMFA(seed, code, loginType string) (resp *model.AuthResponse, err error) {
/*
data = {
'seed': seed,
'otp_code': otp_code,
'login_type': login_type,
}
*/
data := map[string]string{
"seed": seed,
"otp_code": code,
"login_type": loginType,
}
Url := client.ParseUrl(AuthMFAURL, nil)
err = client.Post(Url, data, resp)
if err != nil {
logger.Error(err)
}
return
}
func GetUserProfile(userId string) (user *model.User) {
Url := fmt.Sprintf(UserDetailURL, userId)
func GetUserProfile(userID string) (user *model.User) {
Url := fmt.Sprintf(UserDetailURL, userID)
err := authClient.Get(Url, user)
if err != nil {
logger.Error(err)
......@@ -98,9 +72,9 @@ func CheckUserOTP(seed, code string) (resp *AuthResp, err error) {
return
}
func CheckUserCookie(sessionId, csrfToken string) (user *model.User) {
func CheckUserCookie(sessionID, csrfToken string) (user *model.User) {
client.SetCookie("csrftoken", csrfToken)
client.SetCookie("sessionid", sessionId)
client.SetCookie("sessionid", sessionID)
err := client.Get(UserProfileURL, &user)
if err != nil {
logger.Error(err)
......
......@@ -28,7 +28,7 @@ func AuthDecorator(handler http.HandlerFunc) http.HandlerFunc {
}
}
user := service.CheckUserCookie(sessionid, csrfToken)
if user.Id == "" {
if user.ID == "" {
// Todo: 构建login的url
http.Redirect(responseWriter, request, "", http.StatusFound)
return
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment