* DELETE /api/devcontainer?repoId=${repoId} 删除 DevContainer
* refactor
* GET /api/devcontainer?repoId=${repoId}&wait=true 阻塞式等待打开就绪的 DevContainer
* POST /api/devcontainer 创建 DevContainer
* refactored the code
* Updated context usage with cancel function
* 预留接口,适配单机版 DevStar DevContainer
* bugFix: context canceled while deleting k8s CRD DevcontainerApp
* 用户界面删除 k8s CRD DevContainer
* 用户界面创建 DevContainer 并更新 NodePort
* 完成用户界面创建 DevContainer
* transplant test code into DevStar Studio
* refactored API router to /routers/api
* 更改 DevContainer Doc
* 更改 DevContainer namespace
* 特殊仓库重定向
* [Doc] 更新 Kubernetes 部署 DevStar Studio 文档说明,特别是 namespace 管理
* [Doc] 更新 CI脚本说明
* Revert "optimized CI workflow"
* optimized CI workflow
* fix typo
* [feature test]: 测试 Pod 内使用 Kubernetes Operator 功能
* [Optimization] error msg for archived repo
* [Optimization]: display detailed err msg on creating devContainer for …
233 lines
5.8 KiB
Go
233 lines
5.8 KiB
Go
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||
// SPDX-License-Identifier: MIT
|
||
|
||
package validation
|
||
|
||
import (
|
||
"fmt"
|
||
"regexp"
|
||
"strings"
|
||
|
||
"code.gitea.io/gitea/modules/auth"
|
||
"code.gitea.io/gitea/modules/git"
|
||
|
||
"gitea.com/go-chi/binding"
|
||
"github.com/gobwas/glob"
|
||
)
|
||
|
||
const (
|
||
// ErrGitRefName is git reference name error
|
||
ErrGitRefName = "GitRefNameError"
|
||
// ErrGlobPattern is returned when glob pattern is invalid
|
||
ErrGlobPattern = "GlobPattern"
|
||
// ErrRegexPattern is returned when a regex pattern is invalid
|
||
ErrRegexPattern = "RegexPattern"
|
||
// ErrUsername is username error
|
||
ErrUsername = "UsernameError"
|
||
// ErrInvalidGroupTeamMap is returned when a group team mapping is invalid
|
||
ErrInvalidGroupTeamMap = "InvalidGroupTeamMap"
|
||
|
||
// ErrUserIdOrRepoId 如果 int64 十进制正数规则校验失败(User ID 或者 Repo ID),返回 ErrUserIdOrRepoId
|
||
ErrUserIdOrRepoId = "UserIdOrRepoIdError"
|
||
)
|
||
|
||
// AddBindingRules adds additional binding rules
|
||
func AddBindingRules() {
|
||
addGitRefNameBindingRule()
|
||
addValidURLBindingRule()
|
||
addValidSiteURLBindingRule()
|
||
addGlobPatternRule()
|
||
addRegexPatternRule()
|
||
addGlobOrRegexPatternRule()
|
||
addUsernamePatternRule()
|
||
addValidGroupTeamMapRule()
|
||
|
||
// 下面是 DevStar Studio 定制化规则:校验是否是有效的 int64 十进制正数字符串(UserID 或者 RepoID)
|
||
addUserIdRepoIdPatternRule()
|
||
}
|
||
|
||
func addGitRefNameBindingRule() {
|
||
// Git refname validation rule
|
||
binding.AddRule(&binding.Rule{
|
||
IsMatch: func(rule string) bool {
|
||
return strings.HasPrefix(rule, "GitRefName")
|
||
},
|
||
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
||
str := fmt.Sprintf("%v", val)
|
||
|
||
if !git.IsValidRefPattern(str) {
|
||
errs.Add([]string{name}, ErrGitRefName, "GitRefName")
|
||
return false, errs
|
||
}
|
||
return true, errs
|
||
},
|
||
})
|
||
}
|
||
|
||
func addValidURLBindingRule() {
|
||
// URL validation rule
|
||
binding.AddRule(&binding.Rule{
|
||
IsMatch: func(rule string) bool {
|
||
return strings.HasPrefix(rule, "ValidUrl")
|
||
},
|
||
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
||
str := fmt.Sprintf("%v", val)
|
||
if len(str) != 0 && !IsValidURL(str) {
|
||
errs.Add([]string{name}, binding.ERR_URL, "Url")
|
||
return false, errs
|
||
}
|
||
|
||
return true, errs
|
||
},
|
||
})
|
||
}
|
||
|
||
func addValidSiteURLBindingRule() {
|
||
// URL validation rule
|
||
binding.AddRule(&binding.Rule{
|
||
IsMatch: func(rule string) bool {
|
||
return strings.HasPrefix(rule, "ValidSiteUrl")
|
||
},
|
||
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
||
str := fmt.Sprintf("%v", val)
|
||
if len(str) != 0 && !IsValidSiteURL(str) {
|
||
errs.Add([]string{name}, binding.ERR_URL, "Url")
|
||
return false, errs
|
||
}
|
||
|
||
return true, errs
|
||
},
|
||
})
|
||
}
|
||
|
||
func addGlobPatternRule() {
|
||
binding.AddRule(&binding.Rule{
|
||
IsMatch: func(rule string) bool {
|
||
return rule == "GlobPattern"
|
||
},
|
||
IsValid: globPatternValidator,
|
||
})
|
||
}
|
||
|
||
func globPatternValidator(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
||
str := fmt.Sprintf("%v", val)
|
||
|
||
if len(str) != 0 {
|
||
if _, err := glob.Compile(str); err != nil {
|
||
errs.Add([]string{name}, ErrGlobPattern, err.Error())
|
||
return false, errs
|
||
}
|
||
}
|
||
|
||
return true, errs
|
||
}
|
||
|
||
func addRegexPatternRule() {
|
||
binding.AddRule(&binding.Rule{
|
||
IsMatch: func(rule string) bool {
|
||
return rule == "RegexPattern"
|
||
},
|
||
IsValid: regexPatternValidator,
|
||
})
|
||
}
|
||
|
||
func regexPatternValidator(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
||
str := fmt.Sprintf("%v", val)
|
||
|
||
if _, err := regexp.Compile(str); err != nil {
|
||
errs.Add([]string{name}, ErrRegexPattern, err.Error())
|
||
return false, errs
|
||
}
|
||
|
||
return true, errs
|
||
}
|
||
|
||
func addGlobOrRegexPatternRule() {
|
||
binding.AddRule(&binding.Rule{
|
||
IsMatch: func(rule string) bool {
|
||
return rule == "GlobOrRegexPattern"
|
||
},
|
||
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
||
str := strings.TrimSpace(fmt.Sprintf("%v", val))
|
||
|
||
if len(str) >= 2 && strings.HasPrefix(str, "/") && strings.HasSuffix(str, "/") {
|
||
return regexPatternValidator(errs, name, str[1:len(str)-1])
|
||
}
|
||
return globPatternValidator(errs, name, val)
|
||
},
|
||
})
|
||
}
|
||
|
||
func addUsernamePatternRule() {
|
||
binding.AddRule(&binding.Rule{
|
||
IsMatch: func(rule string) bool {
|
||
return rule == "Username"
|
||
},
|
||
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
||
str := fmt.Sprintf("%v", val)
|
||
if !IsValidUsername(str) {
|
||
errs.Add([]string{name}, ErrUsername, "invalid username")
|
||
return false, errs
|
||
}
|
||
return true, errs
|
||
},
|
||
})
|
||
}
|
||
|
||
// 新增 用户ID 或者 Repo ID (十进制int64正数部分字符串校验规则)
|
||
func addUserIdRepoIdPatternRule() {
|
||
binding.AddRule(&binding.Rule{
|
||
IsMatch: func(rule string) bool {
|
||
return rule == "PositiveBase10IntegerNumberRule"
|
||
},
|
||
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
||
str := fmt.Sprintf("%v", val)
|
||
if !IsValidUserIdOrRepoId(str) {
|
||
errs.Add([]string{name}, ErrUserIdOrRepoId, "invalid User ID or Repo ID")
|
||
return false, errs
|
||
}
|
||
return true, errs
|
||
},
|
||
})
|
||
}
|
||
|
||
func addValidGroupTeamMapRule() {
|
||
binding.AddRule(&binding.Rule{
|
||
IsMatch: func(rule string) bool {
|
||
return strings.HasPrefix(rule, "ValidGroupTeamMap")
|
||
},
|
||
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
|
||
_, err := auth.UnmarshalGroupTeamMapping(fmt.Sprintf("%v", val))
|
||
if err != nil {
|
||
errs.Add([]string{name}, ErrInvalidGroupTeamMap, err.Error())
|
||
return false, errs
|
||
}
|
||
|
||
return true, errs
|
||
},
|
||
})
|
||
}
|
||
|
||
func portOnly(hostport string) string {
|
||
colon := strings.IndexByte(hostport, ':')
|
||
if colon == -1 {
|
||
return ""
|
||
}
|
||
if i := strings.Index(hostport, "]:"); i != -1 {
|
||
return hostport[i+len("]:"):]
|
||
}
|
||
if strings.Contains(hostport, "]") {
|
||
return ""
|
||
}
|
||
return hostport[colon+len(":"):]
|
||
}
|
||
|
||
func validPort(p string) bool {
|
||
for _, r := range []byte(p) {
|
||
if r < '0' || r > '9' {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|