Skip to content

Commit fb1f1e2

Browse files
authored
Merge 6a0270a into f47f990
2 parents f47f990 + 6a0270a commit fb1f1e2

File tree

9 files changed

+86
-31
lines changed

9 files changed

+86
-31
lines changed

.changeset/polite-readers-wait.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@firebase/database': patch
3+
'@firebase/rules-unit-testing': patch
4+
---
5+
6+
Fix detection of admin context in Realtime Database SDK

packages/database/index.node.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,19 @@ const ServerValue = Database.ServerValue;
5151
* @param app A valid FirebaseApp-like object
5252
* @param url A valid Firebase databaseURL
5353
* @param version custom version e.g. firebase-admin version
54+
* @param nodeAdmin true if the SDK is being initialized from Firebase Admin.
5455
*/
55-
export function initStandalone(app: FirebaseApp, url: string, version: string) {
56+
export function initStandalone(
57+
app: FirebaseApp,
58+
url: string,
59+
version: string,
60+
nodeAdmin: boolean = true
61+
) {
5662
/**
5763
* This should allow the firebase-admin package to provide a custom version
5864
* to the backend
5965
*/
60-
CONSTANTS.NODE_ADMIN = true;
66+
CONSTANTS.NODE_ADMIN = nodeAdmin;
6167
setSDKVersion(version);
6268

6369
/**
@@ -84,7 +90,8 @@ export function initStandalone(app: FirebaseApp, url: string, version: string) {
8490
instance: RepoManager.getInstance().databaseFromApp(
8591
app,
8692
authProvider,
87-
url
93+
url,
94+
nodeAdmin
8895
) as types.Database,
8996
namespace: {
9097
Reference,

packages/database/src/api/Database.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,11 @@ export class Database implements FirebaseService {
105105
const apiName = 'database.refFromURL';
106106
this.checkDeleted_(apiName);
107107
validateArgCount(apiName, 1, 1, arguments.length);
108-
const parsedURL = parseRepoInfo(url);
108+
const parsedURL = parseRepoInfo(url, this.repo_.repoInfo_.nodeAdmin);
109109
validateUrl(apiName, 1, parsedURL);
110110

111111
const repoInfo = parsedURL.repoInfo;
112-
if (repoInfo.host !== (this.repo_.repoInfo_ as RepoInfo).host) {
112+
if (repoInfo.host !== this.repo_.repoInfo_.host) {
113113
fatal(
114114
apiName +
115115
': Host name does not match the current database: ' +

packages/database/src/core/PersistentConnection.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,7 @@ export class PersistentConnection extends ServerActions {
796796
.then(null, error => {
797797
self.log_('Failed to get token: ' + error);
798798
if (!canceled) {
799-
if (CONSTANTS.NODE_ADMIN) {
799+
if (this.repoInfo_.nodeAdmin) {
800800
// This may be a critical error for the Admin Node.js SDK, so log a warning.
801801
// But getToken() may also just have temporarily failed, so we still want to
802802
// continue retrying.
@@ -959,10 +959,12 @@ export class PersistentConnection extends ServerActions {
959959
const stats: { [k: string]: number } = {};
960960

961961
let clientName = 'js';
962-
if (CONSTANTS.NODE_ADMIN) {
963-
clientName = 'admin_node';
964-
} else if (CONSTANTS.NODE_CLIENT) {
965-
clientName = 'node';
962+
if (isNodeSdk()) {
963+
if (this.repoInfo_.nodeAdmin) {
964+
clientName = 'admin_node';
965+
} else {
966+
clientName = 'node';
967+
}
966968
}
967969

968970
stats['sdk.' + clientName + '.' + SDK_VERSION.replace(/\./g, '-')] = 1;

packages/database/src/core/RepoInfo.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,21 @@ export class RepoInfo {
3131
internalHost: string;
3232

3333
/**
34-
* @param {string} host Hostname portion of the url for the repo
35-
* @param {boolean} secure Whether or not this repo is accessed over ssl
36-
* @param {string} namespace The namespace represented by the repo
37-
* @param {boolean} webSocketOnly Whether to prefer websockets over all other transports (used by Nest).
38-
* @param {string=} persistenceKey Override the default session persistence storage key
34+
* @param host Hostname portion of the url for the repo
35+
* @param secure Whether or not this repo is accessed over ssl
36+
* @param namespace The namespace represented by the repo
37+
* @param webSocketOnly Whether to prefer websockets over all other transports (used by Nest).
38+
* @param nodeAdmin Whether this instance uses Admin SDK credentials
39+
* @param persistenceKey Override the default session persistence storage key
3940
*/
4041
constructor(
4142
host: string,
42-
public secure: boolean,
43-
public namespace: string,
44-
public webSocketOnly: boolean,
45-
public persistenceKey: string = '',
46-
public includeNamespaceInQueryParams: boolean = false
43+
public readonly secure: boolean,
44+
public readonly namespace: string,
45+
public readonly webSocketOnly: boolean,
46+
public readonly nodeAdmin: boolean = false,
47+
public readonly persistenceKey: string = '',
48+
public readonly includeNamespaceInQueryParams: boolean = false
4749
) {
4850
this.host = host.toLowerCase();
4951
this.domain = this.host.substr(this.host.indexOf('.') + 1);

packages/database/src/core/RepoManager.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ export class RepoManager {
9696
databaseFromApp(
9797
app: FirebaseApp,
9898
authProvider: Provider<FirebaseAuthInternalName>,
99-
url?: string
99+
url?: string,
100+
nodeAdmin?: boolean
100101
): Database {
101102
let dbUrl: string | undefined = url || app.options.databaseURL;
102103
if (dbUrl === undefined) {
@@ -111,7 +112,7 @@ export class RepoManager {
111112
dbUrl = `${app.options.projectId}-default-rtdb.firebaseio.com`;
112113
}
113114

114-
let parsedUrl = parseRepoInfo(dbUrl);
115+
let parsedUrl = parseRepoInfo(dbUrl, nodeAdmin);
115116
let repoInfo = parsedUrl.repoInfo;
116117

117118
let isEmulator: boolean;
@@ -124,14 +125,14 @@ export class RepoManager {
124125
if (dbEmulatorHost) {
125126
isEmulator = true;
126127
dbUrl = `http://${dbEmulatorHost}?ns=${repoInfo.namespace}`;
127-
parsedUrl = parseRepoInfo(dbUrl);
128+
parsedUrl = parseRepoInfo(dbUrl, nodeAdmin);
128129
repoInfo = parsedUrl.repoInfo;
129130
} else {
130131
isEmulator = !parsedUrl.repoInfo.secure;
131132
}
132133

133134
const authTokenProvider =
134-
CONSTANTS.NODE_ADMIN && isEmulator
135+
nodeAdmin && isEmulator
135136
? new EmulatorAdminTokenProvider()
136137
: new FirebaseAuthTokenProvider(app, authProvider);
137138

packages/database/src/core/util/libs/parser.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,9 @@ function decodeQuery(queryString: string): { [key: string]: string } {
6161
return results;
6262
}
6363

64-
/**
65-
*
66-
* @param {!string} dataURL
67-
* @return {{repoInfo: !RepoInfo, path: !Path}}
68-
*/
6964
export const parseRepoInfo = function (
70-
dataURL: string
65+
dataURL: string,
66+
nodeAdmin: boolean
7167
): { repoInfo: RepoInfo; path: Path } {
7268
const parsedUrl = parseDatabaseURL(dataURL),
7369
namespace = parsedUrl.namespace;
@@ -101,6 +97,7 @@ export const parseRepoInfo = function (
10197
parsedUrl.host,
10298
parsedUrl.secure,
10399
namespace,
100+
nodeAdmin,
104101
webSocketOnly,
105102
/*persistenceKey=*/ '',
106103
/*includeNamespaceInQueryParams=*/ namespace !== parsedUrl.subdomain

packages/database/src/realtime/WebSocketConnection.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export class WebSocketConnection implements Transport {
7676
private stats_: StatsCollection;
7777
private everConnected_: boolean;
7878
private isClosed_: boolean;
79+
private nodeAdmin: boolean;
7980

8081
/**
8182
* @param connId identifier for this transport
@@ -99,6 +100,7 @@ export class WebSocketConnection implements Transport {
99100
transportSessionId,
100101
lastSessionId
101102
);
103+
this.nodeAdmin = repoInfo.nodeAdmin;
102104
}
103105

104106
/**
@@ -151,7 +153,7 @@ export class WebSocketConnection implements Transport {
151153

152154
try {
153155
if (isNodeSdk()) {
154-
const device = ENV_CONSTANTS.NODE_ADMIN ? 'AdminNode' : 'Node';
156+
const device = this.nodeAdmin ? 'AdminNode' : 'Node';
155157
// UA Format: Firebase/<wire_protocol>/<sdk_version>/<platform>/<device>
156158
const options: { [k: string]: object } = {
157159
headers: {

packages/rules-unit-testing/test/database.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,44 @@ describe('Testing Module Tests', function () {
152152
);
153153
});
154154

155+
it('initializeAdminApp() and initializeTestApp() work together', async function () {
156+
await firebase.loadDatabaseRules({
157+
databaseName: 'foo',
158+
rules: JSON.stringify({
159+
'rules': {
160+
'public': { '.read': true, '.write': true },
161+
'private': { '.read': false, '.write': false }
162+
}
163+
})
164+
});
165+
166+
const adminApp = firebase.initializeAdminApp({
167+
projectId: 'foo',
168+
databaseName: 'foo'
169+
});
170+
171+
const testApp = firebase.initializeTestApp({
172+
projectId: 'foo',
173+
databaseName: 'foo'
174+
});
175+
176+
// Admin app can write anywhere
177+
await firebase.assertSucceeds(
178+
adminApp.database().ref().child('/public/doc').set({ hello: 'admin' })
179+
);
180+
await firebase.assertSucceeds(
181+
adminApp.database().ref().child('/private/doc').set({ hello: 'admin' })
182+
);
183+
184+
// Test app can only write to public, not to private
185+
await firebase.assertSucceeds(
186+
testApp.database().ref().child('/public/doc').set({ hello: 'test' })
187+
);
188+
await firebase.assertFails(
189+
testApp.database().ref().child('/private/doc').set({ hello: 'test' })
190+
);
191+
});
192+
155193
it('loadDatabaseRules() throws if no databaseName or rules', async function () {
156194
// eslint-disable-next-line @typescript-eslint/no-explicit-any
157195
await expect((firebase as any).loadDatabaseRules.bind(null, {})).to.throw(

0 commit comments

Comments
 (0)