mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-04-25 04:57:31 +00:00
[gitea] week 2025-14 cherry pick (gitea/main -> forgejo) (#7486)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7486 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org> Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
commit
442958df1d
10 changed files with 80 additions and 23 deletions
|
@ -6,6 +6,7 @@ package cmd
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
auth_model "forgejo.org/models/auth"
|
auth_model "forgejo.org/models/auth"
|
||||||
"forgejo.org/models/db"
|
"forgejo.org/models/db"
|
||||||
|
@ -61,6 +62,16 @@ var microcmdUserCreate = &cli.Command{
|
||||||
Name: "access-token",
|
Name: "access-token",
|
||||||
Usage: "Generate access token for the user",
|
Usage: "Generate access token for the user",
|
||||||
},
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "access-token-name",
|
||||||
|
Usage: `Name of the generated access token`,
|
||||||
|
Value: "gitea-admin",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "access-token-scopes",
|
||||||
|
Usage: `Scopes of the generated access token, comma separated. Examples: "all", "public-only,read:issue", "write:repository,write:user"`,
|
||||||
|
Value: "all",
|
||||||
|
},
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "restricted",
|
Name: "restricted",
|
||||||
Usage: "Make a restricted user account",
|
Usage: "Make a restricted user account",
|
||||||
|
@ -157,23 +168,40 @@ func runCreateUser(c *cli.Context) error {
|
||||||
IsRestricted: restricted,
|
IsRestricted: restricted,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var accessTokenName string
|
||||||
|
var accessTokenScope auth_model.AccessTokenScope
|
||||||
|
if c.IsSet("access-token") {
|
||||||
|
accessTokenName = strings.TrimSpace(c.String("access-token-name"))
|
||||||
|
if accessTokenName == "" {
|
||||||
|
return errors.New("access-token-name cannot be empty")
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
accessTokenScope, err = auth_model.AccessTokenScope(c.String("access-token-scopes")).Normalize()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid access token scope provided: %w", err)
|
||||||
|
}
|
||||||
|
if !accessTokenScope.HasPermissionScope() {
|
||||||
|
return errors.New("access token does not have any permission")
|
||||||
|
}
|
||||||
|
} else if c.IsSet("access-token-name") || c.IsSet("access-token-scopes") {
|
||||||
|
return errors.New("access-token-name and access-token-scopes flags are only valid when access-token flag is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// arguments should be prepared before creating the user & access token, in case there is anything wrong
|
||||||
|
|
||||||
|
// create the user
|
||||||
if err := user_model.CreateUser(ctx, u, overwriteDefault); err != nil {
|
if err := user_model.CreateUser(ctx, u, overwriteDefault); err != nil {
|
||||||
return fmt.Errorf("CreateUser: %w", err)
|
return fmt.Errorf("CreateUser: %w", err)
|
||||||
}
|
}
|
||||||
|
fmt.Printf("New user '%s' has been successfully created!\n", username)
|
||||||
|
|
||||||
if c.Bool("access-token") {
|
// create the access token
|
||||||
t := &auth_model.AccessToken{
|
if accessTokenScope != "" {
|
||||||
Name: "gitea-admin",
|
t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope}
|
||||||
UID: u.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := auth_model.NewAccessToken(ctx, t); err != nil {
|
if err := auth_model.NewAccessToken(ctx, t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Access token was successfully created... %s\n", t.Token)
|
fmt.Printf("Access token was successfully created... %s\n", t.Token)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("New user '%s' has been successfully created!\n", username)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,8 +34,8 @@ var microcmdUserGenerateAccessToken = &cli.Command{
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "scopes",
|
Name: "scopes",
|
||||||
Value: "",
|
Value: "all",
|
||||||
Usage: "Comma separated list of scopes to apply to access token",
|
Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: runGenerateAccessToken,
|
Action: runGenerateAccessToken,
|
||||||
|
@ -43,7 +43,7 @@ var microcmdUserGenerateAccessToken = &cli.Command{
|
||||||
|
|
||||||
func runGenerateAccessToken(c *cli.Context) error {
|
func runGenerateAccessToken(c *cli.Context) error {
|
||||||
if !c.IsSet("username") {
|
if !c.IsSet("username") {
|
||||||
return errors.New("You must provide a username to generate a token for")
|
return errors.New("you must provide a username to generate a token for")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
|
@ -77,6 +77,9 @@ func runGenerateAccessToken(c *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid access token scope provided: %w", err)
|
return fmt.Errorf("invalid access token scope provided: %w", err)
|
||||||
}
|
}
|
||||||
|
if !accessTokenScope.HasPermissionScope() {
|
||||||
|
return errors.New("access token does not have any permission")
|
||||||
|
}
|
||||||
t.Scope = accessTokenScope
|
t.Scope = accessTokenScope
|
||||||
|
|
||||||
// create the token
|
// create the token
|
||||||
|
|
|
@ -31,6 +31,18 @@ if [ -e /data/ssh/ssh_host_ecdsa_cert ]; then
|
||||||
SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa_cert"}
|
SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa_cert"}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -e /data/ssh/ssh_host_ed25519-cert.pub ]; then
|
||||||
|
SSH_ED25519_CERT=${SSH_ED25519_CERT:-"/data/ssh/ssh_host_ed25519-cert.pub"}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -e /data/ssh/ssh_host_rsa-cert.pub ]; then
|
||||||
|
SSH_RSA_CERT=${SSH_RSA_CERT:-"/data/ssh/ssh_host_rsa-cert.pub"}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -e /data/ssh/ssh_host_ecdsa-cert.pub ]; then
|
||||||
|
SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa-cert.pub"}
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -d /etc/ssh ]; then
|
if [ -d /etc/ssh ]; then
|
||||||
SSH_PORT=${SSH_PORT:-"22"} \
|
SSH_PORT=${SSH_PORT:-"22"} \
|
||||||
SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \
|
SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \
|
||||||
|
|
|
@ -283,6 +283,10 @@ func (s AccessTokenScope) Normalize() (AccessTokenScope, error) {
|
||||||
return bitmap.toScope(), nil
|
return bitmap.toScope(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s AccessTokenScope) HasPermissionScope() bool {
|
||||||
|
return s != "" && s != AccessTokenScopePublicOnly
|
||||||
|
}
|
||||||
|
|
||||||
// PublicOnly checks if this token scope is limited to public resources
|
// PublicOnly checks if this token scope is limited to public resources
|
||||||
func (s AccessTokenScope) PublicOnly() (bool, error) {
|
func (s AccessTokenScope) PublicOnly() (bool, error) {
|
||||||
bitmap, err := s.parse()
|
bitmap, err := s.parse()
|
||||||
|
|
|
@ -62,7 +62,7 @@ func GenerateRandomAvatar(ctx context.Context, u *User) error {
|
||||||
|
|
||||||
// AvatarLinkWithSize returns a link to the user's avatar with size. size <= 0 means default size
|
// AvatarLinkWithSize returns a link to the user's avatar with size. size <= 0 means default size
|
||||||
func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
|
func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
|
||||||
if u.IsGhost() {
|
if u.IsGhost() || u.ID <= 0 {
|
||||||
return avatars.DefaultAvatarLink()
|
return avatars.DefaultAvatarLink()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,9 @@ func ApplicationsPost(ctx *context.Context) {
|
||||||
ctx.ServerError("GetScope", err)
|
ctx.ServerError("GetScope", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !scope.HasPermissionScope() {
|
||||||
|
ctx.Flash.Error(ctx.Tr("settings.at_least_one_permission"), true)
|
||||||
|
}
|
||||||
t := &auth_model.AccessToken{
|
t := &auth_model.AccessToken{
|
||||||
UID: ctx.Doer.ID,
|
UID: ctx.Doer.ID,
|
||||||
Name: form.Name,
|
Name: form.Name,
|
||||||
|
|
|
@ -410,7 +410,6 @@ func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packageVersion := fmt.Sprintf("%s-%s", pd.FileMetadata.Version, pd.FileMetadata.Release)
|
|
||||||
packages = append(packages, &Package{
|
packages = append(packages, &Package{
|
||||||
Type: "rpm",
|
Type: "rpm",
|
||||||
Name: pd.Package.Name,
|
Name: pd.Package.Name,
|
||||||
|
@ -439,7 +438,7 @@ func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []
|
||||||
Archive: pd.FileMetadata.ArchiveSize,
|
Archive: pd.FileMetadata.ArchiveSize,
|
||||||
},
|
},
|
||||||
Location: Location{
|
Location: Location{
|
||||||
Href: fmt.Sprintf("package/%s/%s/%s/%s-%s.%s.rpm", pd.Package.Name, packageVersion, pd.FileMetadata.Architecture, pd.Package.Name, packageVersion, pd.FileMetadata.Architecture),
|
Href: fmt.Sprintf("package/%s/%s/%s/%s-%s.%s.rpm", pd.Package.Name, pd.Version.Version, pd.FileMetadata.Architecture, pd.Package.Name, pd.Version.Version, pd.FileMetadata.Architecture),
|
||||||
},
|
},
|
||||||
Format: Format{
|
Format: Format{
|
||||||
License: pd.VersionMetadata.License,
|
License: pd.VersionMetadata.License,
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
webhook_model "forgejo.org/models/webhook"
|
webhook_model "forgejo.org/models/webhook"
|
||||||
|
"forgejo.org/modules/base"
|
||||||
"forgejo.org/modules/git"
|
"forgejo.org/modules/git"
|
||||||
"forgejo.org/modules/json"
|
"forgejo.org/modules/json"
|
||||||
"forgejo.org/modules/log"
|
"forgejo.org/modules/log"
|
||||||
|
@ -151,6 +152,18 @@ var (
|
||||||
redColor = color("ff3232")
|
redColor = color("ff3232")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// https://discord.com/developers/docs/resources/message#embed-object-embed-limits
|
||||||
|
// Discord has some limits in place for the embeds.
|
||||||
|
// According to some tests, there is no consistent limit for different character sets.
|
||||||
|
// For example: 4096 ASCII letters are allowed, but only 2490 emoji characters are allowed.
|
||||||
|
// To keep it simple, we currently truncate at 2000.
|
||||||
|
const discordDescriptionCharactersLimit = 2000
|
||||||
|
|
||||||
|
type discordConvertor struct {
|
||||||
|
Username string
|
||||||
|
AvatarURL string
|
||||||
|
}
|
||||||
|
|
||||||
// Create implements PayloadConvertor Create method
|
// Create implements PayloadConvertor Create method
|
||||||
func (d discordConvertor) Create(p *api.CreatePayload) (DiscordPayload, error) {
|
func (d discordConvertor) Create(p *api.CreatePayload) (DiscordPayload, error) {
|
||||||
// created tag/branch
|
// created tag/branch
|
||||||
|
@ -312,11 +325,6 @@ func (d discordConvertor) Package(p *api.PackagePayload) (DiscordPayload, error)
|
||||||
return d.createPayload(p.Sender, text, "", p.Package.HTMLURL, color), nil
|
return d.createPayload(p.Sender, text, "", p.Package.HTMLURL, color), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type discordConvertor struct {
|
|
||||||
Username string
|
|
||||||
AvatarURL string
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ shared.PayloadConvertor[DiscordPayload] = discordConvertor{}
|
var _ shared.PayloadConvertor[DiscordPayload] = discordConvertor{}
|
||||||
|
|
||||||
func (discordHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
|
func (discordHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
|
||||||
|
@ -357,7 +365,7 @@ func (d discordConvertor) createPayload(s *api.User, title, text, url string, co
|
||||||
Embeds: []DiscordEmbed{
|
Embeds: []DiscordEmbed{
|
||||||
{
|
{
|
||||||
Title: title,
|
Title: title,
|
||||||
Description: text,
|
Description: base.TruncateString(text, discordDescriptionCharactersLimit),
|
||||||
URL: url,
|
URL: url,
|
||||||
Color: color,
|
Color: color,
|
||||||
Author: DiscordEmbedAuthor{
|
Author: DiscordEmbedAuthor{
|
||||||
|
|
|
@ -175,7 +175,7 @@ func TestDiscordPayload(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Len(t, pl.Embeds, 1)
|
assert.Len(t, pl.Embeds, 1)
|
||||||
assert.Len(t, pl.Embeds[0].Description, 4096)
|
assert.Len(t, pl.Embeds[0].Description, 2000)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("IssueComment", func(t *testing.T) {
|
t.Run("IssueComment", func(t *testing.T) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 1.5 !important;
|
line-height: 1.5 !important;
|
||||||
word-wrap: break-word;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markup > *:first-child {
|
.markup > *:first-child {
|
||||||
|
|
Loading…
Add table
Reference in a new issue