diff --git a/src/frontend/packages/core/src/features/endpoints/endpoints-page/endpoints-page.component.ts b/src/frontend/packages/core/src/features/endpoints/endpoints-page/endpoints-page.component.ts index ac54b4de52..29d79a534c 100644 --- a/src/frontend/packages/core/src/features/endpoints/endpoints-page/endpoints-page.component.ts +++ b/src/frontend/packages/core/src/features/endpoints/endpoints-page/endpoints-page.component.ts @@ -34,6 +34,7 @@ import { } from '../../../shared/components/list/list-types/endpoint/endpoints-list-config.service'; import { ListConfig } from '../../../shared/components/list/list.component.types'; import { SnackBarService } from '../../../shared/services/snackbar.service'; +import { SessionService } from '../../../shared/services/session.service'; @Component({ selector: 'app-endpoints-page', @@ -45,7 +46,7 @@ import { SnackBarService } from '../../../shared/services/snackbar.service'; }, EndpointListHelper] }) export class EndpointsPageComponent implements AfterViewInit, OnDestroy, OnInit { - public canRegisterEndpoint = [StratosCurrentUserPermissions.EDIT_ADMIN_ENDPOINT, StratosCurrentUserPermissions.EDIT_ENDPOINT]; + public canRegisterEndpoint = [StratosCurrentUserPermissions.EDIT_ADMIN_ENDPOINT]; private healthCheckTimeout: number; public canBackupRestore$: Observable; @@ -68,6 +69,7 @@ export class EndpointsPageComponent implements AfterViewInit, OnDestroy, OnInit private snackBarService: SnackBarService, cs: CustomizationService, currentUserPermissionsService: CurrentUserPermissionsService, + public sessionService: SessionService ) { this.customizations = cs.get(); @@ -93,6 +95,10 @@ export class EndpointsPageComponent implements AfterViewInit, OnDestroy, OnInit map(sessionData => sessionData?.plugins.backup), switchMap(enabled => enabled ? currentUserPermissionsService.can(this.canRegisterEndpoint[0]) : of(false)) ); + + this.sessionService.userEndpointsEnabled().subscribe(enabled => { + if(enabled) this.canRegisterEndpoint.push(StratosCurrentUserPermissions.EDIT_ENDPOINT); + }); } subs: Subscription[] = []; diff --git a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.html b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.html index 088da94032..a432179f91 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.html +++ b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.html @@ -44,7 +44,7 @@ - + Created by
{{ row.creator.admin ? "Admin" : "User" }}
diff --git a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts index c9df05430d..0451257ffa 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts @@ -32,6 +32,7 @@ import { BaseEndpointsDataSource } from '../base-endpoints-data-source'; import { EndpointListDetailsComponent, EndpointListHelper } from '../endpoint-list.helpers'; import { RouterNav } from './../../../../../../../../store/src/actions/router.actions'; import { CopyToClipboardComponent } from './../../../../copy-to-clipboard/copy-to-clipboard.component'; +import { SessionService } from '../../../../../services/session.service'; @Component({ selector: 'app-endpoint-card', @@ -54,6 +55,7 @@ export class EndpointCardComponent extends CardCell implements On public cardStatus$: Observable; private subs: Subscription[] = []; public connectionStatus: string; + public enableUserEndpoints$: Observable; private componentRef: ComponentRef; @@ -121,6 +123,7 @@ export class EndpointCardComponent extends CardCell implements On private endpointListHelper: EndpointListHelper, private componentFactoryResolver: ComponentFactoryResolver, private userFavoriteManager: UserFavoriteManager, + private sessionService: SessionService, ) { super(); this.endpointIds$ = this.endpointIds.asObservable(); @@ -130,6 +133,7 @@ export class EndpointCardComponent extends CardCell implements On this.favorite = this.userFavoriteManager.getFavoriteEndpointFromEntity(this.row); const e = this.endpointCatalogEntity.definition; this.hasDetails = !!e && !!e.listDetailsComponent; + this.enableUserEndpoints$ = this.sessionService.userEndpointsEnabled(); } ngOnDestroy(): void { diff --git a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-list.helpers.ts b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-list.helpers.ts index 22fc0b07b9..de611bdde7 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-list.helpers.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-list.helpers.ts @@ -22,6 +22,7 @@ import { ConfirmationDialogService } from '../../../confirmation-dialog.service' import { createMetaCardMenuItemSeparator } from '../../list-cards/meta-card/meta-card-base/meta-card.component'; import { IListAction } from '../../list.component.types'; import { TableCellCustom } from '../../list.types'; +import { SessionService } from '../../../../../shared/services/session.service'; interface EndpointDetailsContainerRefs { componentRef: ComponentRef; @@ -67,6 +68,7 @@ export class EndpointListHelper { private currentUserPermissionsService: CurrentUserPermissionsService, private confirmDialog: ConfirmationDialogService, private snackBarService: SnackBarService, + private sessionService: SessionService, ) { } endpointActions(includeSeparators = false): IListAction[] { @@ -108,10 +110,10 @@ export class EndpointListHelper { }, label: 'Disconnect', description: ``, // Description depends on console user permission - createVisible: (row$: Observable) => combineLatest( - this.currentUserPermissionsService.can(StratosCurrentUserPermissions.EDIT_ENDPOINT), - row$ - ).pipe( + createVisible: (row$: Observable) => combineLatest([ + this.currentUserPermissionsService.can(StratosCurrentUserPermissions.EDIT_ENDPOINT), + row$ + ]).pipe( map(([isAdmin, row]) => { const isConnected = row.connectionStatus === 'connected'; return isConnected && (!row.system_shared_token || row.system_shared_token && isAdmin); @@ -157,21 +159,20 @@ export class EndpointListHelper { label: 'Unregister', description: 'Remove the endpoint', createVisible: (row$: Observable) => { - // TODO lock this behind featureflag return combineLatest([ + this.sessionService.userEndpointsEnabled(), this.currentUserPermissionsService.can(StratosCurrentUserPermissions.EDIT_ADMIN_ENDPOINT), this.currentUserPermissionsService.can(StratosCurrentUserPermissions.EDIT_ENDPOINT), row$ ]).pipe( - map(([isAdmin, isEndpointAdmin, row])=>{ - if(row.creator.admin){ + map(([userEndpointsEnabled, isAdmin, isEndpointAdmin, row])=>{ + if(!userEndpointsEnabled || row.creator.admin){ return isAdmin; }else{ return isEndpointAdmin || isAdmin; } }) ); - //return this.currentUserPermissionsService.can(StratosCurrentUserPermissions.EDIT_ENDPOINT) } }, { @@ -182,21 +183,20 @@ export class EndpointListHelper { label: 'Edit endpoint', description: 'Edit the endpoint', createVisible: (row$: Observable) => { - // TODO lock this behind featureflag return combineLatest([ + this.sessionService.userEndpointsEnabled(), this.currentUserPermissionsService.can(StratosCurrentUserPermissions.EDIT_ADMIN_ENDPOINT), this.currentUserPermissionsService.can(StratosCurrentUserPermissions.EDIT_ENDPOINT), row$ ]).pipe( - map(([isAdmin, isEndpointAdmin, row])=>{ - if(row.creator.admin){ + map(([userEndpointsEnabled, isAdmin, isEndpointAdmin, row])=>{ + if(!userEndpointsEnabled || row.creator.admin){ return isAdmin; }else{ return isEndpointAdmin || isAdmin; } }) ); - //return this.currentUserPermissionsService.can(StratosCurrentUserPermissions.EDIT_ENDPOINT) } }, ...customActions diff --git a/src/frontend/packages/core/src/shared/services/session.service.ts b/src/frontend/packages/core/src/shared/services/session.service.ts index 9990d0fed5..4253145203 100644 --- a/src/frontend/packages/core/src/shared/services/session.service.ts +++ b/src/frontend/packages/core/src/shared/services/session.service.ts @@ -17,4 +17,11 @@ export class SessionService { map(sessionData => sessionData.config.enableTechPreview || false) ); } + + userEndpointsEnabled(): Observable { + return this.store.select(selectSessionData()).pipe( + first(), + map(sessionData => sessionData.config.enableUserEndpoints || false) + ); + } } diff --git a/src/frontend/packages/store/src/types/auth.types.ts b/src/frontend/packages/store/src/types/auth.types.ts index e9730e6b3b..2f4a2f13db 100644 --- a/src/frontend/packages/store/src/types/auth.types.ts +++ b/src/frontend/packages/store/src/types/auth.types.ts @@ -34,6 +34,7 @@ export interface SessionDataConfig { APIKeysEnabled?: APIKeysEnabled; // Default value for Home View - show only favorited endpoints? homeViewShowFavoritesOnly?: boolean; + enableUserEndpoints?: boolean; } export interface SessionData { endpoints?: SessionEndpoints; diff --git a/src/jetstream/cnsi.go b/src/jetstream/cnsi.go index d0c7af4a6f..c328420b31 100644 --- a/src/jetstream/cnsi.go +++ b/src/jetstream/cnsi.go @@ -148,7 +148,10 @@ func (p *portalProxy) DoRegisterEndpoint(cnsiName string, apiEndpoint string, sk newCNSI.ClientSecret = clientSecret newCNSI.SSOAllowed = ssoAllowed newCNSI.SubType = subType - newCNSI.CreatedBy = userId + + if p.GetConfig().EnableUserEndpoints == true { + newCNSI.CreatedBy = userId + } err = p.setCNSIRecord(guid, newCNSI) @@ -255,12 +258,13 @@ func (p *portalProxy) ListAdminEndpoints(userID string) ([]*interfaces.CNSIRecor if err != nil { return cnsiList, err } - stratosAdmin := strings.Contains(strings.Join(tokenInfo.Scope, ""), "stratos.admin") + stratosAdmin := strings.Contains(strings.Join(tokenInfo.Scope, ""), p.GetConfig().ConsoleConfig.ConsoleAdminScope) if stratosAdmin == true { adminList = append(adminList, tokenInfo.UserGUID) } } adminList = append(adminList, userID) + adminList = append(adminList, "") //legacy endpoints dont have a creator //get a cnsi list from every admin found and given userID cnsiRepo, err := p.GetStoreFactory().EndpointStore() diff --git a/src/jetstream/info.go b/src/jetstream/info.go index f57c388a89..69ad213339 100644 --- a/src/jetstream/info.go +++ b/src/jetstream/info.go @@ -62,6 +62,7 @@ func (p *portalProxy) getInfo(c echo.Context) (*interfaces.Info, error) { s.Configuration.ListAllowLoadMaxed = p.Config.UIListAllowLoadMaxed s.Configuration.APIKeysEnabled = string(p.Config.APIKeysEnabled) s.Configuration.HomeViewShowFavoritesOnly = p.Config.HomeViewShowFavoritesOnly + s.Configuration.EnableUserEndpoints = p.Config.EnableUserEndpoints // Only add diagnostics information if the user is an admin if uaaUser.Admin { @@ -99,13 +100,17 @@ func (p *portalProxy) getInfo(c echo.Context) (*interfaces.Info, error) { endpoint.SystemSharedToken = token.SystemShared } + // set the creator preemptively as admin, if no id is found + endpoint.Creator = &interfaces.CreatorInfo{ + Admin: true, + } + // try to get additional creator information for this cnsi - u, err := p.StratosAuthService.GetUser(cnsi.CreatedBy) - if err == nil { - creator := &interfaces.CreatorInfo{ - Admin: u.Admin, + if len(cnsi.CreatedBy) != 0 { + u, err := p.StratosAuthService.GetUser(cnsi.CreatedBy) + if err == nil { + endpoint.Creator.Admin = u.Admin } - endpoint.Creator = creator } cnsiType := cnsi.CNSIType diff --git a/src/jetstream/main.go b/src/jetstream/main.go index 4f6956cbf1..60f7f766cc 100644 --- a/src/jetstream/main.go +++ b/src/jetstream/main.go @@ -298,11 +298,6 @@ func main() { log.Info("Initialization complete.") - //TODO delete later - enableuserendpoints := portalProxy.GetConfig().EnableUserEndpoints - fmt.Println("User Endpoints enabled?") - fmt.Println(enableuserendpoints) - c := make(chan os.Signal, 2) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { diff --git a/src/jetstream/middleware.go b/src/jetstream/middleware.go index fa062e3d31..f9b38e85ea 100644 --- a/src/jetstream/middleware.go +++ b/src/jetstream/middleware.go @@ -279,16 +279,22 @@ func (p *portalProxy) endpointMiddleware(h echo.HandlerFunc) echo.HandlerFunc { return c.NoContent(http.StatusUnauthorized) } - creator, err := p.StratosAuthService.GetUser(cnsiRecord.CreatedBy) - if err != nil { - return c.NoContent(http.StatusUnauthorized) + // if CreatedBy is not set, then its a legacy admin-endpoint + creator := &interfaces.ConnectedUser{} + creator.Admin = true + if len(cnsiRecord.CreatedBy) != 0 { + creatorRecord, err := p.StratosAuthService.GetUser(cnsiRecord.CreatedBy) + if err != nil { + return c.NoContent(http.StatusUnauthorized) + } + creator = creatorRecord } if creator.Admin == true && u.Admin == false { return handleSessionError(p.Config, c, errors.New("Unauthorized"), false, "You must be Stratos admin to modify this endpoint.") } - if creator.Admin == false && u.Admin == false && creator.GUID != userID.(string) { + if creator.Admin == false && u.Admin == false && len(creator.GUID) != 0 && creator.GUID != userID.(string) { return handleSessionError(p.Config, c, errors.New("Unauthorized"), false, "Endpoint-admins are not allowed to modify endpoints created by other endpoint-admins.") } } diff --git a/src/jetstream/repository/interfaces/structs.go b/src/jetstream/repository/interfaces/structs.go index 1b65f639aa..e1887f7dd9 100644 --- a/src/jetstream/repository/interfaces/structs.go +++ b/src/jetstream/repository/interfaces/structs.go @@ -240,6 +240,7 @@ type Info struct { ListAllowLoadMaxed bool `json:"listAllowLoadMaxed,omitempty"` APIKeysEnabled string `json:"APIKeysEnabled"` HomeViewShowFavoritesOnly bool `json:"homeViewShowFavoritesOnly"` + EnableUserEndpoints bool `json:"enableUserEndpoints"` } `json:"config"` }