Skip to content

Commit

Permalink
feat: forward the Argo CD logged in user to the proxy extension (#19075)
Browse files Browse the repository at this point in the history
* feat: forward the Argo CD logged in user to the proxy extension

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

* Add docs

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>

---------

Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>
  • Loading branch information
leoluz authored Jul 16, 2024
1 parent 7af5793 commit c5d0acf
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 8 deletions.
4 changes: 4 additions & 0 deletions docs/developer-guide/extensions/proxy-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ Note that additional pre-configured headers can be added to outgoing
request. See [backend service headers](#extensionsbackendservicesheaders-list)
section for more details.

#### `Argocd-Username`

Will be populated with the username logged in Argo CD.

### Multi Backend Use-Case

In some cases when Argo CD is configured to sync with multiple remote
Expand Down
27 changes: 22 additions & 5 deletions server/extension/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ const (
// the client, its value will be overridden by the extension
// handler.
HeaderArgoCDTargetClusterName = "Argocd-Target-Cluster-Name"

// HeaderArgoCDUsername is the header name that defines the logged
// in user authenticated by Argo CD.
HeaderArgoCDUsername = "Argocd-Username"
)

// RequestResources defines the authorization scope for
Expand Down Expand Up @@ -302,8 +306,13 @@ type Manager struct {
rbac RbacEnforcer
registry ExtensionRegistry
metricsReg ExtensionMetricsRegistry
username UsernameGetterFunc
}

// UsernameGetterFunc defines the function signature to retrieve the username
// from the context.Context. The real implementation is defined in session.Username()
type UsernameGetterFunc func(context.Context) string

// ExtensionMetricsRegistry exposes operations to update http metrics in the Argo CD
// API server.
type ExtensionMetricsRegistry interface {
Expand All @@ -317,13 +326,14 @@ type ExtensionMetricsRegistry interface {
}

// NewManager will initialize a new manager.
func NewManager(log *log.Entry, sg SettingsGetter, ag ApplicationGetter, pg ProjectGetter, rbac RbacEnforcer) *Manager {
func NewManager(log *log.Entry, sg SettingsGetter, ag ApplicationGetter, pg ProjectGetter, rbac RbacEnforcer, userFn UsernameGetterFunc) *Manager {
return &Manager{
log: log,
settings: sg,
application: ag,
project: pg,
rbac: rbac,
username: userFn,
}
}

Expand Down Expand Up @@ -699,7 +709,7 @@ func (m *Manager) CallExtension() func(http.ResponseWriter, *http.Request) {
return
}

prepareRequest(r, extName, app)
prepareRequest(r, extName, app, m.username(r.Context()))
m.log.Debugf("proxing request for extension %q", extName)
// httpsnoop package is used to properly wrap the responseWriter
// and avoid optional intefaces issue:
Expand All @@ -719,16 +729,23 @@ func registerMetrics(extName string, metrics httpsnoop.Metrics, extensionMetrics
}

// prepareRequest is responsible for cleaning the incoming request URL removing
// the Argo CD extension API section from it. It will set the cluster destination name
// and cluster destination server in the headers as it is defined in the given app.
func prepareRequest(r *http.Request, extName string, app *v1alpha1.Application) {
// the Argo CD extension API section from it. It provides additional information to
// the backend service appending them in the outgoing request headers. The appended
// headers are:
// - Cluster destination name
// - Cluster destination server
// - Argo CD authenticated username
func prepareRequest(r *http.Request, extName string, app *v1alpha1.Application, username string) {
r.URL.Path = strings.TrimPrefix(r.URL.Path, fmt.Sprintf("%s/%s", URLPrefix, extName))
if app.Spec.Destination.Name != "" {
r.Header.Set(HeaderArgoCDTargetClusterName, app.Spec.Destination.Name)
}
if app.Spec.Destination.Server != "" {
r.Header.Set(HeaderArgoCDTargetClusterURL, app.Spec.Destination.Server)
}
if username != "" {
r.Header.Set(HeaderArgoCDUsername, username)
}
}

// AddMetricsRegistry will associate the given metricsReg in the Manager.
Expand Down
9 changes: 7 additions & 2 deletions server/extension/extension_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func TestRegisterExtensions(t *testing.T) {

logger, _ := test.NewNullLogger()
logEntry := logger.WithContext(context.Background())
m := extension.NewManager(logEntry, settMock, nil, nil, nil)
m := extension.NewManager(logEntry, settMock, nil, nil, nil, nil)

return &fixture{
settingsGetterMock: settMock,
Expand Down Expand Up @@ -249,6 +249,10 @@ func TestCallExtension(t *testing.T) {
}
defaultProjectName := "project-name"

usernameFn := func(context.Context) string {
return "loggedinUser"
}

setup := func() *fixture {
appMock := &mocks.ApplicationGetter{}
settMock := &mocks.SettingsGetter{}
Expand All @@ -258,7 +262,7 @@ func TestCallExtension(t *testing.T) {

logger, _ := test.NewNullLogger()
logEntry := logger.WithContext(context.Background())
m := extension.NewManager(logEntry, settMock, appMock, projMock, rbacMock)
m := extension.NewManager(logEntry, settMock, appMock, projMock, rbacMock, usernameFn)
m.AddMetricsRegistry(metricsMock)

mux := http.NewServeMux()
Expand Down Expand Up @@ -437,6 +441,7 @@ func TestCallExtension(t *testing.T) {
assert.Equal(t, backendResponse, actual)
assert.Equal(t, clusterURL, resp.Header.Get(extension.HeaderArgoCDTargetClusterURL))
assert.Equal(t, "Bearer some-bearer-token", resp.Header.Get("Authorization"))
assert.Equal(t, "loggedinUser", resp.Header.Get(extension.HeaderArgoCDUsername))

// waitgroup is necessary to make sure assertions aren't executed before
// the goroutine initiated by extension.CallExtension concludes which would
Expand Down
2 changes: 1 addition & 1 deletion server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts, appsetOpts Applicatio
sg := extension.NewDefaultSettingsGetter(settingsMgr)
ag := extension.NewDefaultApplicationGetter(appLister)
pg := extension.NewDefaultProjectGetter(projLister, dbInstance)
em := extension.NewManager(logger, sg, ag, pg, enf)
em := extension.NewManager(logger, sg, ag, pg, enf, util_session.Username)

a := &ArgoCDServer{
ArgoCDServerOpts: opts,
Expand Down

0 comments on commit c5d0acf

Please sign in to comment.