Compare commits

..

2 Commits

Author SHA1 Message Date
bpiraeus 8fa1922477 minor cleanup based on make check 2025-02-15 23:49:45 +01:00
bpiraeus 836137f15d Adding authorisation options for API access
- ldap currently the only supported method
adding authorisation options for local repositories
  - ldap groups per repo
2025-02-15 23:49:45 +01:00
21 changed files with 357 additions and 933 deletions
+1
View File
@@ -68,3 +68,4 @@ List of contributors, in chronological order:
* Blake Kostner (https://github.com/btkostner) * Blake Kostner (https://github.com/btkostner)
* Leigh London (https://github.com/leighlondon) * Leigh London (https://github.com/leighlondon)
* Gordian Schoenherr (https://github.com/schoenherrg) * Gordian Schoenherr (https://github.com/schoenherrg)
* Brett Hawn (https://github.com/bpiraeus)
+118
View File
@@ -0,0 +1,118 @@
package api
import (
"crypto/tls"
"fmt"
"strings"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/go-ldap/ldap/v3"
)
func Authorize(username string, password string) (ok bool) {
config := context.Config()
if config.Auth.Type != "" {
switch strings.ToLower(config.Auth.Type) {
case "ldap":
ok = doLdapAuth(username, password)
default:
return false
}
if !ok {
return false
}
}
return true
}
func doLdapAuth(username string, password string) bool {
config := context.Config()
attributes := []string{"DN", "CN"}
server := config.Auth.Server
dn := config.Auth.LdapDN
filter := fmt.Sprintf(config.Auth.LdapFilter, username)
// connect to ldap server
conn, err := ldap.Dial("tcp", server)
if err != nil {
return false
}
defer conn.Close()
// reconnect via tls
err = conn.StartTLS(&tls.Config{InsecureSkipVerify: config.Auth.SecureTLS})
if err != nil {
return false
}
// format our request and then fire it off
request := ldap.NewSearchRequest(dn, ldap.ScopeWholeSubtree, 0, 0, 0, false, filter, attributes, nil)
search, err := conn.Search(request)
if err != nil {
return false
}
// get our modified dn and then check our user for auth
udn := search.Entries[0].DN
err = conn.Bind(udn, password)
if err != nil {
return false
}
return true
}
func getGroups(c *gin.Context, username string) {
var groups []string
config := context.Config()
dn := config.Auth.LdapDN
session := sessions.Default(c)
// connect to ldap server
server := fmt.Sprintf("%s", config.Auth.Server)
conn, err := ldap.Dial("tcp", server)
if err != nil {
return
}
// reconnect via tls
err = conn.StartTLS(&tls.Config{InsecureSkipVerify: true})
if err != nil {
return
}
filter := fmt.Sprintf("(|(member=uid=%s,ou=people,dc=llnw,dc=com)(member=uid=%s,ou=people,dc=llnw,dc=com))", username, username)
request := ldap.NewSearchRequest(dn, ldap.ScopeWholeSubtree, 0, 0, 0, false, filter, []string{"dn", "cn"}, nil)
search, err := conn.Search(request)
if err != nil {
return
}
if len(search.Entries) < 1 {
return
}
for _, v := range search.Entries {
value := strings.Split(strings.TrimLeft(v.DN, "cn="), ",")[0]
groups = append(groups, fmt.Sprintf("%s,", value))
}
session.Set("Groups", groups)
}
func checkGroup(c *gin.Context, ldgroup string) bool {
session := sessions.Default(c)
groups := session.Get("Groups")
if ldgroup == "" {
return true
}
for _, v := range groups.([]string) {
if strings.Contains(v, ldgroup) {
return true
}
}
return false
}
func CheckGroup(c *gin.Context, ldgroup string) (err error) {
if !checkGroup(c, ldgroup) {
err = fmt.Errorf("Authorisation Failred")
}
return err
}
+6
View File
@@ -267,7 +267,13 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
return return
} }
err = CheckGroup(c, localRepo.LdapGroup)
if err != nil {
c.AbortWithError(403, err)
}
resources = append(resources, string(localRepo.Key())) resources = append(resources, string(localRepo.Key()))
sources = append(sources, localRepo) sources = append(sources, localRepo)
} }
} else { } else {
+35
View File
@@ -95,6 +95,8 @@ type repoCreateParams struct {
DefaultComponent string ` json:"DefaultComponent" example:"main"` DefaultComponent string ` json:"DefaultComponent" example:"main"`
// Snapshot name to create repoitory from (optional) // Snapshot name to create repoitory from (optional)
FromSnapshot string ` json:"FromSnapshot" example:""` FromSnapshot string ` json:"FromSnapshot" example:""`
//
LdapGroup string
} }
// @Summary Create Repository // @Summary Create Repository
@@ -125,6 +127,7 @@ func apiReposCreate(c *gin.Context) {
repo := deb.NewLocalRepo(b.Name, b.Comment) repo := deb.NewLocalRepo(b.Name, b.Comment)
repo.DefaultComponent = b.DefaultComponent repo.DefaultComponent = b.DefaultComponent
repo.DefaultDistribution = b.DefaultDistribution repo.DefaultDistribution = b.DefaultDistribution
repo.LdapGroup = b.LdapGroup
collectionFactory := context.NewCollectionFactory() collectionFactory := context.NewCollectionFactory()
@@ -173,6 +176,8 @@ type reposEditParams struct {
DefaultDistribution *string ` json:"DefaultDistribution" example:""` DefaultDistribution *string ` json:"DefaultDistribution" example:""`
// Change Devault Component for publishing // Change Devault Component for publishing
DefaultComponent *string ` json:"DefaultComponent" example:""` DefaultComponent *string ` json:"DefaultComponent" example:""`
//
LdapGroup *string
} }
// @Summary Update Repository // @Summary Update Repository
@@ -199,6 +204,12 @@ func apiReposEdit(c *gin.Context) {
return return
} }
err = CheckGroup(c, repo.LdapGroup)
if err != nil {
c.AbortWithError(403, err)
return
}
if b.Name != nil { if b.Name != nil {
_, err := collection.ByName(*b.Name) _, err := collection.ByName(*b.Name)
if err == nil { if err == nil {
@@ -217,6 +228,9 @@ func apiReposEdit(c *gin.Context) {
if b.DefaultComponent != nil { if b.DefaultComponent != nil {
repo.DefaultComponent = *b.DefaultComponent repo.DefaultComponent = *b.DefaultComponent
} }
if b.LdapGroup != nil {
repo.LdapGroup = *b.LdapGroup
}
err = collection.Update(repo) err = collection.Update(repo)
if err != nil { if err != nil {
@@ -276,6 +290,12 @@ func apiReposDrop(c *gin.Context) {
return return
} }
err = CheckGroup(c, repo.LdapGroup)
if err != nil {
c.AbortWithError(403, err)
return
}
resources := []string{string(repo.Key())} resources := []string{string(repo.Key())}
taskName := fmt.Sprintf("Delete repo %s", name) taskName := fmt.Sprintf("Delete repo %s", name)
maybeRunTaskInBackground(c, taskName, resources, func(_ aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) { maybeRunTaskInBackground(c, taskName, resources, func(_ aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
@@ -365,6 +385,11 @@ func apiReposPackagesAddDelete(c *gin.Context, taskNamePrefix string, cb func(li
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, err return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, err
} }
err = CheckGroup(c, repo.LdapGroup)
if err != nil {
return &task.ProcessReturnValue{Code: 403, Value: nil}, err
}
out.Printf("Loading packages...\n") out.Printf("Loading packages...\n")
list, err := deb.NewPackageListFromRefList(repo.RefList(), collectionFactory.PackageCollection(), nil) list, err := deb.NewPackageListFromRefList(repo.RefList(), collectionFactory.PackageCollection(), nil)
if err != nil { if err != nil {
@@ -522,6 +547,11 @@ func apiReposPackageFromDir(c *gin.Context) {
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, err return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, err
} }
err = CheckGroup(c, repo.LdapGroup)
if err != nil {
return &task.ProcessReturnValue{Code: 403, Value: nil}, err
}
verifier := context.GetVerifier() verifier := context.GetVerifier()
var ( var (
@@ -845,6 +875,11 @@ func apiReposIncludePackageFromDir(c *gin.Context) {
AbortWithJSONError(c, 404, err) AbortWithJSONError(c, 404, err)
return return
} }
err = CheckGroup(c, repo.LdapGroup)
if err != nil {
c.AbortWithError(403, err)
return
}
resources = append(resources, string(repo.Key())) resources = append(resources, string(repo.Key()))
} }
+132 -63
View File
@@ -1,9 +1,12 @@
package api package api
import ( import (
"fmt"
"log"
"net/http" "net/http"
"os" "os"
"sync/atomic" "sync/atomic"
"time"
"github.com/aptly-dev/aptly/aptly" "github.com/aptly-dev/aptly/aptly"
ctx "github.com/aptly-dev/aptly/context" ctx "github.com/aptly-dev/aptly/context"
@@ -15,6 +18,10 @@ import (
"github.com/aptly-dev/aptly/docs" "github.com/aptly-dev/aptly/docs"
swaggerFiles "github.com/swaggo/files" swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger" ginSwagger "github.com/swaggo/gin-swagger"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
uuid "github.com/nu7hatch/gouuid"
) )
var context *ctx.AptlyContext var context *ctx.AptlyContext
@@ -133,105 +140,167 @@ func Router(c *ctx.AptlyContext) http.Handler {
api.GET("/healthy", apiHealthy) api.GET("/healthy", apiHealthy)
} }
// set up cookies and sessions
token, err := uuid.NewV4()
if err != nil {
panic(err)
}
store := cookie.NewStore([]byte(token.String()))
router.Use(sessions.Sessions(token.String(), store))
// prep our config fetcher ahead of need
config := context.Config()
// prep a logfile if we've set one
if config.LogFile != "" {
file, err := os.OpenFile(config.LogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
defer file.Close()
log.SetOutput(file)
}
router.GET("/version", apiVersion)
var username string
var password string
router.POST("/login", func(c *gin.Context) {
session := sessions.Default(c)
session.Options(sessions.Options{MaxAge: 30})
if config.UseAuth {
log.Printf("UseAuth is enabled\n")
username = c.PostForm("username")
password = c.PostForm("password")
if !Authorize(username, password) {
c.AbortWithError(403, fmt.Errorf("Authorization Failure"))
}
log.Printf("%s authorized from %s\n", username, c.ClientIP())
}
session.Set(token.String(), time.Now().Unix())
session.Save()
getGroups(c, username)
c.String(200, "Authorized!")
})
router.POST("/logout", func(c *gin.Context) {
session := sessions.Default(c)
session.Options(sessions.Options{MaxAge: -1})
session.Save()
c.String(200, "Deauthorized")
})
authorize := router.Group("/api", func(c *gin.Context) {
session := sessions.Default(c)
if config.UseAuth {
if session.Get(token.String()) == nil {
c.AbortWithError(403, fmt.Errorf("not authorized"))
}
session.Options(sessions.Options{MaxAge: 30})
session.Set(token.String(), time.Now().Unix())
session.Save()
}
})
{ {
api.GET("/repos", apiReposList) authorize.GET("/repos", apiReposList)
api.POST("/repos", apiReposCreate) authorize.POST("/repos", apiReposCreate)
api.GET("/repos/:name", apiReposShow) authorize.GET("/repos/:name", apiReposShow)
api.PUT("/repos/:name", apiReposEdit) authorize.PUT("/repos/:name", apiReposEdit)
api.DELETE("/repos/:name", apiReposDrop) authorize.DELETE("/repos/:name", apiReposDrop)
api.GET("/repos/:name/packages", apiReposPackagesShow) authorize.GET("/repos/:name/packages", apiReposPackagesShow)
api.POST("/repos/:name/packages", apiReposPackagesAdd) authorize.POST("/repos/:name/packages", apiReposPackagesAdd)
api.DELETE("/repos/:name/packages", apiReposPackagesDelete) authorize.DELETE("/repos/:name/packages", apiReposPackagesDelete)
api.POST("/repos/:name/file/:dir/:file", apiReposPackageFromFile) authorize.POST("/repos/:name/file/:dir/:file", apiReposPackageFromFile)
api.POST("/repos/:name/file/:dir", apiReposPackageFromDir) authorize.POST("/repos/:name/file/:dir", apiReposPackageFromDir)
api.POST("/repos/:name/copy/:src/:file", apiReposCopyPackage) authorize.POST("/repos/:name/copy/:src/:file", apiReposCopyPackage)
api.POST("/repos/:name/include/:dir/:file", apiReposIncludePackageFromFile) authorize.POST("/repos/:name/include/:dir/:file", apiReposIncludePackageFromFile)
api.POST("/repos/:name/include/:dir", apiReposIncludePackageFromDir) authorize.POST("/repos/:name/include/:dir", apiReposIncludePackageFromDir)
api.POST("/repos/:name/snapshots", apiSnapshotsCreateFromRepository) authorize.POST("/repos/:name/snapshots", apiSnapshotsCreateFromRepository)
} }
{ {
api.POST("/mirrors/:name/snapshots", apiSnapshotsCreateFromMirror) authorize.POST("/mirrors/:name/snapshots", apiSnapshotsCreateFromMirror)
} }
{ {
api.GET("/mirrors", apiMirrorsList) authorize.GET("/mirrors", apiMirrorsList)
api.GET("/mirrors/:name", apiMirrorsShow) authorize.GET("/mirrors/:name", apiMirrorsShow)
api.GET("/mirrors/:name/packages", apiMirrorsPackages) authorize.GET("/mirrors/:name/packages", apiMirrorsPackages)
api.POST("/mirrors", apiMirrorsCreate) authorize.POST("/mirrors", apiMirrorsCreate)
api.PUT("/mirrors/:name", apiMirrorsUpdate) authorize.PUT("/mirrors/:name", apiMirrorsUpdate)
api.DELETE("/mirrors/:name", apiMirrorsDrop) authorize.DELETE("/mirrors/:name", apiMirrorsDrop)
} }
{ {
api.POST("/gpg/key", apiGPGAddKey) authorize.POST("/gpg/key", apiGPGAddKey)
} }
{ {
api.GET("/s3", apiS3List) authorize.GET("/s3", apiS3List)
} }
{ {
api.GET("/files", apiFilesListDirs) authorize.GET("/files", apiFilesListDirs)
api.POST("/files/:dir", apiFilesUpload) authorize.POST("/files/:dir", apiFilesUpload)
api.GET("/files/:dir", apiFilesListFiles) authorize.GET("/files/:dir", apiFilesListFiles)
api.DELETE("/files/:dir", apiFilesDeleteDir) authorize.DELETE("/files/:dir", apiFilesDeleteDir)
api.DELETE("/files/:dir/:name", apiFilesDeleteFile) authorize.DELETE("/files/:dir/:name", apiFilesDeleteFile)
} }
{ {
api.GET("/publish", apiPublishList) authorize.GET("/publish", apiPublishList)
api.GET("/publish/:prefix/:distribution", apiPublishShow) authorize.GET("/publish/:prefix/:distribution", apiPublishShow)
api.POST("/publish", apiPublishRepoOrSnapshot) authorize.POST("/publish", apiPublishRepoOrSnapshot)
api.POST("/publish/:prefix", apiPublishRepoOrSnapshot) authorize.POST("/publish/:prefix", apiPublishRepoOrSnapshot)
api.PUT("/publish/:prefix/:distribution", apiPublishUpdateSwitch) authorize.PUT("/publish/:prefix/:distribution", apiPublishUpdateSwitch)
api.DELETE("/publish/:prefix/:distribution", apiPublishDrop) authorize.DELETE("/publish/:prefix/:distribution", apiPublishDrop)
api.POST("/publish/:prefix/:distribution/sources", apiPublishAddSource) authorize.POST("/publish/:prefix/:distribution/sources", apiPublishAddSource)
api.GET("/publish/:prefix/:distribution/sources", apiPublishListChanges) authorize.GET("/publish/:prefix/:distribution/sources", apiPublishListChanges)
api.PUT("/publish/:prefix/:distribution/sources", apiPublishSetSources) authorize.PUT("/publish/:prefix/:distribution/sources", apiPublishSetSources)
api.DELETE("/publish/:prefix/:distribution/sources", apiPublishDropChanges) authorize.DELETE("/publish/:prefix/:distribution/sources", apiPublishDropChanges)
api.PUT("/publish/:prefix/:distribution/sources/:component", apiPublishUpdateSource) authorize.PUT("/publish/:prefix/:distribution/sources/:component", apiPublishUpdateSource)
api.DELETE("/publish/:prefix/:distribution/sources/:component", apiPublishRemoveSource) authorize.DELETE("/publish/:prefix/:distribution/sources/:component", apiPublishRemoveSource)
api.POST("/publish/:prefix/:distribution/update", apiPublishUpdate) authorize.POST("/publish/:prefix/:distribution/update", apiPublishUpdate)
} }
{ {
api.GET("/snapshots", apiSnapshotsList) authorize.GET("/snapshots", apiSnapshotsList)
api.POST("/snapshots", apiSnapshotsCreate) authorize.POST("/snapshots", apiSnapshotsCreate)
api.PUT("/snapshots/:name", apiSnapshotsUpdate) authorize.PUT("/snapshots/:name", apiSnapshotsUpdate)
api.GET("/snapshots/:name", apiSnapshotsShow) authorize.GET("/snapshots/:name", apiSnapshotsShow)
api.GET("/snapshots/:name/packages", apiSnapshotsSearchPackages) authorize.GET("/snapshots/:name/packages", apiSnapshotsSearchPackages)
api.DELETE("/snapshots/:name", apiSnapshotsDrop) authorize.DELETE("/snapshots/:name", apiSnapshotsDrop)
api.GET("/snapshots/:name/diff/:withSnapshot", apiSnapshotsDiff) authorize.GET("/snapshots/:name/diff/:withSnapshot", apiSnapshotsDiff)
api.POST("/snapshots/:name/merge", apiSnapshotsMerge) authorize.POST("/snapshots/:name/merge", apiSnapshotsMerge)
api.POST("/snapshots/:name/pull", apiSnapshotsPull) authorize.POST("/snapshots/:name/pull", apiSnapshotsPull)
} }
{ {
api.GET("/packages/:key", apiPackagesShow) authorize.GET("/packages/:key", apiPackagesShow)
api.GET("/packages", apiPackages) authorize.GET("/packages", apiPackages)
} }
{ {
api.GET("/graph.:ext", apiGraph) authorize.GET("/graph.:ext", apiGraph)
} }
{ {
api.POST("/db/cleanup", apiDbCleanup) authorize.POST("/db/cleanup", apiDbCleanup)
} }
{ {
api.GET("/tasks", apiTasksList) authorize.GET("/tasks", apiTasksList)
api.POST("/tasks-clear", apiTasksClear) authorize.POST("/tasks-clear", apiTasksClear)
api.GET("/tasks-wait", apiTasksWait) authorize.GET("/tasks-wait", apiTasksWait)
api.GET("/tasks/:id/wait", apiTasksWaitForTaskByID) authorize.GET("/tasks/:id/wait", apiTasksWaitForTaskByID)
api.GET("/tasks/:id/output", apiTasksOutputShow) authorize.GET("/tasks/:id/output", apiTasksOutputShow)
api.GET("/tasks/:id/detail", apiTasksDetailShow) authorize.GET("/tasks/:id/detail", apiTasksDetailShow)
api.GET("/tasks/:id/return_value", apiTasksReturnValueShow) authorize.GET("/tasks/:id/return_value", apiTasksReturnValueShow)
api.GET("/tasks/:id", apiTasksShow) authorize.GET("/tasks/:id", apiTasksShow)
api.DELETE("/tasks/:id", apiTasksDelete) authorize.DELETE("/tasks/:id", apiTasksDelete)
} }
return router return router
+6
View File
@@ -251,6 +251,12 @@ func apiSnapshotsCreateFromRepository(c *gin.Context) {
return return
} }
err = CheckGroup(c, repo.LdapGroup)
if err != nil {
c.AbortWithError(403, err)
return
}
// including snapshot resource key // including snapshot resource key
resources := []string{string(repo.Key()), "S" + b.Name} resources := []string{string(repo.Key()), "S" + b.Name}
taskName := fmt.Sprintf("Create snapshot of repo %s", name) taskName := fmt.Sprintf("Create snapshot of repo %s", name)
+2
View File
@@ -18,6 +18,7 @@ func aptlyRepoCreate(cmd *commander.Command, args []string) error {
repo := deb.NewLocalRepo(args[0], context.Flags().Lookup("comment").Value.String()) repo := deb.NewLocalRepo(args[0], context.Flags().Lookup("comment").Value.String())
repo.DefaultDistribution = context.Flags().Lookup("distribution").Value.String() repo.DefaultDistribution = context.Flags().Lookup("distribution").Value.String()
repo.DefaultComponent = context.Flags().Lookup("component").Value.String() repo.DefaultComponent = context.Flags().Lookup("component").Value.String()
repo.LdapGroup = context.Flags().Lookup("ldap-group").Value.String()
uploadersFile := context.Flags().Lookup("uploaders-file").Value.Get().(string) uploadersFile := context.Flags().Lookup("uploaders-file").Value.Get().(string)
if uploadersFile != "" { if uploadersFile != "" {
@@ -79,6 +80,7 @@ Example:
cmd.Flag.String("distribution", "", "default distribution when publishing") cmd.Flag.String("distribution", "", "default distribution when publishing")
cmd.Flag.String("component", "main", "default component when publishing") cmd.Flag.String("component", "main", "default component when publishing")
cmd.Flag.String("uploaders-file", "", "uploaders.json to be used when including .changes into this repository") cmd.Flag.String("uploaders-file", "", "uploaders.json to be used when including .changes into this repository")
cmd.Flag.String("ldap-group", "", "ldap group that owns the repo, leave empty to allow ALL")
return cmd return cmd
} }
+3
View File
@@ -39,6 +39,8 @@ func aptlyRepoEdit(cmd *commander.Command, args []string) error {
repo.DefaultComponent = flag.Value.String() repo.DefaultComponent = flag.Value.String()
case "uploaders-file": case "uploaders-file":
uploadersFile = pointer.ToString(flag.Value.String()) uploadersFile = pointer.ToString(flag.Value.String())
case "ldap-group":
repo.LdapGroup = flag.Value.String()
} }
}) })
@@ -82,6 +84,7 @@ Example:
cmd.Flag.String("distribution", "", "default distribution when publishing") cmd.Flag.String("distribution", "", "default distribution when publishing")
cmd.Flag.String("component", "", "default component when publishing") cmd.Flag.String("component", "", "default component when publishing")
cmd.Flag.String("uploaders-file", "", "uploaders.json to be used when including .changes into this repository") cmd.Flag.String("uploaders-file", "", "uploaders.json to be used when including .changes into this repository")
cmd.Flag.String("ldap-group", "", "ldap group that owns the repo, leave empty to allow ALL")
return cmd return cmd
} }
+1
View File
@@ -45,6 +45,7 @@ func aptlyRepoShowTxt(_ *commander.Command, args []string) error {
fmt.Printf("Comment: %s\n", repo.Comment) fmt.Printf("Comment: %s\n", repo.Comment)
fmt.Printf("Default Distribution: %s\n", repo.DefaultDistribution) fmt.Printf("Default Distribution: %s\n", repo.DefaultDistribution)
fmt.Printf("Default Component: %s\n", repo.DefaultComponent) fmt.Printf("Default Component: %s\n", repo.DefaultComponent)
fmt.Printf("Ldap Group: %s\n", repo.LdapGroup)
if repo.Uploaders != nil { if repo.Uploaders != nil {
fmt.Printf("Uploaders: %s\n", repo.Uploaders) fmt.Printf("Uploaders: %s\n", repo.Uploaders)
} }
+1
View File
@@ -242,6 +242,7 @@ local keyring="*-keyring=[gpg keyring to use when verifying Release file (could
local create_edit=("-comment=[any text that would be used to described local repository]:comment: " local create_edit=("-comment=[any text that would be used to described local repository]:comment: "
"-component=[default component when publishing]:component:($components)" "-component=[default component when publishing]:component:($components)"
"-distribution=[default distribution when publishing]:distribution:($dists)" "-distribution=[default distribution when publishing]:distribution:($dists)"
"-ldap-group=[ldap group for repo actions, empty by default]:ldap-group"
$aptly_uploaders $aptly_uploaders
) )
-19
View File
@@ -5,13 +5,11 @@ import (
gocontext "context" gocontext "context"
"fmt" "fmt"
"math/rand" "math/rand"
"net/url"
"os" "os"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
"strconv"
"strings" "strings"
"sync" "sync"
"syscall" "syscall"
@@ -23,7 +21,6 @@ import (
"github.com/aptly-dev/aptly/database" "github.com/aptly-dev/aptly/database"
"github.com/aptly-dev/aptly/database/etcddb" "github.com/aptly-dev/aptly/database/etcddb"
"github.com/aptly-dev/aptly/database/goleveldb" "github.com/aptly-dev/aptly/database/goleveldb"
"github.com/aptly-dev/aptly/database/ssdb"
"github.com/aptly-dev/aptly/deb" "github.com/aptly-dev/aptly/deb"
"github.com/aptly-dev/aptly/files" "github.com/aptly-dev/aptly/files"
"github.com/aptly-dev/aptly/http" "github.com/aptly-dev/aptly/http"
@@ -32,7 +29,6 @@ import (
"github.com/aptly-dev/aptly/swift" "github.com/aptly-dev/aptly/swift"
"github.com/aptly-dev/aptly/task" "github.com/aptly-dev/aptly/task"
"github.com/aptly-dev/aptly/utils" "github.com/aptly-dev/aptly/utils"
"github.com/seefan/gossdb/v2/conf"
"github.com/smira/commander" "github.com/smira/commander"
"github.com/smira/flag" "github.com/smira/flag"
) )
@@ -305,21 +301,6 @@ func (context *AptlyContext) _database() (database.Storage, error) {
context.database, err = goleveldb.NewDB(dbPath) context.database, err = goleveldb.NewDB(dbPath)
case "etcd": case "etcd":
context.database, err = etcddb.NewDB(context.config().DatabaseBackend.URL) context.database, err = etcddb.NewDB(context.config().DatabaseBackend.URL)
case "ssdb":
var cfg conf.Config
u, e := url.Parse(context.config().DatabaseBackend.URL)
if e != nil {
return nil, e
}
cfg.Port, e = strconv.Atoi(u.Port())
cfg.Host = strings.Split(u.Host, ":")[0]
if e != nil {
return nil, e
}
password, _ := u.User.Password()
cfg.Password = password
context.database, err = ssdb.NewOpenDB(&cfg)
default: default:
context.database, err = goleveldb.NewDB(context.dbPath()) context.database, err = goleveldb.NewDB(context.dbPath())
} }
-129
View File
@@ -1,129 +0,0 @@
package ssdb
import (
"fmt"
"github.com/aptly-dev/aptly/database"
"github.com/seefan/gossdb/v2/conf"
"github.com/seefan/gossdb/v2/pool"
)
const (
delOpt = "del"
)
type bWriteData struct {
key []byte
value []byte
opts string
err error
}
type Batch struct {
cfg *conf.Config
// key-value chan
w chan bWriteData
p map[string]interface{}
d []string
db *pool.Client
}
// func internalOpenBatch...
func internalOpenBatch(_ database.Storage) *Batch {
b := &Batch{
w: make(chan bWriteData),
p: make(map[string]interface{}),
}
b.run()
return b
}
func (b *Batch) run() {
go func() {
for {
select {
case w, ok := <-b.w:
{
if !ok {
ssdbLog("ssdb batch write chan closed")
return
}
if w.opts == "write" {
ssdbLog("ssdb batch write")
var err error
if len(b.p) > 0 && len(b.d) == 0 {
err = b.db.MultiSet(b.p)
ssdbLog("ssdb batch set errinfo: ", err)
} else if len(b.d) > 0 && len(b.p) == 0 {
err = b.db.MultiDel(b.d...)
ssdbLog("ssdb batch del errinfo: ", err)
} else if len(b.p) == 0 && len(b.d) == 0 {
err = nil
} else {
err = fmt.Errorf("ssdb batch does not support both put and delete operations")
}
ssdbLog("ssdb batch write errinfo: ", err)
b.w <- bWriteData{
err: err,
}
ssdbLog("ssdb batch write end")
} else {
ssdbLog("ssdb batch", w.opts)
if w.opts == "put" {
b.p[string(w.key)] = w.value
} else if w.opts == delOpt {
b.d = append(b.d, string(w.key))
}
}
}
}
}
}()
}
func (b *Batch) stop() {
ssdbLog("ssdb batch stop")
close(b.w)
}
func (b *Batch) Put(key, value []byte) (err error) {
// err = b.db.Set(string(key), string(value))
w := bWriteData{
key: key,
value: value,
opts: "put",
}
b.w <- w
return nil
}
func (b *Batch) Delete(key []byte) (err error) {
/* err = b.db.Del(string(key))
return */
w := bWriteData{
key: key,
opts: delOpt,
}
b.w <- w
return nil
}
func (b *Batch) Write() (err error) {
defer b.stop()
w := bWriteData{
opts: "write",
}
b.w <- w
result := <-b.w
return result.err
}
// batch should implement database.Batch
var (
_ database.Batch = &Batch{}
)
-62
View File
@@ -1,62 +0,0 @@
package ssdb
import (
"os"
"strconv"
"github.com/aptly-dev/aptly/database"
"github.com/seefan/gossdb/v2"
"github.com/seefan/gossdb/v2/conf"
"github.com/seefan/gossdb/v2/pool"
)
var defaultBufSize = 102400
var defaultPoolSize = 1
func internalOpen(cfg *conf.Config) (*pool.Client, error) {
ssdbLog("internalOpen")
cfg.ReadBufferSize = defaultBufSize
cfg.WriteBufferSize = defaultBufSize
cfg.MaxPoolSize = defaultPoolSize
cfg.PoolSize = defaultPoolSize
cfg.MinPoolSize = defaultPoolSize
cfg.MaxWaitSize = 100 * defaultPoolSize
cfg.RetryEnabled = true
//override by env
if os.Getenv("SSDB_READBUFFERSIZE") != "" {
readBufSize, err := strconv.Atoi(os.Getenv("SSDB_READBUFFERSIZE"))
if err != nil {
cfg.ReadBufferSize = readBufSize
}
}
if os.Getenv("SSDB_WRITEBUFFERSIZE") != "" {
writeBufSize, err := strconv.Atoi(os.Getenv("SSDB_WRITEBUFFERSIZE"))
if err != nil {
cfg.WriteBufferSize = writeBufSize
}
}
var cfgs = []*conf.Config{cfg}
err := gossdb.Start(cfgs...)
if err != nil {
return nil, err
}
return gossdb.NewClient()
}
func NewDB(cfg *conf.Config) (database.Storage, error) {
return &Storage{cfg: cfg}, nil
}
func NewOpenDB(cfg *conf.Config) (database.Storage, error) {
db, err := NewDB(cfg)
if err != nil {
return nil, err
}
return db, db.Open()
}
-274
View File
@@ -1,274 +0,0 @@
package ssdb_test
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"testing"
"github.com/aptly-dev/aptly/database"
"github.com/aptly-dev/aptly/database/ssdb"
"github.com/seefan/gossdb/v2/conf"
. "gopkg.in/check.v1"
)
// Launch gocheck tests
func Test(t *testing.T) {
TestingT(t)
}
func setUpSsdb() error {
setUpStr := `
#!/bin/bash
if [ ! -e /tmp/ssdb-master/ssdb-master ]; then
mkdir -p /tmp/ssdb-master
wget --no-check-certificate https://github.com/ideawu/ssdb/archive/master.zip -O /tmp/ssdb-master/master.zip
cd /tmp/ssdb-master && unzip master && cd ssdb-master && make all
fi
cd /tmp/ssdb-master/ssdb-master && ./ssdb-server -d ssdb.conf -s restart
sleep 2`
tmpShell, err := ioutil.TempFile("/tmp", "ssdbSetup")
if err != nil {
return err
}
defer os.Remove(tmpShell.Name())
_, err = tmpShell.WriteString(setUpStr)
if err != nil {
return err
}
cmd := exec.Command("/bin/bash", tmpShell.Name())
fmt.Println(cmd.String())
output, err := cmd.Output()
fmt.Println(string(output))
if err != nil {
return err
}
return nil
}
func TestMain(m *testing.M) {
setUpSsdb()
m.Run()
}
type SSDBSuite struct {
cfg *conf.Config
db database.Storage
}
var _ = Suite(&SSDBSuite{cfg: &conf.Config{
Host: "127.0.0.1",
Port: 8888,
}})
func (s *SSDBSuite) SetUpTest(c *C) {
var err error
s.db, err = ssdb.NewOpenDB(s.cfg)
c.Assert(err, IsNil)
}
func (s *SSDBSuite) TestSetUpTest(c *C) {
var err error
s.db, err = ssdb.NewOpenDB(s.cfg)
c.Assert(err, IsNil)
}
func (s *SSDBSuite) TestGetPut(c *C) {
var (
key = []byte("key")
value = []byte("value")
)
var err error
err = s.db.Put(key, value)
c.Assert(err, IsNil)
result, err := s.db.Get(key)
c.Assert(err, IsNil)
c.Assert(result, DeepEquals, value)
}
func (s *SSDBSuite) TestTemporaryDelete(c *C) {
fmt.Println("TestTemporaryDelete")
var (
key = []byte("key")
value = []byte("value")
)
temp, err := s.db.CreateTemporary()
c.Assert(err, IsNil)
c.Check(temp.HasPrefix([]byte(nil)), Equals, false)
err = temp.Put(key, value)
c.Assert(err, IsNil)
c.Check(temp.HasPrefix([]byte(nil)), Equals, true)
c.Assert(temp.Close(), IsNil)
c.Assert(temp.Drop(), IsNil)
}
func (s *SSDBSuite) TestDelete(c *C) {
var (
key = []byte("key")
value = []byte("value")
)
err := s.db.Put(key, value)
c.Assert(err, IsNil)
_, err = s.db.Get(key)
c.Assert(err, IsNil)
err = s.db.Delete(key)
c.Assert(err, IsNil)
}
func (s *SSDBSuite) TestByPrefix(c *C) {
//c.Check(s.db.FetchByPrefix([]byte{0x80}), DeepEquals, [][]byte{})
s.db.Put([]byte{0x80, 0x01}, []byte{0x01})
s.db.Put([]byte{0x80, 0x03}, []byte{0x03})
s.db.Put([]byte{0x80, 0x02}, []byte{0x02})
c.Check(len(s.db.FetchByPrefix([]byte{0x80})), DeepEquals, len([][]byte{{0x01}, {0x02}, {0x03}}))
c.Check(len(s.db.KeysByPrefix([]byte{0x80})), DeepEquals, len([][]byte{{0x80, 0x01}, {0x80, 0x02}, {0x80, 0x03}}))
s.db.Put([]byte{0x90, 0x01}, []byte{0x04})
c.Check(len(s.db.FetchByPrefix([]byte{0x80})), DeepEquals, len([][]byte{{0x01}, {0x02}, {0x03}}))
c.Check(len(s.db.KeysByPrefix([]byte{0x80})), DeepEquals, len([][]byte{{0x80, 0x01}, {0x80, 0x02}, {0x80, 0x03}}))
s.db.Put([]byte{0x00, 0x01}, []byte{0x05})
c.Check(len(s.db.FetchByPrefix([]byte{0x80})), DeepEquals, len([][]byte{{0x01}, {0x02}, {0x03}}))
c.Check(len(s.db.KeysByPrefix([]byte{0x80})), DeepEquals, len([][]byte{{0x80, 0x01}, {0x80, 0x02}, {0x80, 0x03}}))
keys := [][]byte{}
values := [][]byte{}
c.Check(s.db.ProcessByPrefix([]byte{0x80}, func(k, v []byte) error {
keys = append(keys, append([]byte(nil), k...))
values = append(values, append([]byte(nil), v...))
return nil
}), IsNil)
c.Check(len(values), DeepEquals, len([][]byte{{0x01}, {0x02}, {0x03}}))
c.Check(len(keys), DeepEquals, len([][]byte{{0x80, 0x01}, {0x80, 0x02}, {0x80, 0x03}}))
c.Check(s.db.ProcessByPrefix([]byte{0x80}, func(k, v []byte) error {
return database.ErrNotFound
}), Equals, database.ErrNotFound)
c.Check(s.db.ProcessByPrefix([]byte{0xa0}, func(k, v []byte) error {
return database.ErrNotFound
}), IsNil)
c.Check(s.db.FetchByPrefix([]byte{0xa0}), DeepEquals, [][]byte{})
c.Check(s.db.KeysByPrefix([]byte{0xa0}), DeepEquals, [][]byte{})
}
func (s *SSDBSuite) TestHasPrefix(c *C) {
s.db.Put([]byte{0x80, 0x01}, []byte{0x01})
//c.Check(s.db.HasPrefix([]byte("")), Equals, true)
c.Check(s.db.HasPrefix([]byte{0x80}), Equals, true)
c.Check(s.db.HasPrefix([]byte{0x79}), Equals, false)
}
func (s *SSDBSuite) TestTransactionCommit(c *C) {
var (
key = []byte("key")
key2 = []byte("key2")
value = []byte("value")
value2 = []byte("value2")
)
s.db.Delete(key)
s.db.Delete(key2)
transaction, err := s.db.OpenTransaction()
c.Assert(err, IsNil)
defer transaction.Discard()
err = s.db.Put(key, value)
c.Assert(err, IsNil)
v, err := s.db.Get(key)
c.Assert(err, IsNil)
c.Check(v, DeepEquals, value)
err = transaction.Put(key2, value2)
c.Assert(err, IsNil)
v, err = transaction.Get(key2)
c.Check(err, IsNil)
c.Check(v, DeepEquals, value2)
_, err = s.db.Get(key2)
c.Assert(err, ErrorMatches, "key not found")
err = transaction.Delete(key)
c.Assert(err, IsNil)
_, err = transaction.Get(key)
c.Assert(err, ErrorMatches, "key not found")
v, err = s.db.Get(key)
c.Assert(err, IsNil)
c.Check(v, DeepEquals, value)
err = transaction.Commit()
c.Check(err, IsNil)
v, err = s.db.Get(key2)
c.Check(err, IsNil)
c.Check(v, DeepEquals, value2)
_, err = s.db.Get(key)
c.Assert(err, ErrorMatches, "key not found")
}
func (s *SSDBSuite) TestBatch(c *C) {
var (
key = []byte("bkey")
key2 = []byte("bkey2")
value = []byte("bvalue")
value2 = []byte("bvalue2")
)
err := s.db.Put(key, value)
c.Check(err, IsNil)
batch := s.db.CreateBatch()
batch.Put(key2, value2)
v, err := s.db.Get(key)
c.Check(err, IsNil)
c.Check(v, DeepEquals, value)
_, err = s.db.Get(key2)
c.Check(err, ErrorMatches, "key not found")
err = batch.Write()
c.Check(err, IsNil)
v, err = s.db.Get(key2)
c.Check(err, IsNil)
c.Check(v, DeepEquals, value2)
batch = s.db.CreateBatch()
batch.Delete(key)
batch.Delete(key2)
c.Check(err, IsNil)
v, err = s.db.Get(key)
c.Check(err, IsNil)
c.Check(v, DeepEquals, value)
c.Check(err, IsNil)
v, err = s.db.Get(key2)
c.Check(err, IsNil)
c.Check(v, DeepEquals, value2)
err = batch.Write()
c.Check(err, IsNil)
_, err = s.db.Get(key2)
c.Check(err, ErrorMatches, "key not found")
_, err = s.db.Get(key)
c.Check(err, ErrorMatches, "key not found")
}
-12
View File
@@ -1,12 +0,0 @@
package ssdb
import (
"fmt"
"os"
)
func ssdbLog(a ...interface{}) {
if os.Getenv("SSDB_DEBUG") != "" {
fmt.Println(a...)
}
}
-183
View File
@@ -1,183 +0,0 @@
package ssdb
import (
"os"
"github.com/aptly-dev/aptly/database"
"github.com/aptly-dev/aptly/database/goleveldb"
"github.com/seefan/gossdb/v2"
"github.com/seefan/gossdb/v2/conf"
"github.com/seefan/gossdb/v2/pool"
)
type Storage struct {
cfg *conf.Config
db *pool.Client
}
// CreateTemporary creates new DB of the same type in temp dir
func (s *Storage) CreateTemporary() (database.Storage, error) {
// use leveldb as temp db
tmpPath := os.Getenv("SSDB_TMPDB_PATH")
if tmpPath == "" {
tmpPath = "/tmp/ssdb_tmpdb_path"
}
gdb, err := goleveldb.NewDB(tmpPath)
if err != nil {
return nil, err
}
return gdb.CreateTemporary()
}
// Get key value from ssdb
func (s *Storage) Get(key []byte) (value []byte, err error) {
// ssdbLog("ssdb origin db get key:", string(key))
getResp, err := s.db.Get(string(key))
if err != nil {
return
}
value = getResp.Bytes()
if len(value) == 0 {
err = database.ErrNotFound
return
}
return
}
// Put saves key to ssdb, if key has the same value in DB already, it is not saved
func (s *Storage) Put(key []byte, value []byte) (err error) {
//ssdbLog("ssdb origin db put key:", string(key), " value: ", string(value))
err = s.db.Set(string(key), value)
if err != nil {
return
}
return
}
// Delete removes key from ssdb
func (s *Storage) Delete(key []byte) (err error) {
//ssdbLog("ssdb origin db del key:", string(key))
err = s.db.Del(string(key))
if err != nil {
return
}
return
}
// KeysByPrefix returns all keys that start with prefix
func (s *Storage) KeysByPrefix(prefix []byte) [][]byte {
result := make([][]byte, 0)
getResp, err := s.db.Keys(string(prefix), string(prefix)+"}", -1)
if err != nil {
return nil
}
for _, ev := range getResp {
key := []byte(ev)
keyc := make([]byte, len(key))
copy(keyc, key)
result = append(result, key)
}
return result
}
// FetchByPrefix returns all values with keys that start with prefix
func (s *Storage) FetchByPrefix(prefix []byte) [][]byte {
result := make([][]byte, 0)
getResp, err := s.db.Scan(string(prefix), string(prefix)+"}", -1)
if err != nil {
return nil
}
for _, ev := range getResp {
value := ev.Bytes()
valuec := make([]byte, len(value))
copy(valuec, value)
result = append(result, valuec)
}
return result
}
// HasPrefix checks whether it can find any key with given prefix and returns true if one exists
func (s *Storage) HasPrefix(prefix []byte) bool {
//ssdbLog("HasPrefix", string(prefix), string(prefix)+"}")
getResp, err := s.db.Keys(string(prefix), string(prefix)+"}", -1)
if err != nil {
return false
}
//ssdbLog("HasPrefix", len(getResp))
if len(getResp) > 0 {
return true
}
return false
}
// ProcessByPrefix iterates through all entries where key starts with prefix and calls
// StorageProcessor on key value pair
func (s *Storage) ProcessByPrefix(prefix []byte, proc database.StorageProcessor) error {
getResp, err := s.db.Scan(string(prefix), string(prefix)+"}", -1)
if err != nil {
return err
}
for k, v := range getResp {
err := proc([]byte(k), v.Bytes())
if err != nil {
return err
}
}
return nil
}
// Close finishes ssdb connect
func (s *Storage) Close() error {
ssdbLog("ssdb close")
if s.db != nil {
s.db.Close()
s.db = nil
}
gossdb.Shutdown()
return nil
}
// Reopen tries to open (re-open) the database
func (s *Storage) Open() error {
ssdbLog("ssdb open")
if s.db != nil && s.db.IsOpen() {
ssdbLog("ssdb opened")
return nil
}
var err error
s.db, err = internalOpen(s.cfg)
return err
}
// CreateBatch creates a Batch object
func (s *Storage) CreateBatch() database.Batch {
Batch := internalOpenBatch(s)
Batch.cfg = s.cfg
Batch.db = s.db
return Batch
}
// OpenTransaction creates new transaction.
func (s *Storage) OpenTransaction() (database.Transaction, error) {
return internalOpenTransaction(s)
}
// CompactDB compacts database by merging layers
func (s *Storage) CompactDB() error {
return nil
}
// Drop removes all the ssdb files (DANGEROUS!)
func (s *Storage) Drop() error {
return nil
}
// Check interface
var (
_ database.Storage = &Storage{}
)
-188
View File
@@ -1,188 +0,0 @@
package ssdb
import (
"fmt"
"github.com/aptly-dev/aptly/database"
)
type trWriteData struct {
key []byte
value []byte
opts string
err error
}
type trReadData struct {
kv []byte
err error
}
type transaction struct {
// for key-value-operation chan
w chan trWriteData
// key read chan
r chan trReadData
q map[string]trWriteData
t database.Storage
}
// func internalOpenTransaction...
func internalOpenTransaction(t database.Storage) (*transaction, error) {
tr := &transaction{
w: make(chan trWriteData),
r: make(chan trReadData),
q: make(map[string]trWriteData),
t: t,
}
return tr, tr.run()
}
// func run...
func (t *transaction) run() error {
go func() {
for {
select {
case w, ok := <-t.w:
{
if !ok {
ssdbLog("ssdb transaction write chan closed")
return
}
if w.opts == "commit" {
ssdbLog("ssdb transaction commit")
var errs []error
for _, vo := range t.q {
if vo.opts == "put" {
err := t.t.Put(vo.key, vo.value)
if err != nil {
//ssdbLog(err)
errs = append(errs, err)
}
}
if vo.opts == delOpt {
err := t.t.Delete(vo.key)
if err != nil {
errs = append(errs, err)
}
}
}
if len(errs) == 0 {
t.w <- trWriteData{
err: nil,
}
} else {
t.w <- trWriteData{
err: fmt.Errorf("ssdb transaction write errs: %v", errs),
}
}
ssdbLog("ssdb transaction commit end")
} else {
ssdbLog("ssdb transaction", w.opts)
//ssdbLog("ssdb r transaction", w.opts, "key: ", string(w.key), "value: ", string(w.value))
t.q[string(w.key)] = w
}
}
case r, ok := <-t.r:
{
if !ok {
ssdbLog("ssdb transaction read chan closed")
return
}
if rData, ok := t.q[string(r.kv)]; ok {
if rData.opts == delOpt {
// del return not found error
t.r <- trReadData{
kv: nil,
err: database.ErrNotFound,
}
} else {
t.r <- trReadData{
kv: rData.value,
err: nil,
}
}
} else {
v, err := t.t.Get(r.kv)
t.r <- trReadData{
kv: v,
err: err,
}
}
}
}
}
}()
return nil
}
// Get implements database.Reader interface.
func (t *transaction) Get(key []byte) ([]byte, error) {
keyc := make([]byte, len(key))
copy(keyc, key)
r := trReadData{
kv: keyc,
err: nil,
}
t.r <- r
result := <-t.r
return result.kv, result.err
}
// Put implements database.Writer interface.
func (t *transaction) Put(key, value []byte) error {
//ssdbLog("golf*********************ssdb put")
//ssdbLog("ssdb transaction db put key:", string(key), " value: ", string(value))
keyc := make([]byte, len(key))
copy(keyc, key)
valuec := make([]byte, len(value))
copy(valuec, value)
w := trWriteData{
key: keyc,
value: valuec,
opts: "put",
}
t.w <- w
return nil
}
// Delete implements database.Writer interface.
func (t *transaction) Delete(key []byte) error {
//return t.t.Delete(key)
//ssdbLog("golf*********************ssdb del")
keyc := make([]byte, len(key))
copy(keyc, key)
w := trWriteData{
key: keyc,
opts: delOpt,
}
t.w <- w
return nil
}
func (t *transaction) Commit() error {
w := trWriteData{
opts: "commit",
}
t.w <- w
result := <-t.w
return result.err
}
// Discard is safe to call after Commit(), it would be no-op
func (t *transaction) Discard() {
ssdbLog("ssdb transaction stop")
close(t.r)
close(t.w)
}
// transaction should implement database.Transaction
var _ database.Transaction = &transaction{}
+10
View File
@@ -27,6 +27,8 @@ type LocalRepo struct {
Uploaders *Uploaders `codec:"Uploaders,omitempty" json:"-"` Uploaders *Uploaders `codec:"Uploaders,omitempty" json:"-"`
// "Snapshot" of current list of packages // "Snapshot" of current list of packages
packageRefs *PackageRefList packageRefs *PackageRefList
// ldap group for repos
LdapGroup string `codec:",ldap-group"`
} }
// NewLocalRepo creates new instance of Debian local repository // NewLocalRepo creates new instance of Debian local repository
@@ -54,6 +56,14 @@ func (repo *LocalRepo) NumPackages() int {
return repo.packageRefs.Len() return repo.packageRefs.Len()
} }
// LdapGroup returns the ldapgroup if any for the repo
func (repo *LocalRepo) GetLDGroup() string {
if repo.LdapGroup != "" {
return fmt.Sprintf("[%s]", repo.LdapGroup)
}
return ""
}
// RefList returns package list for repo // RefList returns package list for repo
func (repo *LocalRepo) RefList() *PackageRefList { func (repo *LocalRepo) RefList() *PackageRefList {
return repo.packageRefs return repo.packageRefs
+16 -1
View File
@@ -341,6 +341,7 @@ The legacy json configuration is still supported (and also supports comments):
// Storage. First, publishing endpoints should be described in the aptly // Storage. First, publishing endpoints should be described in the aptly
// configuration file. Each endpoint has its name and associated settings. // configuration file. Each endpoint has its name and associated settings.
"AzurePublishEndpoints": { "AzurePublishEndpoints": {
<<<<<<< HEAD
// // Endpoint Name // // Endpoint Name
// "test": { // "test": {
@@ -392,12 +393,26 @@ The legacy json configuration is still supported (and also supports comments):
// // See: Azure documentation https://docs.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string // // See: Azure documentation https://docs.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string
// // defaults to "https://<accountName>.blob.core.windows.net" // // defaults to "https://<accountName>.blob.core.windows.net"
// "endpoint": "" // "endpoint": ""
},
// Authorization for repos may be configured for ldap groups (and is extensible for others),
// default is no authorization.
"Auth": {
// // auth type, only supports ldap currently
// "authType: "",
// // auth server to use (eg. ldaps://ldap.example.com)
// "server\": "",
// // DN for ldap searches
// "ldapDN\": "",
// // ldap filter
// "ldapFilter": "",
// // enable secureTLS, default is off
// "secureTLS": false
} }
// End of config // End of config
} }
## PACKAGE QUERY ## PACKAGE QUERY
Some commands accept package queries to identify list of packages to process. Some commands accept package queries to identify list of packages to process.
+16
View File
@@ -13,6 +13,7 @@ import (
// ConfigStructure is structure of main configuration // ConfigStructure is structure of main configuration
type ConfigStructure struct { // nolint: maligned type ConfigStructure struct { // nolint: maligned
<<<<<<< HEAD
// General // General
RootDir string `json:"rootDir" yaml:"root_dir"` RootDir string `json:"rootDir" yaml:"root_dir"`
LogLevel string `json:"logLevel" yaml:"log_level"` LogLevel string `json:"logLevel" yaml:"log_level"`
@@ -63,6 +64,10 @@ type ConfigStructure struct { // nolint: maligned
SwiftPublishRoots map[string]SwiftPublishRoot `json:"SwiftPublishEndpoints" yaml:"swift_publish_endpoints"` SwiftPublishRoots map[string]SwiftPublishRoot `json:"SwiftPublishEndpoints" yaml:"swift_publish_endpoints"`
AzurePublishRoots map[string]AzureEndpoint `json:"AzurePublishEndpoints" yaml:"azure_publish_endpoints"` AzurePublishRoots map[string]AzureEndpoint `json:"AzurePublishEndpoints" yaml:"azure_publish_endpoints"`
PackagePoolStorage PackagePoolStorage `json:"packagePoolStorage" yaml:"packagepool_storage"` PackagePoolStorage PackagePoolStorage `json:"packagePoolStorage" yaml:"packagepool_storage"`
// Authentication
UseAuth bool `json:"useAuth"`
Auth AAuth `json:"Auth"`
} }
// DBConfig // DBConfig
@@ -211,9 +216,19 @@ type AzureEndpoint struct {
Endpoint string `json:"endpoint" yaml:"endpoint"` Endpoint string `json:"endpoint" yaml:"endpoint"`
} }
type AAuth struct {
Type string `json:"authType"`
Server string `json:"server"`
LdapDN string `json:"ldapDN"`
LdapFilter string `json:"ldapFilter"`
SecureTLS bool `json:"secureTLS"`
}
// Config is configuration for aptly, shared by all modules // Config is configuration for aptly, shared by all modules
var Config = ConfigStructure{ var Config = ConfigStructure{
RootDir: filepath.Join(os.Getenv("HOME"), ".aptly"), RootDir: filepath.Join(os.Getenv("HOME"), ".aptly"),
LogFile: "",
UseAuth: false, // should we enable auth
DownloadConcurrency: 4, DownloadConcurrency: 4,
DownloadLimit: 0, DownloadLimit: 0,
Downloader: "default", Downloader: "default",
@@ -243,6 +258,7 @@ var Config = ConfigStructure{
LogFormat: "default", LogFormat: "default",
ServeInAPIMode: false, ServeInAPIMode: false,
EnableSwaggerEndpoint: false, EnableSwaggerEndpoint: false,
Auth: AAuth{},
} }
// LoadConfig loads configuration from json file // LoadConfig loads configuration from json file
+9 -1
View File
@@ -155,7 +155,15 @@ func (s *ConfigSuite) TestSaveConfig(c *C) {
" \"packagePoolStorage\": {\n" + " \"packagePoolStorage\": {\n" +
" \"type\": \"local\",\n" + " \"type\": \"local\",\n" +
" \"path\": \"/tmp/aptly-pool\"\n" + " \"path\": \"/tmp/aptly-pool\"\n" +
" }\n" + " },\n" +
" \"useAuth\": false,\n"+
" \"Auth\": {\n"+
" \"authType\": \"\",\n"+
" \"server\": \"\",\n"+
" \"ldapDN\": \"\",\n"+
" \"ldapFilter\": \"\",\n"+
" \"secureTLS\": false\n"+
" }\n"+
"}") "}")
} }