Browse Source

Merge pull request 'dev-query-public' (#109) from dev-query-public into develop

Reviewed-by: stardust <denglf@pcl.ac.cn>
tags/v0.1.8
yuyuanshifu 4 years ago
parent
commit
b37eae2e49
18 changed files with 365 additions and 17 deletions
  1. BIN
      docs/opendata对外接口文档.docx
  2. BIN
      docs/云脑用户登录流程图.png
  3. BIN
      docs/开源社区与云脑平台对接方案.docx
  4. BIN
      docs/用户登录流程图.png
  5. +17
    -1
      models/attachment.go
  6. +50
    -0
      models/login_source.go
  7. +3
    -0
      models/user.go
  8. +136
    -0
      modules/auth/cloudbrain/cloudbrain.go
  9. +0
    -0
      modules/cloudbrain/cloudbrain.go
  10. +8
    -0
      modules/setting/setting.go
  11. +3
    -0
      options/locale/locale_zh-CN.ini
  12. +50
    -13
      routers/repo/attachment.go
  13. +4
    -2
      routers/routes/routes.go
  14. +20
    -0
      routers/user/auth.go
  15. +5
    -0
      templates/repo/datasets/index.tmpl
  16. +10
    -0
      templates/user/auth/signin_cloud_brain.tmpl
  17. +55
    -0
      templates/user/auth/signin_cloudbrain.tmpl
  18. +4
    -1
      templates/user/auth/signin_navbar.tmpl

BIN
docs/opendata对外接口文档.docx View File


BIN
docs/云脑用户登录流程图.png View File

Before After
Width: 497  |  Height: 688  |  Size: 39 kB

BIN
docs/开源社区与云脑平台对接方案.docx View File


BIN
docs/用户登录流程图.png View File

Before After
Width: 897  |  Height: 803  |  Size: 73 kB

+ 17
- 1
models/attachment.go View File

@@ -6,6 +6,7 @@ package models

import (
"bytes"
"code.gitea.io/gitea/modules/log"
"fmt"
"io"
"path"
@@ -353,5 +354,20 @@ func GetAllPublicAttachments() ([]*Attachment, error) {

func getAllPublicAttachments(e Engine) ([]*Attachment, error) {
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)
}


+ 50
- 0
models/login_source.go View File

@@ -6,6 +6,7 @@
package models

import (
"code.gitea.io/gitea/modules/auth/cloudbrain"
"crypto/tls"
"encoding/json"
"errors"
@@ -39,6 +40,7 @@ const (
LoginDLDAP // 5
LoginOAuth2 // 6
LoginSSPI // 7
LoginCloudBrain // 8
)

// LoginNames contains the name of LoginType values.
@@ -49,6 +51,7 @@ var LoginNames = map[LoginType]string{
LoginPAM: "PAM",
LoginOAuth2: "OAuth2",
LoginSSPI: "SPNEGO with SSPI",
LoginCloudBrain: "Cloud Brain",
}

// SecurityProtocolNames contains the name of SecurityProtocol values.
@@ -199,6 +202,8 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
source.Cfg = new(OAuth2Config)
case LoginSSPI:
source.Cfg = new(SSPIConfig)
case LoginCloudBrain:
source.Cfg = new(CloudBrainConfig)
default:
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))
case LoginPAM:
user, err = LoginViaPAM(user, login, password, source.ID, source.Cfg.(*PAMConfig))
case LoginCloudBrain:
user, err = LoginViaCloudBrain(user, login, password, source)
default:
return nil, ErrUnsupportedLoginType
}
@@ -817,3 +824,46 @@ func UserSignIn(username, password string) (*User, error) {

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
}

+ 3
- 0
models/user.go View File

@@ -165,6 +165,9 @@ type User struct {
// Preferences
DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"`
Theme string `xorm:"NOT NULL DEFAULT ''"`

//CloudBrain
Token string `xorm:"VARCHAR(1024)"`
}

// SearchOrganizationsOptions options to filter organizations


+ 136
- 0
modules/auth/cloudbrain/cloudbrain.go View File

@@ -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
}

+ 0
- 0
modules/cloudbrain/cloudbrain.go View File


+ 8
- 0
modules/setting/setting.go View File

@@ -434,6 +434,10 @@ var (
//cloudbrain config
CBAuthUser string
CBAuthPassword string
ClientID string
ClientSecret string
UserCeterHost string
RestServerHost string
)

// DateLang transforms standard language locale name to corresponding value in datetime plugin.
@@ -1105,6 +1109,10 @@ func NewContext() {
sec = Cfg.Section("cloudbrain")
CBAuthUser = sec.Key("USER").MustString("cW4cMtH24eoWPE7X")
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 {


+ 3
- 0
options/locale/locale_zh-CN.ini View File

@@ -192,6 +192,7 @@ no_reply_address_helper=具有隐藏电子邮件地址的用户的域名。例

[home]
uname_holder=登录名或电子邮箱地址
uname_holder_cloud_brain=云脑登录名
password_holder=密码
switch_dashboard_context=切换控制面板用户
my_repos=项目列表
@@ -267,6 +268,7 @@ twofa_passcode_incorrect=你的验证码不正确。如果你丢失了你的设
twofa_scratch_token_incorrect=你的验证口令不正确。
login_userpass=登录
login_openid=OpenID
login_cloudbrain=云脑用户登录
oauth_signup_tab=注册帐号
oauth_signup_title=添加电子邮件和密码 (用于帐号恢复)
oauth_signup_submit=完成账号
@@ -615,6 +617,7 @@ email_notifications.disable=停用邮件通知
email_notifications.submit=邮件通知设置

[dataset]
alert=如果要发起云脑任务,请上传zip格式的数据集
dataset=数据集
dataset_setting=数据集设置
title=名称


+ 50
- 13
routers/repo/attachment.go View File

@@ -5,13 +5,6 @@
package repo

import (
contexExt "context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
@@ -20,6 +13,12 @@ import (
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/worker"
contexExt "context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"

gouuid "github.com/satori/go.uuid"
)
@@ -30,9 +29,12 @@ const (
DecompressFailed = "1"
)

type PublicDataset struct {
type CloudBrainDataset struct {
UUID string `json:"id"`
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) {
@@ -627,22 +629,55 @@ func QueryAllPublicDataset(ctx *context.Context){
if err != nil {
ctx.JSON(200, map[string]string{
"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": "",
})
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 {
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 {
log.Error("json.Marshal failed:", err.Error())
ctx.JSON(200, map[string]string{
"result_code": "-1",
"error_msg": err.Error(),
"data": "",
})
return
@@ -650,6 +685,8 @@ func QueryAllPublicDataset(ctx *context.Context){

ctx.JSON(200, map[string]string{
"result_code": "0",
"error_msg": "",
"data": string(data),
})
return
}

+ 4
- 2
routers/routes/routes.go View File

@@ -305,6 +305,7 @@ func RegisterRoutes(m *macaron.Macaron) {
// ***** START: User *****
m.Group("/user", func() {
m.Get("/login", user.SignIn)
m.Get("/login/cloud_brain", user.SignInCloudBrain)
m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost)
m.Group("", func() {
m.Combo("/login/openid").
@@ -534,8 +535,9 @@ func RegisterRoutes(m *macaron.Macaron) {
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)

m.Group("/:username", func() {


+ 20
- 0
routers/user/auth.go View File

@@ -36,6 +36,8 @@ const (
tplMustChangePassword = "user/auth/change_passwd"
// tplSignIn template for sign in page
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 base.TplName = "user/auth/signup"
// TplActivate template path for activate user
@@ -143,10 +145,28 @@ func SignIn(ctx *context.Context) {
ctx.Data["PageIsSignIn"] = true
ctx.Data["PageIsLogin"] = true
ctx.Data["EnableSSPI"] = models.IsSSPIEnabled()
ctx.Data["EnableCloudBrain"] = true

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
func SignInPost(ctx *context.Context, form auth.SignInForm) {
ctx.Data["Title"] = ctx.Tr("sign_in")


+ 5
- 0
templates/repo/datasets/index.tmpl View File

@@ -3,6 +3,11 @@
{{template "repo/header" .}}
<form class="ui container" action="{{.Link}}" method="post">
<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}}">
{{.CsrfTokenHtml}}
{{template "base/alert" .}}


+ 10
- 0
templates/user/auth/signin_cloud_brain.tmpl View File

@@ -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" .}}

+ 55
- 0
templates/user/auth/signin_cloudbrain.tmpl View File

@@ -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>

+ 4
- 1
templates/user/auth/signin_navbar.tmpl View File

@@ -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">
<a class="{{if .PageIsLogin}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login">
{{.i18n.Tr "auth.login_userpass"}}
</a>
<a class="{{if .PageIsCloudBrainLogin}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login/cloud_brain">
{{.i18n.Tr "auth.login_cloudbrain"}}
</a>
{{if .EnableOpenIDSignIn}}
<a class="{{if .PageIsLoginOpenID}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login/openid">
<i class="fa fa-openid"></i>


Loading…
Cancel
Save