Browse Source

Merge branch 'V202108' into fix-208

pull/218/head
avadesian 3 years ago
parent
commit
36200a4c31
14 changed files with 251 additions and 40 deletions
  1. +4
    -4
      models/user.go
  2. +12
    -0
      models/user_mail.go
  3. +1
    -0
      modules/convert/convert.go
  4. +2
    -0
      modules/structs/user.go
  5. +47
    -11
      routers/repo/attachment.go
  6. +28
    -7
      routers/repo/dataset.go
  7. +33
    -8
      routers/repo/view.go
  8. +1
    -1
      routers/routes/routes.go
  9. +33
    -1
      routers/secure/user.go
  10. +22
    -1
      routers/user/auth.go
  11. +12
    -0
      routers/user/setting/profile.go
  12. +54
    -6
      templates/repo/home.tmpl
  13. +1
    -0
      web_src/js/components/MinioUploader.vue
  14. +1
    -1
      web_src/less/_repository.less

+ 4
- 4
models/user.go View File

@@ -976,12 +976,12 @@ func CreateUser(u *User) (err error) {
result, err := blockchain.CreateBlockchainAccount() result, err := blockchain.CreateBlockchainAccount()
if err != nil { if err != nil {
log.Error("createBlockchainAccount failed:", err.Error()) log.Error("createBlockchainAccount failed:", err.Error())
return err
//return err
} else {
u.PublicKey = result.Payload["publickey"].(string)
u.PrivateKey = result.Payload["privatekey"].(string)
} }


u.PublicKey = result.Payload["publickey"].(string)
u.PrivateKey = result.Payload["privatekey"].(string)

sess := x.NewSession() sess := x.NewSession()
defer sess.Close() defer sess.Close()
if err = sess.Begin(); err != nil { if err = sess.Begin(); err != nil {


+ 12
- 0
models/user_mail.go View File

@@ -80,6 +80,18 @@ func GetEmailAddressByID(uid, id int64) (*EmailAddress, error) {
return email, nil return email, nil
} }


// GetEmailAddressByIDAndEmail gets a user's email address by ID and email
func GetEmailAddressByIDAndEmail(uid int64, emailAddr string) (*EmailAddress, error) {
// User ID is required for security reasons
email := &EmailAddress{UID: uid, Email: emailAddr}
if has, err := x.Get(email); err != nil {
return nil, err
} else if !has {
return nil, nil
}
return email, nil
}

func isEmailActive(e Engine, email string, userID, emailID int64) (bool, error) { func isEmailActive(e Engine, email string, userID, emailID int64) (bool, error) {
if len(email) == 0 { if len(email) == 0 {
return true, nil return true, nil


+ 1
- 0
modules/convert/convert.go View File

@@ -335,6 +335,7 @@ func ToUser(user *models.User, signed, authed bool) *api.User {
AvatarURL: user.AvatarLink(), AvatarURL: user.AvatarLink(),
FullName: markup.Sanitize(user.FullName), FullName: markup.Sanitize(user.FullName),
Created: user.CreatedUnix.AsTime(), Created: user.CreatedUnix.AsTime(),
IsActive: user.IsActive,
} }
// hide primary email if API caller is anonymous or user keep email private // hide primary email if API caller is anonymous or user keep email private
if signed && (!user.KeepEmailPrivate || authed) { if signed && (!user.KeepEmailPrivate || authed) {


+ 2
- 0
modules/structs/user.go View File

@@ -26,6 +26,8 @@ type User struct {
Language string `json:"language"` Language string `json:"language"`
// Is the user an administrator // Is the user an administrator
IsAdmin bool `json:"is_admin"` IsAdmin bool `json:"is_admin"`
// Is the user active
IsActive bool `json:"is_active"`
// swagger:strfmt date-time // swagger:strfmt date-time
LastLogin time.Time `json:"last_login,omitempty"` LastLogin time.Time `json:"last_login,omitempty"`
// swagger:strfmt date-time // swagger:strfmt date-time


+ 47
- 11
routers/repo/attachment.go View File

@@ -141,6 +141,35 @@ func DeleteAttachment(ctx *context.Context) {
}) })
} }


func DownloadUserIsOrgOrCollaboration(ctx *context.Context, attach *models.Attachment) bool {
dataset, err := models.GetDatasetByID(attach.DatasetID)
if err != nil {
log.Info("query dataset error")
} else {
repo, err := models.GetRepositoryByID(dataset.RepoID)
if err != nil {
log.Info("query repo error.")
} else {
repo.GetOwner()
if ctx.User != nil {

if repo.Owner.IsOrganization() {
if repo.Owner.IsUserPartOfOrg(ctx.User.ID) {
log.Info("org user may visit the attach.")
return true
}
}
isCollaborator, _ := repo.IsCollaborator(ctx.User.ID)
if isCollaborator {
log.Info("Collaborator user may visit the attach.")
return true
}
}
}
}
return false
}

// GetAttachment serve attachements // GetAttachment serve attachements
func GetAttachment(ctx *context.Context) { func GetAttachment(ctx *context.Context) {
typeCloudBrain := ctx.QueryInt("type") typeCloudBrain := ctx.QueryInt("type")
@@ -165,16 +194,29 @@ func GetAttachment(ctx *context.Context) {
ctx.ServerError("LinkedRepository", err) ctx.ServerError("LinkedRepository", err)
return return
} }
dataSet, err := attach.LinkedDataSet()
if err != nil {
ctx.ServerError("LinkedDataSet", err)
return
}

if repository == nil && dataSet != nil {
repository, _ = models.GetRepositoryByID(dataSet.RepoID)
unitType = models.UnitTypeDatasets
}


if repository == nil { //If not linked if repository == nil { //If not linked
if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) && attach.IsPrivate { //We block if not the uploader
//if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) && attach.IsPrivate { //We block if not the uploader
//log.Info("ctx.IsSigned =" + fmt.Sprintf("%v", ctx.IsSigned))
if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) && attach.IsPrivate && !DownloadUserIsOrgOrCollaboration(ctx, attach) { //We block if not the uploader
ctx.Error(http.StatusNotFound) ctx.Error(http.StatusNotFound)
return return
} }

} else { //If we have the repository we check access } else { //If we have the repository we check access
perm, err := models.GetUserRepoPermission(repository, ctx.User)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err.Error())
perm, errPermission := models.GetUserRepoPermission(repository, ctx.User)
if errPermission != nil {
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", errPermission.Error())
return return
} }
if !perm.CanRead(unitType) { if !perm.CanRead(unitType) {
@@ -183,12 +225,6 @@ func GetAttachment(ctx *context.Context) {
} }
} }


dataSet, err := attach.LinkedDataSet()
if err != nil {
ctx.ServerError("LinkedDataSet", err)
return
}

if dataSet != nil { if dataSet != nil {
isPermit, err := models.GetUserDataSetPermission(dataSet, ctx.User) isPermit, err := models.GetUserDataSetPermission(dataSet, ctx.User)
if err != nil { if err != nil {
@@ -205,7 +241,7 @@ func GetAttachment(ctx *context.Context) {
if setting.Attachment.StoreType == storage.MinioStorageType { if setting.Attachment.StoreType == storage.MinioStorageType {
url := "" url := ""
if typeCloudBrain == models.TypeCloudBrainOne { if typeCloudBrain == models.TypeCloudBrainOne {
url, err = storage.Attachments.PresignedGetURL(setting.Attachment.Minio.BasePath + attach.RelativePath(), attach.Name)
url, err = storage.Attachments.PresignedGetURL(setting.Attachment.Minio.BasePath+attach.RelativePath(), attach.Name)
if err != nil { if err != nil {
ctx.ServerError("PresignedGetURL", err) ctx.ServerError("PresignedGetURL", err)
return return


+ 28
- 7
routers/repo/dataset.go View File

@@ -1,15 +1,13 @@
package repo package repo


import ( import (
"sort"

"code.gitea.io/gitea/modules/setting"

"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"sort"
) )


const ( const (
@@ -24,19 +22,42 @@ func MustEnableDataset(ctx *context.Context) {
} }
} }


func filterPrivateAttachments(ctx *context.Context, list []*models.Attachment) []*models.Attachment {
func newFilterPrivateAttachments(ctx *context.Context, list []*models.Attachment, repo *models.Repository) []*models.Attachment {

if ctx.Repo.CanWrite(models.UnitTypeDatasets) { if ctx.Repo.CanWrite(models.UnitTypeDatasets) {
log.Info("can write.")
return list return list
} else { } else {
if repo.Owner == nil {
repo.GetOwner()
}
permission := false
if repo.Owner.IsOrganization() && ctx.User != nil {
if repo.Owner.IsUserPartOfOrg(ctx.User.ID) {
log.Info("user is member of org.")
permission = true
}
}
if !permission && ctx.User != nil {
isCollaborator, _ := repo.IsCollaborator(ctx.User.ID)
if isCollaborator {
log.Info("Collaborator user may visit the attach.")
permission = true
}
}

var publicList []*models.Attachment var publicList []*models.Attachment
for _, attach := range list { for _, attach := range list {
if !attach.IsPrivate { if !attach.IsPrivate {
publicList = append(publicList, attach) publicList = append(publicList, attach)
} else {
if permission {
publicList = append(publicList, attach)
}
} }
} }
return publicList return publicList
} }

} }


func DatasetIndex(ctx *context.Context) { func DatasetIndex(ctx *context.Context) {
@@ -60,7 +81,7 @@ func DatasetIndex(ctx *context.Context) {
ctx.ServerError("GetDatasetAttachments", err) ctx.ServerError("GetDatasetAttachments", err)
return return
} }
attachments := filterPrivateAttachments(ctx, dataset.Attachments)
attachments := newFilterPrivateAttachments(ctx, dataset.Attachments, repo)


ctx.Data["SortType"] = ctx.Query("sort") ctx.Data["SortType"] = ctx.Query("sort")
switch ctx.Query("sort") { switch ctx.Query("sort") {


+ 33
- 8
routers/repo/view.go View File

@@ -569,10 +569,19 @@ func safeURL(address string) string {
} }


type ContributorInfo struct { type ContributorInfo struct {
UserInfo *models.User
Email string // for contributor who is not a registered user
UserInfo *models.User // nil for contributor who is not a registered user
Email string
CommitCnt int
} }


func getContributorInfo(contributorInfos []*ContributorInfo, email string) *ContributorInfo{
for _, c := range contributorInfos {
if strings.Compare(c.Email,email) == 0 {
return c
}
}
return nil
}
// Home render repository home page // Home render repository home page
func Home(ctx *context.Context) { func Home(ctx *context.Context) {
if len(ctx.Repo.Units) > 0 { if len(ctx.Repo.Units) > 0 {
@@ -581,15 +590,31 @@ func Home(ctx *context.Context) {
if err == nil && contributors != nil { if err == nil && contributors != nil {
var contributorInfos []*ContributorInfo var contributorInfos []*ContributorInfo
for _, c := range contributors { for _, c := range contributors {
// get user info from committer email
user, err := models.GetUserByEmail(c.Email) user, err := models.GetUserByEmail(c.Email)
if err == nil { if err == nil {
contributorInfos = append(contributorInfos, &ContributorInfo{
user, c.Email,
})
// committer is system user, get info through user's primary email
existedContributorInfo := getContributorInfo(contributorInfos,user.Email)
if existedContributorInfo != nil {
// existed: same primary email, different committer name
existedContributorInfo.CommitCnt += c.CommitCnt
}else{
// new committer info
contributorInfos = append(contributorInfos, &ContributorInfo{
user, user.Email,c.CommitCnt,
})
}
} else { } else {
contributorInfos = append(contributorInfos, &ContributorInfo{
nil, c.Email,
})
// committer is not system user
existedContributorInfo := getContributorInfo(contributorInfos,c.Email)
if existedContributorInfo != nil {
// existed: same primary email, different committer name
existedContributorInfo.CommitCnt += c.CommitCnt
}else{
contributorInfos = append(contributorInfos, &ContributorInfo{
nil, c.Email,c.CommitCnt,
})
}
} }
} }
ctx.Data["ContributorInfo"] = contributorInfos ctx.Data["ContributorInfo"] = contributorInfos


+ 1
- 1
routers/routes/routes.go View File

@@ -1129,7 +1129,7 @@ func RegisterRoutes(m *macaron.Macaron) {


//secure api, //secure api,
m.Group("/secure", func() { m.Group("/secure", func() {
m.Post("/user", binding.Bind(structs.CreateUserOption{}), secure.CreateUser)
m.Post("/user", binding.BindIgnErr(structs.CreateUserOption{}), secure.CreateUser)
}, reqBasicAuth) }, reqBasicAuth)


m.Group("/api/internal", func() { m.Group("/api/internal", func() {


+ 33
- 1
routers/secure/user.go View File

@@ -7,6 +7,7 @@ package secure


import ( import (
"net/http" "net/http"
"strings"


"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@@ -75,11 +76,19 @@ func CreateUser(ctx *context.Context, form api.CreateUserOption) {
u.MustChangePassword = *form.MustChangePassword u.MustChangePassword = *form.MustChangePassword
} }


if strings.Contains(form.Email, " ") {
log.Error("CreateUser failed: email(%s) contains blank space", form.Email, ctx.Data["MsgID"])
ctx.JSON(http.StatusBadRequest, map[string]string{
"error_msg": "Email contains blank space",
})
return
}

parseLoginSource(ctx, u, form.SourceID, form.LoginName) parseLoginSource(ctx, u, form.SourceID, form.LoginName)
if ctx.Written() { if ctx.Written() {
return return
} }
if !password.IsComplexEnough(form.Password) {
if !password.IsComplexEnough(form.Password) || len(form.Password) < setting.MinPasswordLength {
log.Error("CreateUser failed: PasswordComplexity", ctx.Data["MsgID"]) log.Error("CreateUser failed: PasswordComplexity", ctx.Data["MsgID"])
ctx.JSON(http.StatusBadRequest, map[string]string{ ctx.JSON(http.StatusBadRequest, map[string]string{
"error_msg": "PasswordComplexity", "error_msg": "PasswordComplexity",
@@ -104,6 +113,29 @@ func CreateUser(ctx *context.Context, form api.CreateUserOption) {
} }
return return
} }

err := models.AddEmailAddress(&models.EmailAddress{
UID: u.ID,
Email: form.Email,
IsActivated: !setting.Service.RegisterEmailConfirm,
})

if err != nil {
log.Error("AddEmailAddress failed:%v", err.Error(), ctx.Data["MsgID"])
ctx.JSON(http.StatusInternalServerError, map[string]string{
"error_msg": err.Error(),
})
return
}

// Send confirmation email
if setting.Service.RegisterEmailConfirm{
mailer.SendActivateAccountMail(ctx.Locale, u)
if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
log.Error("Set cache(MailResendLimit) fail: %v", err)
}
}

log.Trace("Account created (%s): %s", ctx.User.Name, u.Name, ctx.Data["MsgID"]) log.Trace("Account created (%s): %s", ctx.User.Name, u.Name, ctx.Data["MsgID"])


// Send email notification. // Send email notification.


+ 22
- 1
routers/user/auth.go View File

@@ -1165,7 +1165,19 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo
} }
return return
} }
log.Trace("Account created: %s", u.Name)
log.Trace("Account created: %s", u.Name, ctx.Data["MsgID"])

err := models.AddEmailAddress(&models.EmailAddress{
UID: u.ID,
Email: form.Email,
IsActivated: !setting.Service.RegisterEmailConfirm,
})

if err != nil {
log.Error("AddEmailAddress failed:%v", err.Error(), ctx.Data["MsgID"])
ctx.ServerError("AddEmailAddress", err)
return
}


// Auto-set admin for the only user. // Auto-set admin for the only user.
if models.CountUsers() == 1 { if models.CountUsers() == 1 {
@@ -1254,6 +1266,15 @@ func Activate(ctx *context.Context) {
log.Error("Error storing session: %v", err) log.Error("Error storing session: %v", err)
} }


email, err := models.GetEmailAddressByIDAndEmail(user.ID, user.Email)
if err != nil || email == nil{
log.Error("GetEmailAddressByIDAndEmail failed", ctx.Data["MsgID"])
} else {
if err := email.Activate(); err != nil {
log.Error("Activate failed: %v", err, ctx.Data["MsgID"])
}
}

ctx.Flash.Success(ctx.Tr("auth.account_activated")) ctx.Flash.Success(ctx.Tr("auth.account_activated"))
ctx.Redirect(setting.AppSubURL + "/") ctx.Redirect(setting.AppSubURL + "/")
return return


+ 12
- 0
routers/user/setting/profile.go View File

@@ -96,6 +96,18 @@ func ProfilePost(ctx *context.Context, form auth.UpdateProfileForm) {
ctx.User.Location = form.Location ctx.User.Location = form.Location
ctx.User.Language = form.Language ctx.User.Language = form.Language
ctx.User.Description = form.Description ctx.User.Description = form.Description
isUsed, err := models.IsEmailUsed(form.Email)
if err != nil {
ctx.ServerError("IsEmailUsed", err)
return
}

if isUsed {
ctx.Flash.Error(ctx.Tr("form.email_been_used"))
ctx.Redirect(setting.AppSubURL + "/user/settings")
return
}

if err := models.UpdateUserSetting(ctx.User); err != nil { if err := models.UpdateUserSetting(ctx.User); err != nil {
if _, ok := err.(models.ErrEmailAlreadyUsed); ok { if _, ok := err.(models.ErrEmailAlreadyUsed); ok {
ctx.Flash.Error(ctx.Tr("form.email_been_used")) ctx.Flash.Error(ctx.Tr("form.email_been_used"))


+ 54
- 6
templates/repo/home.tmpl View File

@@ -4,6 +4,48 @@
font-size: 1.0em; font-size: 1.0em;
margin-bottom: 1.0rem; margin-bottom: 1.0rem;
} }
#contributorInfo > a:nth-child(n+25){
display:none;
}
#contributorInfo > a{
width: 2.0em;
float: left;
margin: .25em;
}
#contributorInfo > a.circular{
height: 2.0em;
padding: 0;
overflow: hidden;
letter-spacing:1.0em;
text-indent: 0.6em;
line-height: 2.0em;
text-transform:capitalize;
color: #FFF;
}
#contributorInfo > a.circular:nth-child(9n+1){
background-color: #4ccdec;
}
#contributorInfo > a.circular:nth-child(9n+2){
background-color: #e0b265;
}
#contributorInfo > a.circular:nth-child(9n+3){
background-color: #d884b7;
}
#contributorInfo > a.circular:nth-child(9n+4){
background-color: #8c6bdc;
}
#contributorInfo > a.circular:nth-child(9n+5){
background-color: #3cb99f;
}
#contributorInfo > a.circular:nth-child(9n+6){
background-color: #6995b9;
}
#contributorInfo > a.circular:nth-child(9n+7){
background-color: #ab91a7;
}
#contributorInfo > a.circular:nth-child(9n+8){
background-color: #bfd0aa;
}
</style> </style>
<div class="repository file list"> <div class="repository file list">
{{template "repo/header" .}} {{template "repo/header" .}}
@@ -211,17 +253,15 @@
<h4 class="ui header"> <h4 class="ui header">
<strong>贡献者 ({{len .ContributorInfo}})</strong> <strong>贡献者 ({{len .ContributorInfo}})</strong>
<div class="ui right"> <div class="ui right">
<a class="text grey" href="">全部 {{svg "octicon-chevron-right" 16}}</a>
<a class="membersmore text grey" href="javascript:;">全部 {{svg "octicon-chevron-right" 16}}</a>
</div> </div>
</h4> </h4>

<div class="ui members">
<div class="ui members" id="contributorInfo">
{{range .ContributorInfo}} {{range .ContributorInfo}}
{{/*<img class="ui avatar image" src="{{.UserInfo.RelAvatarLink}}" alt=""/>&nbsp;&nbsp;<a href="{{AppSubUrl}}/{{.UserInfo.Name}}">{{.UserInfo.Name}}</a>*/}}
{{if .UserInfo}} {{if .UserInfo}}
<a href="{{AppSubUrl}}/{{.UserInfo.Name}}"><img class="ui avatar image" src="{{.UserInfo.RelAvatarLink}}" alt=""/></a>
<a href="{{AppSubUrl}}/{{.UserInfo.Name}}"><img class="ui avatar image" src="{{.UserInfo.RelAvatarLink}}"></a>
{{else if .Email}} {{else if .Email}}
<a href="mailto:{{.Email}}"><img class="ui avatar image" src="{{AvatarLink .Email}}" alt=""/></a>
<a href="mailto:{{.Email}}" class="circular ui button">{{.Email}}</a>
{{end}} {{end}}
{{end}} {{end}}
</div> </div>
@@ -233,4 +273,12 @@
</div> </div>
</div> </div>

<script type="text/javascript">
$(document).ready(function(){
$(".membersmore").click(function(){
$("#contributorInfo > a:nth-child(n+25)").show();
});
});
</script>
{{template "base/footer" .}} {{template "base/footer" .}}

+ 1
- 0
web_src/js/components/MinioUploader.vue View File

@@ -335,6 +335,7 @@ export default {


async function uploadMinio(url, e) { async function uploadMinio(url, e) {
const res = await axios.put(url, e.target.result); const res = await axios.put(url, e.target.result);
delete e.target.result
etags[currentChunk] = res.headers.etag; etags[currentChunk] = res.headers.etag;
} }




+ 1
- 1
web_src/less/_repository.less View File

@@ -2685,7 +2685,7 @@ tbody.commit-list {
width: 1127px; width: 1127px;
} }
th .message-wrapper { th .message-wrapper {
max-width: 680px;
max-width: 510px;
} }
} }




Loading…
Cancel
Save