Reviewed-by: stardust <denglf@pcl.ac.cn>tags/v1.21.12.1
| @@ -1049,6 +1049,10 @@ RESULT_BACKEND = redis://localhost:6379 | |||||
| HOST = http://192.168.204.24 | HOST = http://192.168.204.24 | ||||
| USERNAME = | USERNAME = | ||||
| PASSWORD = | PASSWORD = | ||||
| USER_CENTER_HOST = http://192.168.202.73:31441 | |||||
| CLIENT_ID = 3Z377wcplxeE2qpycpjv | |||||
| CLIENT_SECRET = J5ykfVl2kcxW0H9cawSL | |||||
| REST_SERVER_HOST = http://192.168.202.73 | |||||
| ; cloudbrain visit opendata | ; cloudbrain visit opendata | ||||
| USER = cW4cMtH24eoWPE7X | USER = cW4cMtH24eoWPE7X | ||||
| PWD = 4BPmgvK2hb2Eywwyp4YZRY4B7yQf4DAC | PWD = 4BPmgvK2hb2Eywwyp4YZRY4B7yQf4DAC | ||||
| @@ -6,6 +6,7 @@ package models | |||||
| import ( | import ( | ||||
| "bytes" | "bytes" | ||||
| "code.gitea.io/gitea/modules/log" | |||||
| "fmt" | "fmt" | ||||
| "io" | "io" | ||||
| "path" | "path" | ||||
| @@ -353,5 +354,20 @@ func GetAllPublicAttachments() ([]*Attachment, error) { | |||||
| func getAllPublicAttachments(e Engine) ([]*Attachment, error) { | func getAllPublicAttachments(e Engine) ([]*Attachment, error) { | ||||
| attachments := make([]*Attachment, 0, 10) | attachments := make([]*Attachment, 0, 10) | ||||
| return attachments, e.Where("is_private = true ").Find(&attachments) | |||||
| return attachments, e.Where("is_private = false and decompress_state = ?", DecompressStateDone).Find(&attachments) | |||||
| } | } | ||||
| func GetPrivateAttachments(username string) ([]*Attachment, error) { | |||||
| user, err := getUserByName(x, username) | |||||
| if err != nil { | |||||
| log.Error("getUserByName(%s) failed:%v", username, err) | |||||
| return nil, err | |||||
| } | |||||
| return getPrivateAttachments(x, user.ID) | |||||
| } | |||||
| func getPrivateAttachments(e Engine, userID int64) ([]*Attachment, error) { | |||||
| attachments := make([]*Attachment, 0, 10) | |||||
| return attachments, e.Where("uploader_id = ? and decompress_state = ?", userID, DecompressStateDone).Find(&attachments) | |||||
| } | |||||
| @@ -6,6 +6,7 @@ | |||||
| package models | package models | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/modules/auth/cloudbrain" | |||||
| "crypto/tls" | "crypto/tls" | ||||
| "encoding/json" | "encoding/json" | ||||
| "errors" | "errors" | ||||
| @@ -39,6 +40,7 @@ const ( | |||||
| LoginDLDAP // 5 | LoginDLDAP // 5 | ||||
| LoginOAuth2 // 6 | LoginOAuth2 // 6 | ||||
| LoginSSPI // 7 | LoginSSPI // 7 | ||||
| LoginCloudBrain // 8 | |||||
| ) | ) | ||||
| // LoginNames contains the name of LoginType values. | // LoginNames contains the name of LoginType values. | ||||
| @@ -49,6 +51,7 @@ var LoginNames = map[LoginType]string{ | |||||
| LoginPAM: "PAM", | LoginPAM: "PAM", | ||||
| LoginOAuth2: "OAuth2", | LoginOAuth2: "OAuth2", | ||||
| LoginSSPI: "SPNEGO with SSPI", | LoginSSPI: "SPNEGO with SSPI", | ||||
| LoginCloudBrain: "Cloud Brain", | |||||
| } | } | ||||
| // SecurityProtocolNames contains the name of SecurityProtocol values. | // SecurityProtocolNames contains the name of SecurityProtocol values. | ||||
| @@ -199,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)) | ||||
| } | } | ||||
| @@ -714,6 +719,8 @@ 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 LoginCloudBrain: | |||||
| user, err = LoginViaCloudBrain(user, login, password, source) | |||||
| default: | default: | ||||
| return nil, ErrUnsupportedLoginType | return nil, ErrUnsupportedLoginType | ||||
| } | } | ||||
| @@ -817,3 +824,46 @@ func UserSignIn(username, password string) (*User, error) { | |||||
| return nil, ErrUserNotExist{user.ID, user.Name, 0} | return nil, ErrUserNotExist{user.ID, user.Name, 0} | ||||
| } | } | ||||
| func LoginViaCloudBrain(user *User, login, password string, source *LoginSource) (*User, error) { | |||||
| token, err := cloudbrain.UserValidate(login, password) | |||||
| if err != nil { | |||||
| log.Error("UserValidate(%s) failed: %v", login, err) | |||||
| return nil, err | |||||
| } | |||||
| if user != nil { | |||||
| user.Token = token | |||||
| return user, UpdateUserCols(user, "token") | |||||
| } | |||||
| 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 { | |||||
| cloudBrainUser.Email = fmt.Sprintf("%s@cloudbrain", login) | |||||
| } | |||||
| user = &User{ | |||||
| LowerName: strings.ToLower(login), | |||||
| Name: login, | |||||
| Email: cloudBrainUser.Email, | |||||
| LoginType: source.Type, | |||||
| LoginSource: source.ID, | |||||
| LoginName: login, | |||||
| IsActive: true, | |||||
| Token: token, | |||||
| } | |||||
| err = CreateUser(user) | |||||
| if err != nil { | |||||
| log.Error("CreateUser(%s) failed: %v", login, err) | |||||
| return nil, err | |||||
| } | |||||
| return user, nil | |||||
| } | |||||
| @@ -165,6 +165,9 @@ type User struct { | |||||
| // Preferences | // Preferences | ||||
| DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"` | DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"` | ||||
| Theme string `xorm:"NOT NULL DEFAULT ''"` | Theme string `xorm:"NOT NULL DEFAULT ''"` | ||||
| //CloudBrain | |||||
| Token string `xorm:"VARCHAR(1024)"` | |||||
| } | } | ||||
| // SearchOrganizationsOptions options to filter organizations | // SearchOrganizationsOptions options to filter organizations | ||||
| @@ -0,0 +1,136 @@ | |||||
| package cloudbrain | |||||
| import ( | |||||
| "bytes" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "encoding/json" | |||||
| "errors" | |||||
| "io/ioutil" | |||||
| "net/http" | |||||
| "strings" | |||||
| ) | |||||
| const ( | |||||
| UrlToken = "/rest-server/api/v1/token/" | |||||
| UrlGetUserInfo = "/rest-server/api/v1/user/" | |||||
| TokenTypeBear = "Bearer " | |||||
| SuccessCode = "S000" | |||||
| ) | |||||
| type RespAuth struct { | |||||
| AccessToken string `json:"access_token"` | |||||
| RefreshToken string `json:"refresh_token"` | |||||
| TokenType string `json:"token_type"` | |||||
| ExpiresIn int `json:"expires_in"` | |||||
| Error string `json:"error"` | |||||
| ErrorDescription string `json:"error_description"` | |||||
| } | |||||
| type RespToken struct { | |||||
| Code string `json:"code"` | |||||
| Message string `json:"msg"` | |||||
| Payload PayloadToken `json:"payload"` | |||||
| } | |||||
| type PayloadToken struct { | |||||
| Username string `json:"username"` | |||||
| Token string `json:"token"` | |||||
| IsAdmin bool `json:"admin"` | |||||
| } | |||||
| type RespUserInfo struct { | |||||
| Code string `json:"code"` | |||||
| Message string `json:"msg"` | |||||
| Payload PayloadUserInfo `json:"payload"` | |||||
| } | |||||
| type PayloadUserInfo struct { | |||||
| UserInfo StUserInfo `json:"userInfo"` | |||||
| } | |||||
| type StUserInfo struct { | |||||
| Email string `json:"email"` | |||||
| } | |||||
| type CloudBrainUser struct { | |||||
| UserName string `json:"username"` | |||||
| Email string `json:"email"` | |||||
| } | |||||
| func UserValidate(username string, password string) (string, error) { | |||||
| values := map[string]string{"username": username, "password": password} | |||||
| jsonValue, _ := json.Marshal(values) | |||||
| resp, err := http.Post(setting.RestServerHost + UrlToken, | |||||
| "application/json", | |||||
| bytes.NewBuffer(jsonValue)) | |||||
| if err != nil { | |||||
| log.Error("req user center failed:" + err.Error()) | |||||
| return "", err | |||||
| } | |||||
| defer resp.Body.Close() | |||||
| body,err := ioutil.ReadAll(resp.Body) | |||||
| if err != nil { | |||||
| log.Error("read resp body failed:" + err.Error()) | |||||
| return "", err | |||||
| } | |||||
| var res RespToken | |||||
| err = json.Unmarshal(body, &res) | |||||
| if err != nil { | |||||
| log.Error("unmarshal res failed:" + err.Error()) | |||||
| return "", err | |||||
| } | |||||
| if res.Code != SuccessCode { | |||||
| log.Error("req rest-server for token failed:", res.Message) | |||||
| return "", errors.New(res.Message) | |||||
| } | |||||
| return res.Payload.Token, nil | |||||
| } | |||||
| func GetUserInfo(username string, token string) (*CloudBrainUser, error) { | |||||
| user := &CloudBrainUser{} | |||||
| client := &http.Client{} | |||||
| reqHttp,err := http.NewRequest("GET", setting.RestServerHost + UrlGetUserInfo + username, strings.NewReader("")) | |||||
| if err != nil { | |||||
| log.Error("new req failed:", err.Error()) | |||||
| return nil, err | |||||
| } | |||||
| reqHttp.Header.Set("Authorization", TokenTypeBear + token) | |||||
| resp,err := client.Do(reqHttp) | |||||
| if err != nil { | |||||
| log.Error("req rest-server failed:", err.Error()) | |||||
| return nil, err | |||||
| } | |||||
| defer resp.Body.Close() | |||||
| body,err := ioutil.ReadAll(resp.Body) | |||||
| if err != nil { | |||||
| log.Error("read resp body failed:", err.Error()) | |||||
| return nil, err | |||||
| } | |||||
| var res RespUserInfo | |||||
| err = json.Unmarshal(body, &res) | |||||
| if err != nil { | |||||
| log.Error("unmarshal resp failed:", err.Error()) | |||||
| return nil, err | |||||
| } | |||||
| if res.Code != SuccessCode { | |||||
| log.Error("get userInfo failed:", err.Error()) | |||||
| return nil, err | |||||
| } | |||||
| user.Email = res.Payload.UserInfo.Email | |||||
| return user, nil | |||||
| } | |||||
| @@ -434,6 +434,10 @@ var ( | |||||
| //cloudbrain config | //cloudbrain config | ||||
| CBAuthUser string | CBAuthUser string | ||||
| CBAuthPassword string | CBAuthPassword string | ||||
| ClientID string | |||||
| ClientSecret string | |||||
| UserCeterHost string | |||||
| RestServerHost string | |||||
| ) | ) | ||||
| // DateLang transforms standard language locale name to corresponding value in datetime plugin. | // DateLang transforms standard language locale name to corresponding value in datetime plugin. | ||||
| @@ -1105,6 +1109,10 @@ func NewContext() { | |||||
| sec = Cfg.Section("cloudbrain") | sec = Cfg.Section("cloudbrain") | ||||
| CBAuthUser = sec.Key("USER").MustString("cW4cMtH24eoWPE7X") | CBAuthUser = sec.Key("USER").MustString("cW4cMtH24eoWPE7X") | ||||
| CBAuthPassword = sec.Key("PWD").MustString("4BPmgvK2hb2Eywwyp4YZRY4B7yQf4DAC") | CBAuthPassword = sec.Key("PWD").MustString("4BPmgvK2hb2Eywwyp4YZRY4B7yQf4DAC") | ||||
| ClientID = sec.Key("CLIENT_ID").MustString("3Z377wcplxeE2qpycpjv") | |||||
| ClientSecret = sec.Key("CLIENT_SECRET").MustString("J5ykfVl2kcxW0H9cawSL") | |||||
| UserCeterHost = sec.Key("USER_CENTER_HOST").MustString("http://192.168.202.73:31441") | |||||
| RestServerHost = sec.Key("REST_SERVER_HOST").MustString("http://192.168.202.73") | |||||
| } | } | ||||
| func loadInternalToken(sec *ini.Section) string { | func loadInternalToken(sec *ini.Section) string { | ||||
| @@ -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=完成账号 | ||||
| @@ -615,6 +617,7 @@ email_notifications.disable=停用邮件通知 | |||||
| email_notifications.submit=邮件通知设置 | email_notifications.submit=邮件通知设置 | ||||
| [dataset] | [dataset] | ||||
| alert=如果要发起云脑任务,请上传zip格式的数据集 | |||||
| dataset=数据集 | dataset=数据集 | ||||
| dataset_setting=数据集设置 | dataset_setting=数据集设置 | ||||
| title=名称 | title=名称 | ||||
| @@ -5,13 +5,6 @@ | |||||
| package repo | package repo | ||||
| import ( | import ( | ||||
| contexExt "context" | |||||
| "encoding/json" | |||||
| "fmt" | |||||
| "net/http" | |||||
| "strconv" | |||||
| "strings" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| @@ -20,6 +13,12 @@ import ( | |||||
| "code.gitea.io/gitea/modules/storage" | "code.gitea.io/gitea/modules/storage" | ||||
| "code.gitea.io/gitea/modules/upload" | "code.gitea.io/gitea/modules/upload" | ||||
| "code.gitea.io/gitea/modules/worker" | "code.gitea.io/gitea/modules/worker" | ||||
| contexExt "context" | |||||
| "encoding/json" | |||||
| "fmt" | |||||
| "net/http" | |||||
| "strconv" | |||||
| "strings" | |||||
| gouuid "github.com/satori/go.uuid" | gouuid "github.com/satori/go.uuid" | ||||
| ) | ) | ||||
| @@ -30,9 +29,12 @@ const ( | |||||
| DecompressFailed = "1" | DecompressFailed = "1" | ||||
| ) | ) | ||||
| type PublicDataset struct { | |||||
| type CloudBrainDataset struct { | |||||
| UUID string `json:"id"` | |||||
| Name string `json:"name"` | Name string `json:"name"` | ||||
| Path string `json:"path"` | |||||
| Path string `json:"place"` | |||||
| UserName string `json:"provider"` | |||||
| CreateTime string `json:"created_at"` | |||||
| } | } | ||||
| func RenderAttachmentSettings(ctx *context.Context) { | func RenderAttachmentSettings(ctx *context.Context) { | ||||
| @@ -627,22 +629,55 @@ func QueryAllPublicDataset(ctx *context.Context){ | |||||
| if err != nil { | if err != nil { | ||||
| ctx.JSON(200, map[string]string{ | ctx.JSON(200, map[string]string{ | ||||
| "result_code": "-1", | "result_code": "-1", | ||||
| "error_msg": err.Error(), | |||||
| "data": "", | |||||
| }) | |||||
| return | |||||
| } | |||||
| queryDatasets(ctx, "admin", attachs) | |||||
| } | |||||
| func QueryPrivateDataset(ctx *context.Context){ | |||||
| username := ctx.Params(":username") | |||||
| attachs, err := models.GetPrivateAttachments(username) | |||||
| if err != nil { | |||||
| ctx.JSON(200, map[string]string{ | |||||
| "result_code": "-1", | |||||
| "error_msg": err.Error(), | |||||
| "data": "", | "data": "", | ||||
| }) | }) | ||||
| return | return | ||||
| } | } | ||||
| var publicDatasets []PublicDataset | |||||
| queryDatasets(ctx, username, attachs) | |||||
| } | |||||
| func queryDatasets(ctx *context.Context, username string, attachs []*models.Attachment) { | |||||
| var datasets []CloudBrainDataset | |||||
| for _, attch := range attachs { | for _, attch := range attachs { | ||||
| publicDatasets = append(publicDatasets, PublicDataset{attch.Name, | |||||
| models.AttachmentRelativePath(attch.UUID)}) | |||||
| has,err := storage.Attachments.HasObject(models.AttachmentRelativePath(attch.UUID)) | |||||
| if err != nil || !has { | |||||
| continue | |||||
| } | |||||
| datasets = append(datasets, CloudBrainDataset{attch.UUID, | |||||
| attch.Name, | |||||
| setting.Attachment.Minio.RealPath + | |||||
| setting.Attachment.Minio.Bucket + "/" + | |||||
| setting.Attachment.Minio.BasePath + | |||||
| models.AttachmentRelativePath(attch.UUID) + | |||||
| attch.UUID, | |||||
| username, | |||||
| attch.CreatedUnix.Format("2006-01-02 03:04:05")}) | |||||
| } | } | ||||
| data,err := json.Marshal(publicDatasets) | |||||
| data,err := json.Marshal(datasets) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("json.Marshal failed:", err.Error()) | log.Error("json.Marshal failed:", err.Error()) | ||||
| ctx.JSON(200, map[string]string{ | ctx.JSON(200, map[string]string{ | ||||
| "result_code": "-1", | "result_code": "-1", | ||||
| "error_msg": err.Error(), | |||||
| "data": "", | "data": "", | ||||
| }) | }) | ||||
| return | return | ||||
| @@ -650,6 +685,8 @@ func QueryAllPublicDataset(ctx *context.Context){ | |||||
| ctx.JSON(200, map[string]string{ | ctx.JSON(200, map[string]string{ | ||||
| "result_code": "0", | "result_code": "0", | ||||
| "error_msg": "", | |||||
| "data": string(data), | "data": string(data), | ||||
| }) | }) | ||||
| return | |||||
| } | } | ||||
| @@ -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"). | ||||
| @@ -534,8 +535,9 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Post("/decompress_done_notify", repo.UpdateAttachmentDecompressState) | m.Post("/decompress_done_notify", repo.UpdateAttachmentDecompressState) | ||||
| }) | }) | ||||
| m.Group("/attachments/public", func() { | |||||
| m.Get("/query", repo.QueryAllPublicDataset) | |||||
| m.Group("/attachments", func() { | |||||
| m.Get("/public/query", repo.QueryAllPublicDataset) | |||||
| m.Get("/private/:username", repo.QueryPrivateDataset) | |||||
| }, reqBasicAuth) | }, reqBasicAuth) | ||||
| m.Group("/:username", func() { | m.Group("/:username", func() { | ||||
| @@ -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") | ||||
| @@ -3,6 +3,11 @@ | |||||
| {{template "repo/header" .}} | {{template "repo/header" .}} | ||||
| <form class="ui container" action="{{.Link}}" method="post"> | <form class="ui container" action="{{.Link}}" method="post"> | ||||
| <input name="id" value="{{.dataset.ID}}" type="hidden" /> | <input name="id" value="{{.dataset.ID}}" type="hidden" /> | ||||
| <!-- | |||||
| <span class="alert" style="font-size:20px;color:red"> | |||||
| <strong>{{.i18n.Tr "dataset.alert"}}</strong> | |||||
| </span> | |||||
| --> | |||||
| <div id="datasetId" datasetId="{{.dataset.ID}}"> | <div id="datasetId" datasetId="{{.dataset.ID}}"> | ||||
| {{.CsrfTokenHtml}} | {{.CsrfTokenHtml}} | ||||
| {{template "base/alert" .}} | {{template "base/alert" .}} | ||||
| @@ -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> | ||||