+37
-9
cmd/admin_user_create.go
+37
-9
cmd/admin_user_create.go
···
6
6
import (
7
7
"errors"
8
8
"fmt"
9
+
"strings"
9
10
10
11
auth_model "forgejo.org/models/auth"
11
12
"forgejo.org/models/db"
···
60
61
&cli.BoolFlag{
61
62
Name: "access-token",
62
63
Usage: "Generate access token for the user",
64
+
},
65
+
&cli.StringFlag{
66
+
Name: "access-token-name",
67
+
Usage: `Name of the generated access token`,
68
+
Value: "gitea-admin",
69
+
},
70
+
&cli.StringFlag{
71
+
Name: "access-token-scopes",
72
+
Usage: `Scopes of the generated access token, comma separated. Examples: "all", "public-only,read:issue", "write:repository,write:user"`,
73
+
Value: "all",
63
74
},
64
75
&cli.BoolFlag{
65
76
Name: "restricted",
···
157
168
IsRestricted: restricted,
158
169
}
159
170
171
+
var accessTokenName string
172
+
var accessTokenScope auth_model.AccessTokenScope
173
+
if c.IsSet("access-token") {
174
+
accessTokenName = strings.TrimSpace(c.String("access-token-name"))
175
+
if accessTokenName == "" {
176
+
return errors.New("access-token-name cannot be empty")
177
+
}
178
+
var err error
179
+
accessTokenScope, err = auth_model.AccessTokenScope(c.String("access-token-scopes")).Normalize()
180
+
if err != nil {
181
+
return fmt.Errorf("invalid access token scope provided: %w", err)
182
+
}
183
+
if !accessTokenScope.HasPermissionScope() {
184
+
return errors.New("access token does not have any permission")
185
+
}
186
+
} else if c.IsSet("access-token-name") || c.IsSet("access-token-scopes") {
187
+
return errors.New("access-token-name and access-token-scopes flags are only valid when access-token flag is set")
188
+
}
189
+
190
+
// arguments should be prepared before creating the user & access token, in case there is anything wrong
191
+
192
+
// create the user
160
193
if err := user_model.CreateUser(ctx, u, overwriteDefault); err != nil {
161
194
return fmt.Errorf("CreateUser: %w", err)
162
195
}
196
+
fmt.Printf("New user '%s' has been successfully created!\n", username)
163
197
164
-
if c.Bool("access-token") {
165
-
t := &auth_model.AccessToken{
166
-
Name: "gitea-admin",
167
-
UID: u.ID,
168
-
}
169
-
198
+
// create the access token
199
+
if accessTokenScope != "" {
200
+
t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope}
170
201
if err := auth_model.NewAccessToken(ctx, t); err != nil {
171
202
return err
172
203
}
173
-
174
204
fmt.Printf("Access token was successfully created... %s\n", t.Token)
175
205
}
176
-
177
-
fmt.Printf("New user '%s' has been successfully created!\n", username)
178
206
return nil
179
207
}
+6
-3
cmd/admin_user_generate_access_token.go
+6
-3
cmd/admin_user_generate_access_token.go
···
34
34
},
35
35
&cli.StringFlag{
36
36
Name: "scopes",
37
-
Value: "",
38
-
Usage: "Comma separated list of scopes to apply to access token",
37
+
Value: "all",
38
+
Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`,
39
39
},
40
40
},
41
41
Action: runGenerateAccessToken,
···
43
43
44
44
func runGenerateAccessToken(c *cli.Context) error {
45
45
if !c.IsSet("username") {
46
-
return errors.New("You must provide a username to generate a token for")
46
+
return errors.New("you must provide a username to generate a token for")
47
47
}
48
48
49
49
ctx, cancel := installSignals()
···
76
76
accessTokenScope, err := auth_model.AccessTokenScope(c.String("scopes")).Normalize()
77
77
if err != nil {
78
78
return fmt.Errorf("invalid access token scope provided: %w", err)
79
+
}
80
+
if !accessTokenScope.HasPermissionScope() {
81
+
return errors.New("access token does not have any permission")
79
82
}
80
83
t.Scope = accessTokenScope
81
84
+12
docker/root/etc/s6/openssh/setup
+12
docker/root/etc/s6/openssh/setup
···
31
31
SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa_cert"}
32
32
fi
33
33
34
+
if [ -e /data/ssh/ssh_host_ed25519-cert.pub ]; then
35
+
SSH_ED25519_CERT=${SSH_ED25519_CERT:-"/data/ssh/ssh_host_ed25519-cert.pub"}
36
+
fi
37
+
38
+
if [ -e /data/ssh/ssh_host_rsa-cert.pub ]; then
39
+
SSH_RSA_CERT=${SSH_RSA_CERT:-"/data/ssh/ssh_host_rsa-cert.pub"}
40
+
fi
41
+
42
+
if [ -e /data/ssh/ssh_host_ecdsa-cert.pub ]; then
43
+
SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa-cert.pub"}
44
+
fi
45
+
34
46
if [ -d /etc/ssh ]; then
35
47
SSH_PORT=${SSH_PORT:-"22"} \
36
48
SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \
+4
models/auth/access_token_scope.go
+4
models/auth/access_token_scope.go
···
283
283
return bitmap.toScope(), nil
284
284
}
285
285
286
+
func (s AccessTokenScope) HasPermissionScope() bool {
287
+
return s != "" && s != AccessTokenScopePublicOnly
288
+
}
289
+
286
290
// PublicOnly checks if this token scope is limited to public resources
287
291
func (s AccessTokenScope) PublicOnly() (bool, error) {
288
292
bitmap, err := s.parse()
+1
-1
models/user/avatar.go
+1
-1
models/user/avatar.go
···
62
62
63
63
// AvatarLinkWithSize returns a link to the user's avatar with size. size <= 0 means default size
64
64
func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
65
-
if u.IsGhost() {
65
+
if u.IsGhost() || u.ID <= 0 {
66
66
return avatars.DefaultAvatarLink()
67
67
}
68
68
+3
routers/web/user/setting/applications.go
+3
routers/web/user/setting/applications.go
+1
-2
services/packages/rpm/repository.go
+1
-2
services/packages/rpm/repository.go
···
410
410
files = append(files, f)
411
411
}
412
412
}
413
-
packageVersion := fmt.Sprintf("%s-%s", pd.FileMetadata.Version, pd.FileMetadata.Release)
414
413
packages = append(packages, &Package{
415
414
Type: "rpm",
416
415
Name: pd.Package.Name,
···
439
438
Archive: pd.FileMetadata.ArchiveSize,
440
439
},
441
440
Location: Location{
442
-
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),
441
+
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),
443
442
},
444
443
Format: Format{
445
444
License: pd.VersionMetadata.License,
+14
-6
services/webhook/discord.go
+14
-6
services/webhook/discord.go
···
16
16
"unicode/utf8"
17
17
18
18
webhook_model "forgejo.org/models/webhook"
19
+
"forgejo.org/modules/base"
19
20
"forgejo.org/modules/git"
20
21
"forgejo.org/modules/json"
21
22
"forgejo.org/modules/log"
···
151
152
redColor = color("ff3232")
152
153
)
153
154
155
+
// https://discord.com/developers/docs/resources/message#embed-object-embed-limits
156
+
// Discord has some limits in place for the embeds.
157
+
// According to some tests, there is no consistent limit for different character sets.
158
+
// For example: 4096 ASCII letters are allowed, but only 2490 emoji characters are allowed.
159
+
// To keep it simple, we currently truncate at 2000.
160
+
const discordDescriptionCharactersLimit = 2000
161
+
162
+
type discordConvertor struct {
163
+
Username string
164
+
AvatarURL string
165
+
}
166
+
154
167
// Create implements PayloadConvertor Create method
155
168
func (d discordConvertor) Create(p *api.CreatePayload) (DiscordPayload, error) {
156
169
// created tag/branch
···
312
325
return d.createPayload(p.Sender, text, "", p.Package.HTMLURL, color), nil
313
326
}
314
327
315
-
type discordConvertor struct {
316
-
Username string
317
-
AvatarURL string
318
-
}
319
-
320
328
var _ shared.PayloadConvertor[DiscordPayload] = discordConvertor{}
321
329
322
330
func (discordHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
···
357
365
Embeds: []DiscordEmbed{
358
366
{
359
367
Title: title,
360
-
Description: text,
368
+
Description: base.TruncateString(text, discordDescriptionCharactersLimit),
361
369
URL: url,
362
370
Color: color,
363
371
Author: DiscordEmbedAuthor{
+1
-1
services/webhook/discord_test.go
+1
-1
services/webhook/discord_test.go