@@ -40,7 +40,7 @@ const ( | |||||
LoginDLDAP // 5 | LoginDLDAP // 5 | ||||
LoginOAuth2 // 6 | LoginOAuth2 // 6 | ||||
LoginSSPI // 7 | LoginSSPI // 7 | ||||
LoginCloubBrain // 8 | |||||
LoginCloudBrain // 8 | |||||
) | ) | ||||
// LoginNames contains the name of LoginType values. | // LoginNames contains the name of LoginType values. | ||||
@@ -51,7 +51,7 @@ var LoginNames = map[LoginType]string{ | |||||
LoginPAM: "PAM", | LoginPAM: "PAM", | ||||
LoginOAuth2: "OAuth2", | LoginOAuth2: "OAuth2", | ||||
LoginSSPI: "SPNEGO with SSPI", | LoginSSPI: "SPNEGO with SSPI", | ||||
LoginCloubBrain: "Cloud Brain", | |||||
LoginCloudBrain: "Cloud Brain", | |||||
} | } | ||||
// SecurityProtocolNames contains the name of SecurityProtocol values. | // SecurityProtocolNames contains the name of SecurityProtocol values. | ||||
@@ -202,6 +202,8 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) { | |||||
source.Cfg = new(OAuth2Config) | source.Cfg = new(OAuth2Config) | ||||
case LoginSSPI: | case LoginSSPI: | ||||
source.Cfg = new(SSPIConfig) | source.Cfg = new(SSPIConfig) | ||||
case LoginCloudBrain: | |||||
source.Cfg = new(CloudBrainConfig) | |||||
default: | default: | ||||
panic("unrecognized login source type: " + com.ToStr(*val)) | panic("unrecognized login source type: " + com.ToStr(*val)) | ||||
} | } | ||||
@@ -717,7 +719,7 @@ func ExternalUserLogin(user *User, login, password string, source *LoginSource) | |||||
user, err = LoginViaSMTP(user, login, password, source.ID, source.Cfg.(*SMTPConfig)) | user, err = LoginViaSMTP(user, login, password, source.ID, source.Cfg.(*SMTPConfig)) | ||||
case LoginPAM: | case LoginPAM: | ||||
user, err = LoginViaPAM(user, login, password, source.ID, source.Cfg.(*PAMConfig)) | user, err = LoginViaPAM(user, login, password, source.ID, source.Cfg.(*PAMConfig)) | ||||
case LoginCloubBrain: | |||||
case LoginCloudBrain: | |||||
user, err = LoginViaCloudBrain(user, login, password, source) | user, err = LoginViaCloudBrain(user, login, password, source) | ||||
default: | default: | ||||
return nil, ErrUnsupportedLoginType | return nil, ErrUnsupportedLoginType | ||||
@@ -831,12 +833,16 @@ func LoginViaCloudBrain(user *User, login, password string, source *LoginSource) | |||||
} | } | ||||
if user != nil { | if user != nil { | ||||
//todo: update token | |||||
user.Token = token | user.Token = token | ||||
return user, UpdateUserCols(user, "token") | return user, UpdateUserCols(user, "token") | ||||
} | } | ||||
cloudBrainUser, err := cloudbrain.GetUserInfo(token, login) | |||||
cloudBrainUser, err := cloudbrain.GetUserInfo(login, token) | |||||
if err != nil { | |||||
log.Error("GetUserInfo(%s) failed: %v", login, err) | |||||
return nil, err | |||||
} | |||||
if len(cloudBrainUser.Email) == 0 { | if len(cloudBrainUser.Email) == 0 { | ||||
cloudBrainUser.Email = fmt.Sprintf("%s@cloudbrain", login) | cloudBrainUser.Email = fmt.Sprintf("%s@cloudbrain", login) | ||||
@@ -31,7 +31,7 @@ type RespAuth struct { | |||||
type RespToken struct { | type RespToken struct { | ||||
Code string `json:"code"` | Code string `json:"code"` | ||||
Message string `json:"message"` | |||||
Message string `json:"msg"` | |||||
Payload PayloadToken `json:"payload"` | Payload PayloadToken `json:"payload"` | ||||
} | } | ||||
@@ -43,7 +43,7 @@ type PayloadToken struct { | |||||
type RespUserInfo struct { | type RespUserInfo struct { | ||||
Code string `json:"code"` | Code string `json:"code"` | ||||
Message string `json:"message"` | |||||
Message string `json:"msg"` | |||||
Payload PayloadUserInfo `json:"payload"` | Payload PayloadUserInfo `json:"payload"` | ||||
} | } | ||||
@@ -71,6 +71,8 @@ func UserValidate(username string, password string) (string, error) { | |||||
return "", err | return "", err | ||||
} | } | ||||
defer resp.Body.Close() | |||||
body,err := ioutil.ReadAll(resp.Body) | body,err := ioutil.ReadAll(resp.Body) | ||||
if err != nil { | if err != nil { | ||||
log.Error("read resp body failed:" + err.Error()) | log.Error("read resp body failed:" + err.Error()) | ||||
@@ -109,6 +111,8 @@ func GetUserInfo(username string, token string) (*CloudBrainUser, error) { | |||||
return nil, err | return nil, err | ||||
} | } | ||||
defer resp.Body.Close() | |||||
body,err := ioutil.ReadAll(resp.Body) | body,err := ioutil.ReadAll(resp.Body) | ||||
if err != nil { | if err != nil { | ||||
log.Error("read resp body failed:", err.Error()) | log.Error("read resp body failed:", err.Error()) | ||||
@@ -192,6 +192,7 @@ no_reply_address_helper=具有隐藏电子邮件地址的用户的域名。例 | |||||
[home] | [home] | ||||
uname_holder=登录名或电子邮箱地址 | uname_holder=登录名或电子邮箱地址 | ||||
uname_holder_cloud_brain=云脑登录名 | |||||
password_holder=密码 | password_holder=密码 | ||||
switch_dashboard_context=切换控制面板用户 | switch_dashboard_context=切换控制面板用户 | ||||
my_repos=项目列表 | my_repos=项目列表 | ||||
@@ -267,6 +268,7 @@ twofa_passcode_incorrect=你的验证码不正确。如果你丢失了你的设 | |||||
twofa_scratch_token_incorrect=你的验证口令不正确。 | twofa_scratch_token_incorrect=你的验证口令不正确。 | ||||
login_userpass=登录 | login_userpass=登录 | ||||
login_openid=OpenID | login_openid=OpenID | ||||
login_cloudbrain=云脑用户登录 | |||||
oauth_signup_tab=注册帐号 | oauth_signup_tab=注册帐号 | ||||
oauth_signup_title=添加电子邮件和密码 (用于帐号恢复) | oauth_signup_title=添加电子邮件和密码 (用于帐号恢复) | ||||
oauth_signup_submit=完成账号 | oauth_signup_submit=完成账号 | ||||
@@ -305,6 +305,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
// ***** START: User ***** | // ***** START: User ***** | ||||
m.Group("/user", func() { | m.Group("/user", func() { | ||||
m.Get("/login", user.SignIn) | m.Get("/login", user.SignIn) | ||||
m.Get("/login/cloud_brain", user.SignInCloudBrain) | |||||
m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost) | m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost) | ||||
m.Group("", func() { | m.Group("", func() { | ||||
m.Combo("/login/openid"). | m.Combo("/login/openid"). | ||||
@@ -36,6 +36,8 @@ const ( | |||||
tplMustChangePassword = "user/auth/change_passwd" | tplMustChangePassword = "user/auth/change_passwd" | ||||
// tplSignIn template for sign in page | // tplSignIn template for sign in page | ||||
tplSignIn base.TplName = "user/auth/signin" | tplSignIn base.TplName = "user/auth/signin" | ||||
// tplSignIn template for sign in page | |||||
tplSignInCloudBrain base.TplName = "user/auth/signin_cloud_brain" | |||||
// tplSignUp template path for sign up page | // tplSignUp template path for sign up page | ||||
tplSignUp base.TplName = "user/auth/signup" | tplSignUp base.TplName = "user/auth/signup" | ||||
// TplActivate template path for activate user | // TplActivate template path for activate user | ||||
@@ -143,10 +145,28 @@ func SignIn(ctx *context.Context) { | |||||
ctx.Data["PageIsSignIn"] = true | ctx.Data["PageIsSignIn"] = true | ||||
ctx.Data["PageIsLogin"] = true | ctx.Data["PageIsLogin"] = true | ||||
ctx.Data["EnableSSPI"] = models.IsSSPIEnabled() | ctx.Data["EnableSSPI"] = models.IsSSPIEnabled() | ||||
ctx.Data["EnableCloudBrain"] = true | |||||
ctx.HTML(200, tplSignIn) | ctx.HTML(200, tplSignIn) | ||||
} | } | ||||
// SignInCloudBrain render sign in page | |||||
func SignInCloudBrain(ctx *context.Context) { | |||||
ctx.Data["Title"] = ctx.Tr("sign_in") | |||||
// Check auto-login. | |||||
if checkAutoLogin(ctx) { | |||||
return | |||||
} | |||||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" | |||||
ctx.Data["PageIsSignIn"] = true | |||||
ctx.Data["PageIsCloudBrainLogin"] = true | |||||
ctx.Data["EnableCloudBrain"] = true | |||||
ctx.HTML(200, tplSignInCloudBrain) | |||||
} | |||||
// SignInPost response for sign in request | // SignInPost response for sign in request | ||||
func SignInPost(ctx *context.Context, form auth.SignInForm) { | func SignInPost(ctx *context.Context, form auth.SignInForm) { | ||||
ctx.Data["Title"] = ctx.Tr("sign_in") | ctx.Data["Title"] = ctx.Tr("sign_in") | ||||
@@ -0,0 +1,10 @@ | |||||
{{template "base/head" .}} | |||||
<div class="user signin"> | |||||
{{template "user/auth/signin_navbar" .}} | |||||
<div class="ui container"> | |||||
<div class="ui raised very padded text container segment"> | |||||
{{template "user/auth/signin_cloudbrain" .}} | |||||
</div> | |||||
</div> | |||||
</div> | |||||
{{template "base/footer" .}} |
@@ -0,0 +1,55 @@ | |||||
<style> | |||||
.full.height{background-color: #F9F9F9;} | |||||
.ui.left:not(.action){ float:none;} | |||||
.ui.left{ float:none;} | |||||
.ui.secondary.pointing.menu{ border-bottom:none;} | |||||
</style> | |||||
{{template "base/alert" .}} | |||||
<div class="ui centered grid"> | |||||
<div class="sixteen wide mobile ten wide tablet ten wide computer column"> | |||||
<div class="ui bottom aligned two column grid"> | |||||
<div class="column"> | |||||
<h2 class="ui header"> | |||||
{{.i18n.Tr "auth.login_userpass"}} | |||||
</h2> | |||||
</div> | |||||
</div> | |||||
<div class="ui grid"> | |||||
<div class="column"> | |||||
<form class="ui form" action="{{.SignInLink}}" method="post"> | |||||
{{.CsrfTokenHtml}} | |||||
<div class="field"> | |||||
<div class="ui left icon input {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}"> | |||||
<i class="user icon"></i> | |||||
<input id="user_name" name="user_name" value="{{.user_name}}" placeholder="{{.i18n.Tr "home.uname_holder_cloud_brain"}}" autofocus required> | |||||
</div> | |||||
</div> | |||||
<div class="field"> | |||||
<div class="ui left icon input {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}"> | |||||
<i class="lock icon"></i> | |||||
<input id="password" name="password" type="password" value="{{.password}}" placeholder="{{.i18n.Tr "password"}}" autocomplete="off" required> | |||||
</div> | |||||
</div> | |||||
<div class="two fields inline"> | |||||
<div class="field"> | |||||
<div class="ui checkbox"> | |||||
<label>{{.i18n.Tr "auth.remember_me"}}</label> | |||||
<input name="remember" type="checkbox"> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="ui hidden divider"></div> | |||||
<div class="center aligned field"> | |||||
<button class="fluid large ui blue button"> | |||||
{{.i18n.Tr "sign_in"}} | |||||
</button> | |||||
</div> | |||||
</form> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> |
@@ -1,8 +1,11 @@ | |||||
{{if or .EnableOpenIDSignIn .EnableSSPI}} | |||||
{{if or .EnableOpenIDSignIn .EnableSSPI .EnableCloudBrain}} | |||||
<div class="ui secondary pointing tabular top attached borderless menu new-menu navbar"> | <div class="ui secondary pointing tabular top attached borderless menu new-menu navbar"> | ||||
<a class="{{if .PageIsLogin}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login"> | <a class="{{if .PageIsLogin}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login"> | ||||
{{.i18n.Tr "auth.login_userpass"}} | {{.i18n.Tr "auth.login_userpass"}} | ||||
</a> | </a> | ||||
<a class="{{if .PageIsCloudBrainLogin}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login/cloud_brain"> | |||||
{{.i18n.Tr "auth.login_cloudbrain"}} | |||||
</a> | |||||
{{if .EnableOpenIDSignIn}} | {{if .EnableOpenIDSignIn}} | ||||
<a class="{{if .PageIsLoginOpenID}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login/openid"> | <a class="{{if .PageIsLoginOpenID}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login/openid"> | ||||
<i class="fa fa-openid"></i> | <i class="fa fa-openid"></i> | ||||