Полная схема (PlantUML)¶ Диаграмма рендерится из исходника .puml: BMS -> app-metabase-embedding -> Metabase (runtime auth + access)Client .BMS.UI.SecurityFilterChainRoutingDataSourceFilterOnPremBoxRoutingServiceDataSourceContextHolderBasicAuthenticationFilterLdapAuthFilterTokenAuthFilterTokenAuthenticationProviderUserAuthServiceRequestRoutingDataSourceRouting DB defaultRouting DB instance-.MetabaseWidgetsController . MetabaseSettingsController . AdminAccessController . MetabaseFavoritesControllerMetabaseWidgetsService . MetabaseSettingsService . EmployeeAccessService . MetabaseFavoritesServiceLdapDirectoryServiceLDAP DirectoryEmployeeAccessRepositoryRoleWidgetAccessStoreFavoritesStoreMetabaseCollectionResolverMetabaseCollectionAccessServiceMetabaseClientMetabase APIGuestTokenServiceApiExceptionHandlerClient (BMS/UI)Client (BMS/UI)SecurityFilterChainSecurityFilterChainRoutingDataSourceFilterRoutingDataSourceFilterOnPremBoxRoutingServiceOnPremBoxRoutingServiceDataSourceContextHolderDataSourceContextHolderBasicAuthenticationFilterBasicAuthenticationFilterLdapAuthFilterLdapAuthFilterTokenAuthFilterTokenAuthFilterTokenAuthenticationProviderTokenAuthenticationProviderUserAuthServiceUserAuthServiceRequestRoutingDataSourceRequestRoutingDataSourceRouting DB defaultRouting DB defaultRouting DB instance-*Routing DB instance-*MetabaseWidgetsController / MetabaseSettingsController / AdminAccessController / MetabaseFavoritesControllerMetabaseWidgetsController / MetabaseSettingsController / AdminAccessController / MetabaseFavoritesControllerMetabaseWidgetsService / MetabaseSettingsService / EmployeeAccessService / MetabaseFavoritesServiceMetabaseWidgetsService / MetabaseSettingsService / EmployeeAccessService / MetabaseFavoritesServiceLdapDirectoryServiceLdapDirectoryServiceLDAP DirectoryLDAP DirectoryEmployeeAccessRepositoryEmployeeAccessRepositoryRoleWidgetAccessStoreRoleWidgetAccessStoreFavoritesStoreFavoritesStoreMetabaseCollectionResolverMetabaseCollectionResolverMetabaseCollectionAccessServiceMetabaseCollectionAccessServiceMetabaseClientMetabaseClientMetabase APIMetabase APIGuestTokenServiceGuestTokenServiceApiExceptionHandlerApiExceptionHandler1/api/metabase/**2doFilterInternal()3resolveTokenRouting(X-BMS-DOMEN)4source by cached domain->instance_id mappingunknown/missing/non-token -> default-source5setCurrent(source)6process BasicAuthalt[Authorization Basic + LDAP bind success]7Authentication(principal=DirContextOperations)[no BasicAuth or bind failed]8continue9doFilterInternal()alt[authenticated LDAP principal and not AuthenticatedUser]10resolve uid + entryUUID11attributes12findTagsByUserId(ldap_user_uuid)13root + filter tags14build AccessContext(bms_ids[], instance_ids[])15convert to AuthenticatedUser(authSystem=LDAP)16put request context (userId, bmsId, ldapLogin)[skip]17unchanged18doFilterInternal()alt[AuthenticatedUser already exists (LDAP branch)]19skip token authentication[no AuthenticatedUser]20resolve token header / Bearer fallbackalt[token present]21authenticate(token)22authenticate(rawToken)23findByToken()24current sourcealt[source=default]25userdata + userdata_bms + bms_roles26user row[source=instance-*]27userdata + userdata_bms + bms_roles28user row29UserData30Optional<UserData>31Authentication(principal=AuthenticatedUser, authSystem=TOKEN)[token missing]32skip33authorize requestalt[/api/metabase/settings/widgets/access]34allow only AuthSystemType.TOKEN[/api/metabase/settings/admin/**]35allow only AuthSystemType.LDAP36login must be in app.ldap.adminLogins[other /api/metabase/**]37require authenticated (TOKEN or LDAP)alt[unauthorized / forbidden]38401/40339ProblemDetail[authorized]alt[Widgets endpoints]40listWidgets/getWidgetById/getWidgetsByIdsalt[authSystem == TOKEN]41findAllowedWidgetIds(source_key, bms_id, role_id)42widget ids43resolveRootCollectionIds(root-names.token)[authSystem == LDAP]44findAllowedWidgetIdsByUser(bms_ids[], ldap_user_uuid)45widget ids46findDeniedSubfolderIdsByUser(ldap_user_uuid)47denied collection ids48resolveRootCollectionIds by LDAP tags49findTagsByUserId(ldap_user_uuid)50tags51ROOT_COLLECTION tags only + tag-roots mapping + fallback tag.code52listCollections()53GET /api/collection54collections55collection idsloop[recursive tree]56listSubcollections + loadAllowedItems57GET /api/collection/{id}/items58request59items60filtered items input61apply archived/role/BMS/ACL filters62skip denied LDAP subfolders before descending into subtreeopt[page is provided]63recursively match folder name or collection idloop[each allowed widget]alt[structureOnly=false]64optional metadata fetch (/api/dashboard|card/{id})65request metadata66embedding_params67createToken(resource, params)68JWT[structureOnly=true]69return widget without token70widgets payload71200[Settings endpoints]72get/update widget access by role73read/write metabase_widget_access74resolve root collections75build access tree76200[Admin endpoints]77list users/tags, replace user tags, manage on-prem boxes,get/replace subfolder deny-list, update tag widget access78listEmployees()79search uid/givenName/sn/displayName/mail/entryUUID80users81read/write metabase_access_tag/metabase_user_tag/metabase_tag_widget_access82resolve root collection for selected user tag83read nested Metabase subfolders for selected root84read/write metabase_user_tag_denied_collection85resolve root collections for current LDAP admin user86compute widget/category scope87200[Favorites endpoints]88get/add/remove/reorder groups/items89read/write by source_key + bms_id + owner_user_id90200/20491clear() (finally)Error handling92MetabaseClientException93bubble up94502 Bad GatewaySecurity policy is path-dependent:- /settings/widgets/access: TokenAuth only- /settings/admin/**: LDAP BasicAuth + whitelist- others: TokenAuth or LDAP BasicAuthX-BMS-DOMEN is used only for TokenAuth requests.Domain -> instance_id mapping is cached in memory andinvalidated on admin writes to metabase_on_prem_box.LDAP root resolution:1) only ROOT_COLLECTION tags participate2) explicit tag-roots mapping3) fallback collection name = tag.codeLDAP nested subfolders use deny-list storage:only forbidden collection ids are stored,so new Metabase folders stay available by default.BMS -> app-metabase-embedding -> Metabase (runtime auth + access)Client .BMS.UI.SecurityFilterChainRoutingDataSourceFilterOnPremBoxRoutingServiceDataSourceContextHolderBasicAuthenticationFilterLdapAuthFilterTokenAuthFilterTokenAuthenticationProviderUserAuthServiceRequestRoutingDataSourceRouting DB defaultRouting DB instance-.MetabaseWidgetsController . MetabaseSettingsController . AdminAccessController . MetabaseFavoritesControllerMetabaseWidgetsService . MetabaseSettingsService . EmployeeAccessService . MetabaseFavoritesServiceLdapDirectoryServiceLDAP DirectoryEmployeeAccessRepositoryRoleWidgetAccessStoreFavoritesStoreMetabaseCollectionResolverMetabaseCollectionAccessServiceMetabaseClientMetabase APIGuestTokenServiceApiExceptionHandlerClient (BMS/UI)Client (BMS/UI)SecurityFilterChainSecurityFilterChainRoutingDataSourceFilterRoutingDataSourceFilterOnPremBoxRoutingServiceOnPremBoxRoutingServiceDataSourceContextHolderDataSourceContextHolderBasicAuthenticationFilterBasicAuthenticationFilterLdapAuthFilterLdapAuthFilterTokenAuthFilterTokenAuthFilterTokenAuthenticationProviderTokenAuthenticationProviderUserAuthServiceUserAuthServiceRequestRoutingDataSourceRequestRoutingDataSourceRouting DB defaultRouting DB defaultRouting DB instance-*Routing DB instance-*MetabaseWidgetsController / MetabaseSettingsController / AdminAccessController / MetabaseFavoritesControllerMetabaseWidgetsController / MetabaseSettingsController / AdminAccessController / MetabaseFavoritesControllerMetabaseWidgetsService / MetabaseSettingsService / EmployeeAccessService / MetabaseFavoritesServiceMetabaseWidgetsService / MetabaseSettingsService / EmployeeAccessService / MetabaseFavoritesServiceLdapDirectoryServiceLdapDirectoryServiceLDAP DirectoryLDAP DirectoryEmployeeAccessRepositoryEmployeeAccessRepositoryRoleWidgetAccessStoreRoleWidgetAccessStoreFavoritesStoreFavoritesStoreMetabaseCollectionResolverMetabaseCollectionResolverMetabaseCollectionAccessServiceMetabaseCollectionAccessServiceMetabaseClientMetabaseClientMetabase APIMetabase APIGuestTokenServiceGuestTokenServiceApiExceptionHandlerApiExceptionHandler1/api/metabase/**2doFilterInternal()3resolveTokenRouting(X-BMS-DOMEN)4source by cached domain->instance_id mappingunknown/missing/non-token -> default-source5setCurrent(source)6process BasicAuthalt[Authorization Basic + LDAP bind success]7Authentication(principal=DirContextOperations)[no BasicAuth or bind failed]8continue9doFilterInternal()alt[authenticated LDAP principal and not AuthenticatedUser]10resolve uid + entryUUID11attributes12findTagsByUserId(ldap_user_uuid)13root + filter tags14build AccessContext(bms_ids[], instance_ids[])15convert to AuthenticatedUser(authSystem=LDAP)16put request context (userId, bmsId, ldapLogin)[skip]17unchanged18doFilterInternal()alt[AuthenticatedUser already exists (LDAP branch)]19skip token authentication[no AuthenticatedUser]20resolve token header / Bearer fallbackalt[token present]21authenticate(token)22authenticate(rawToken)23findByToken()24current sourcealt[source=default]25userdata + userdata_bms + bms_roles26user row[source=instance-*]27userdata + userdata_bms + bms_roles28user row29UserData30Optional<UserData>31Authentication(principal=AuthenticatedUser, authSystem=TOKEN)[token missing]32skip33authorize requestalt[/api/metabase/settings/widgets/access]34allow only AuthSystemType.TOKEN[/api/metabase/settings/admin/**]35allow only AuthSystemType.LDAP36login must be in app.ldap.adminLogins[other /api/metabase/**]37require authenticated (TOKEN or LDAP)alt[unauthorized / forbidden]38401/40339ProblemDetail[authorized]alt[Widgets endpoints]40listWidgets/getWidgetById/getWidgetsByIdsalt[authSystem == TOKEN]41findAllowedWidgetIds(source_key, bms_id, role_id)42widget ids43resolveRootCollectionIds(root-names.token)[authSystem == LDAP]44findAllowedWidgetIdsByUser(bms_ids[], ldap_user_uuid)45widget ids46findDeniedSubfolderIdsByUser(ldap_user_uuid)47denied collection ids48resolveRootCollectionIds by LDAP tags49findTagsByUserId(ldap_user_uuid)50tags51ROOT_COLLECTION tags only + tag-roots mapping + fallback tag.code52listCollections()53GET /api/collection54collections55collection idsloop[recursive tree]56listSubcollections + loadAllowedItems57GET /api/collection/{id}/items58request59items60filtered items input61apply archived/role/BMS/ACL filters62skip denied LDAP subfolders before descending into subtreeopt[page is provided]63recursively match folder name or collection idloop[each allowed widget]alt[structureOnly=false]64optional metadata fetch (/api/dashboard|card/{id})65request metadata66embedding_params67createToken(resource, params)68JWT[structureOnly=true]69return widget without token70widgets payload71200[Settings endpoints]72get/update widget access by role73read/write metabase_widget_access74resolve root collections75build access tree76200[Admin endpoints]77list users/tags, replace user tags, manage on-prem boxes,get/replace subfolder deny-list, update tag widget access78listEmployees()79search uid/givenName/sn/displayName/mail/entryUUID80users81read/write metabase_access_tag/metabase_user_tag/metabase_tag_widget_access82resolve root collection for selected user tag83read nested Metabase subfolders for selected root84read/write metabase_user_tag_denied_collection85resolve root collections for current LDAP admin user86compute widget/category scope87200[Favorites endpoints]88get/add/remove/reorder groups/items89read/write by source_key + bms_id + owner_user_id90200/20491clear() (finally)Error handling92MetabaseClientException93bubble up94502 Bad GatewaySecurity policy is path-dependent:- /settings/widgets/access: TokenAuth only- /settings/admin/**: LDAP BasicAuth + whitelist- others: TokenAuth or LDAP BasicAuthX-BMS-DOMEN is used only for TokenAuth requests.Domain -> instance_id mapping is cached in memory andinvalidated on admin writes to metabase_on_prem_box.LDAP root resolution:1) only ROOT_COLLECTION tags participate2) explicit tag-roots mapping3) fallback collection name = tag.codeLDAP nested subfolders use deny-list storage:only forbidden collection ids are stored,so new Metabase folders stay available by default.