fleet/server/service/org_logo_test.go
Nico b4a207fb5a
Add ability to upload custom org logos (#44390)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #44330, Resolves #44331

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] Added/updated automated tests. (I'd defer integration tests to a
separate PR since this one is pretty large already.)

- [x] QA'd all new/changed functionality manually. I've tested this on
both the setup flow and the organization settings page. I haven't had
the time to test this on other places where we render the logo (macOS
setup experience / MDM migration dialog).


https://github.com/user-attachments/assets/95d4eae5-3da6-40f4-98a1-8575b97d96b3

## New Fleet configuration settings

- [x] Setting(s) is/are explicitly excluded from GitOps.

Will handle GitOps in a separate PR.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Organizations can upload custom logos for light and dark modes.
* Registration and Org Settings support logo file upload, preview,
per-mode replace/delete, and validation (size & image formats).
* Activity feed records logo changes/deletions; site nav displays
uploaded logos per theme.
* File uploader/preview adds a Fleet logo graphic option and improved
logo validation.
  * Config/GitOps outputs now include separate dark/light logo fields.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-05 14:42:52 +02:00

126 lines
3.2 KiB
Go

package service
import (
"bytes"
"context"
"testing"
"github.com/fleetdm/fleet/v4/server/authz"
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mock"
"github.com/fleetdm/fleet/v4/server/ptr"
"github.com/stretchr/testify/require"
)
func TestOrgLogoAuth(t *testing.T) {
ds := new(mock.Store)
svc, ctx := newTestService(t, ds, nil, nil)
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
return &fleet.AppConfig{}, nil
}
ds.SaveAppConfigFunc = func(ctx context.Context, conf *fleet.AppConfig) error {
return nil
}
testCases := []struct {
name string
user *fleet.User
shouldFailWrite bool // PUT and DELETE
}{
{
"global admin",
&fleet.User{GlobalRole: ptr.String(fleet.RoleAdmin)},
false,
},
{
"global maintainer",
&fleet.User{GlobalRole: ptr.String(fleet.RoleMaintainer)},
true,
},
{
"global observer",
&fleet.User{GlobalRole: ptr.String(fleet.RoleObserver)},
true,
},
{
"global observer+",
&fleet.User{GlobalRole: ptr.String(fleet.RoleObserverPlus)},
true,
},
{
"global gitops",
&fleet.User{GlobalRole: ptr.String(fleet.RoleGitOps)},
true,
},
{
"team admin",
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleAdmin}}},
true,
},
{
"team maintainer",
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleMaintainer}}},
true,
},
{
"team observer",
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleObserver}}},
true,
},
{
"team observer+",
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleObserverPlus}}},
true,
},
{
"team gitops",
&fleet.User{Teams: []fleet.UserTeam{{Team: fleet.Team{ID: 1}, Role: fleet.RoleGitOps}}},
true,
},
{
"user without roles",
&fleet.User{ID: 777},
true,
},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
authedCtx := viewer.NewContext(ctx, viewer.Viewer{User: tt.user})
err := svc.UploadOrgLogo(authedCtx, fleet.OrgLogoModeLight, bytes.NewReader([]byte{}))
checkOrgLogoAuth(t, tt.shouldFailWrite, err)
err = svc.DeleteOrgLogo(authedCtx, fleet.OrgLogoModeLight)
checkOrgLogoAuth(t, tt.shouldFailWrite, err)
// GET is public — never an authz failure regardless of viewer.
_, _, err = svc.GetOrgLogo(authedCtx, fleet.OrgLogoModeLight)
checkOrgLogoAuth(t, false, err)
})
}
// GET should also work without any viewer in the context (login page
// case). It may still fail downstream because no store is wired, but
// that's not an authz failure.
t.Run("public GET without viewer", func(t *testing.T) {
_, _, err := svc.GetOrgLogo(ctx, fleet.OrgLogoModeLight)
checkOrgLogoAuth(t, false, err)
})
}
func checkOrgLogoAuth(t *testing.T, shouldFail bool, err error) {
t.Helper()
var forbidden *authz.Forbidden
if shouldFail {
require.Error(t, err)
require.ErrorAs(t, err, &forbidden, "expected authz Forbidden, got %T: %v", err, err)
return
}
if err != nil {
require.NotErrorAs(t, err, &forbidden,
"expected non-authz error, got authz Forbidden: %v", err)
}
}