@@ -89,8 +89,18 @@ export class Contact {
8989 for ( const p of access . permissions ) {
9090 if ( ! p . streamId ) continue ;
9191 if ( p . streamId === '*' ) {
92+ if ( ! this . isPerson ) {
93+ // Bridge with wildcard: resolve bridge's own streams from access name
94+ // (access.name is typically the bridge's base stream ID, e.g. "bridge-mira")
95+ const bridgeStream = access . name ? streamsById [ access . name ] : null ;
96+ if ( bridgeStream ) {
97+ const ids = getStreamIdAndChildrenIds ( bridgeStream ) ;
98+ ids . forEach ( ( id : string ) => this . #accessibleStreamIds! . add ( id ) ) ;
99+ }
100+ continue ; // don't add wildcard for bridges
101+ }
92102 this . #accessibleStreamIds. add ( '*' ) ;
93- return ; // wildcard covers everything
103+ return ; // wildcard covers everything for person contacts
94104 }
95105 const stream = streamsById [ p . streamId ] ;
96106 if ( ! stream ) continue ;
@@ -100,8 +110,26 @@ export class Contact {
100110 }
101111 }
102112
103- /** Check if an event is in a stream accessible by this contact */
113+ /**
114+ * Check if an event belongs to this contact's scope.
115+ * - Person contacts (doctors): event is in a stream covered by their access permissions
116+ * - Bridge/service contacts: event was created by the bridge OR is in the bridge's streams
117+ * (bridges typically have wildcard `*` permissions but should only show their own data)
118+ */
104119 eventIsAccessible ( event : pryv . Event ) : boolean {
120+ if ( ! this . isPerson ) {
121+ // Bridge/service contacts: check authorship first (fastest)
122+ if ( this . eventIsFromContact ( event ) ) return true ;
123+ // Also check if event is in the bridge's own stream tree
124+ // (covers data created by older accesses not in previousAccessIds chain)
125+ if ( this . #accessibleStreamIds && ! this . #accessibleStreamIds. has ( '*' ) && event . streamIds ) {
126+ for ( const streamId of event . streamIds ) {
127+ if ( this . #accessibleStreamIds. has ( streamId ) ) return true ;
128+ }
129+ }
130+ return false ;
131+ }
132+ // Person contacts: filter by stream permissions
105133 if ( ! this . #accessibleStreamIds) return false ;
106134 if ( this . #accessibleStreamIds. has ( '*' ) ) return true ;
107135 if ( ! event . streamIds ) return false ;
@@ -115,9 +143,12 @@ export class Contact {
115143 eventIsFromContact ( event : pryv . Event ) : boolean {
116144 for ( const access of this . accessObjects ) {
117145 if ( access . id && event . modifiedBy === access . id ) return true ;
118- // Check previous access IDs from replaced accesses (access update chain)
119- const prevIds = access . clientData ?. hdsCollectorClient ?. previousAccessIds ;
120- if ( Array . isArray ( prevIds ) && prevIds . includes ( event . modifiedBy ) ) return true ;
146+ // Check previous access IDs from replaced accesses (collector pattern)
147+ const collectorPrevIds = access . clientData ?. hdsCollectorClient ?. previousAccessIds ;
148+ if ( Array . isArray ( collectorPrevIds ) && collectorPrevIds . includes ( event . modifiedBy ) ) return true ;
149+ // Check previous access IDs from bridge access recreate pattern
150+ const bridgePrevIds = access . clientData ?. previousAccessIds ;
151+ if ( Array . isArray ( bridgePrevIds ) && bridgePrevIds . includes ( event . modifiedBy ) ) return true ;
121152 }
122153 return false ;
123154 }
0 commit comments