mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-05-31 04:30:44 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8fa1922477 | |||
| 836137f15d |
@@ -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
@@ -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
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+17
-2
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"+
|
||||||
"}")
|
"}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user