package enidpsync import ( "context" "fmt" "net/http" "github.com/golang-jwt/jwt/v4" "github.com/google/uuid" "golang.org/x/xerrors" "cdr.dev/slog" "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/idpsync" "github.com/coder/coder/v2/coderd/runtimeconfig" "github.com/coder/coder/v2/coderd/util/slice" "github.com/coder/coder/v2/codersdk" ) func (e EnterpriseIDPSync) RoleSyncEntitled() bool { return e.entitlements.Enabled(codersdk.FeatureUserRoleManagement) } func (e EnterpriseIDPSync) OrganizationRoleSyncEnabled(ctx context.Context, db database.Store, orgID uuid.UUID) (bool, error) { if !e.RoleSyncEntitled() { return false, nil } roleSyncSettings, err := e.Role.Resolve(ctx, e.Manager.OrganizationResolver(db, orgID)) if err != nil { if xerrors.Is(err, runtimeconfig.ErrEntryNotFound) { return false, nil } return false, err } return roleSyncSettings.Field != "", nil } func (e EnterpriseIDPSync) SiteRoleSyncEnabled() bool { if !e.RoleSyncEntitled() { return false } return e.AGPLIDPSync.SiteRoleField != "" } func (e EnterpriseIDPSync) ParseRoleClaims(ctx context.Context, mergedClaims jwt.MapClaims) (idpsync.RoleParams, *idpsync.HTTPError) { if !e.RoleSyncEntitled() { return e.AGPLIDPSync.ParseRoleClaims(ctx, mergedClaims) } var claimRoles []string if e.AGPLIDPSync.SiteRoleField != "" { var err error // TODO: Smoke test this error for org and site claimRoles, err = e.AGPLIDPSync.RolesFromClaim(e.AGPLIDPSync.SiteRoleField, mergedClaims) if err != nil { rawType := mergedClaims[e.AGPLIDPSync.SiteRoleField] e.Logger.Error(ctx, "oidc claims user roles field was an unknown type", slog.F("type", fmt.Sprintf("%T", rawType)), slog.F("field", e.AGPLIDPSync.SiteRoleField), slog.F("raw_value", rawType), slog.Error(err), ) // TODO: Determine a static page or not return idpsync.RoleParams{}, &idpsync.HTTPError{ Code: http.StatusInternalServerError, Msg: "Login disabled until site wide OIDC config is fixed", Detail: fmt.Sprintf("Roles claim must be an array of strings, type found: %T. Disabling role sync will allow login to proceed.", rawType), RenderStaticPage: false, } } } siteRoles := append([]string{}, e.SiteDefaultRoles...) for _, role := range claimRoles { if mappedRoles, ok := e.SiteRoleMapping[role]; ok { if len(mappedRoles) == 0 { continue } // Mapped roles are added to the list of roles siteRoles = append(siteRoles, mappedRoles...) continue } // Append as is. siteRoles = append(siteRoles, role) } return idpsync.RoleParams{ SyncEntitled: e.RoleSyncEntitled(), SyncSiteWide: e.SiteRoleSyncEnabled(), SiteWideRoles: slice.Unique(siteRoles), MergedClaims: mergedClaims, }, nil }