Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Public dashboard security rules #1558

Closed
wants to merge 17 commits into from
Closed
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions metrics/firebase/firestore/rules/firestore.rules
Original file line number Diff line number Diff line change
@@ -3,30 +3,31 @@ service cloud.firestore {
match /databases/{database}/documents {

match /project_groups/{projectGroupId} {
allow read, delete: if isAccessAuthorized(database);
allow read: if isAccessAuthorized(database) || isSignInProviderAnonymous();
allow delete: if isAccessAuthorized(database);
allow create, update: if isAccessAuthorized(database) && isProjectGroupValid();
}

match /projects/{projectId} {
allow read: if isAccessAuthorized(database);
allow read: if isAccessAuthorized(database) || isSignInProviderAnonymous();
allow create, update: if isAccessAuthorized(database) && isProjectValid();
allow delete: if false;
}

match /build/{buildId} {
allow read: if isAccessAuthorized(database);
allow read: if isAccessAuthorized(database) || isSignInProviderAnonymous();
allow create, update: if isAccessAuthorized(database) && isBuildValid(database);
allow delete: if false;
}

match /build_days/{buildDayId} {
allow read: if isAccessAuthorized(database);
allow read: if isAccessAuthorized(database) || isSignInProviderAnonymous();
allow write: if false;
}

match /user_profiles/{userProfileId} {
allow get: if isAccessAuthorized(database) && isDocumentOwner(userProfileId);
allow write: if isAccessAuthorized(database) && isDocumentOwner(userProfileId) && isUserProfileValid(database);
allow get: if (isAccessAuthorized(database) || isSignInProviderAnonymous()) && isDocumentOwner(userProfileId);
allow write: if (isAccessAuthorized(database) || isSignInProviderAnonymous()) && isDocumentOwner(userProfileId) && isUserProfileValid(database);
allow list: if false;
allow delete: if false;
}
@@ -68,11 +69,21 @@ service cloud.firestore {
return isEmailDomainExists;
}

/// Checks if the sign in provider from the request equals to the given parameter.
function checkSignInProvider(signInProvider) {
let authToken = request.auth.token;

return authToken.firebase.sign_in_provider == signInProvider;
}

/// Checks if the sign in provider from the request is a password.
function isSignInProviderPassword() {
let authToken = request.auth.token;
return checkSignInProvider('password');
}

return authToken.firebase.sign_in_provider == 'password';
/// Checks if the sign in provider from the request is an anonymous.
function isSignInProviderAnonymous() {
return checkSignInProvider('anonymous');
}

/// Checks whether project group data from the request is valid.
12 changes: 12 additions & 0 deletions metrics/firebase/test/firestore/rules/build-days-test.js
Original file line number Diff line number Diff line change
@@ -16,12 +16,24 @@ const {
buildDays,
getBuildDay,
allowedEmailDomains,
getAnonymousUser,
} = require("./test_utils/test-data");

describe("", async () => {
const collection = "build_days";
const anonymousSignIn = await getApplicationWith(getAnonymousUser());

const users = [
{
'describe': 'Authenticated as an anonymous user',
'app': anonymousSignIn,
'can': {
'create': false,
'read': true,
'update': false,
'delete': false,
}
},
{
'describe': 'Authenticated with a password and allowed email domain user with a verified email',
'app': await getApplicationWith(
12 changes: 12 additions & 0 deletions metrics/firebase/test/firestore/rules/builds-test.js
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ const {
getBuild,
allowedEmailDomains,
projects,
getAnonymousUser,
} = require("./test_utils/test-data");
const firestore = require("firebase").firestore;

@@ -25,9 +26,20 @@ describe("", async () => {
getAllowedEmailUser(passwordSignInProviderId, true)
);
const unauthenticatedApp = await getApplicationWith(null);
const anonymousSignIn = await getApplicationWith(getAnonymousUser());
const collection = "build";

const users = [
{
'describe': 'Authenticated as an anonymous user',
'app': anonymousSignIn,
'can': {
'create': false,
'read': true,
'update': false,
'delete': false,
}
},
{
'describe': 'Authenticated with a password and allowed email domain user with a verified email',
'app': await getApplicationWith(
14 changes: 13 additions & 1 deletion metrics/firebase/test/firestore/rules/project-groups-test.js
Original file line number Diff line number Diff line change
@@ -16,20 +16,32 @@ const {
passwordSignInProviderId,
allowedEmailDomains,
getProjectGroup,
getAnonymousUser,
} = require("./test_utils/test-data");

describe("", async function () {
const passwordProviderAllowedEmailApp = await getApplicationWith(
getAllowedEmailUser(passwordSignInProviderId, true)
);
const unauthenticatedApp = await getApplicationWith(null);
const anonymousSignIn = await getApplicationWith(getAnonymousUser());
const collection = "project_groups";

const users = [
{
'describe': 'Authenticated as an anonymous user',
'app': anonymousSignIn,
'can': {
'create': false,
'read': true,
'update': false,
'delete': false,
}
},
{
'describe': 'Authenticated with a password and allowed email domain user with a verified email',
'app': await getApplicationWith(
getAllowedEmailUser(passwordSignInProviderId, true)
getAllowedEmailUser(passwordSignInProviderId, true)
),
'can': {
'create': true,
12 changes: 12 additions & 0 deletions metrics/firebase/test/firestore/rules/projects-test.js
Original file line number Diff line number Diff line change
@@ -16,16 +16,28 @@ const {
passwordSignInProviderId,
googleSignInProviderId,
allowedEmailDomains,
getAnonymousUser,
} = require("./test_utils/test-data");

describe("", async function () {
const unauthenticatedApp = await getApplicationWith(null);
const passwordProviderAllowedEmailApp = await getApplicationWith(
getAllowedEmailUser(passwordSignInProviderId, true)
);
const anonymousSignIn = await getApplicationWith(getAnonymousUser());
const collection = "projects";

const users = [
{
'describe': 'Authenticated as an anonymous user',
'app': anonymousSignIn,
'can': {
'create': false,
'read': true,
'update': false,
'delete': false,
}
},
{
'describe': 'Authenticated with a password and allowed email domain user with a verified email',
'app': passwordProviderAllowedEmailApp,
10 changes: 9 additions & 1 deletion metrics/firebase/test/firestore/rules/test_utils/test-data.js
Original file line number Diff line number Diff line change
@@ -143,6 +143,9 @@ exports.allowedEmailDomains = allowedEmailDomains;
exports.featureConfig = featureConfig;
exports.tasks = tasks;

/** An anonymous sign in provider identifier */
exports.anonymousSignInProviderId = "anonymous";

/** An email and password sign in provider identifier */
exports.passwordSignInProviderId = "password";

@@ -154,11 +157,16 @@ exports.getAllowedEmailUser = function (signInProviderId, emailVerified, uid = "
return getUser(allowedEmail, signInProviderId, emailVerified, uid);
};

/** Provides a firebase user with not allowed email, sign-in provider identifier, and uid*/
/** Provides a firebase user with not allowed email, sign-in provider identifier, and uid */
exports.getDeniedEmailUser = function (signInProviderId, emailVerified, uid = "uid") {
return getUser(deniedEmail, signInProviderId, emailVerified, uid);
};

/** Provides a firebase anonymous user with the given uid */
exports.getAnonymousUser = function (uid = "uid") {
return getUser(null, "anonymous", null, uid);
};

/** Get a test project group */
exports.getProjectGroup = function () {
return cloneDeep(projectGroups["project_groups/1"]);
25 changes: 24 additions & 1 deletion metrics/firebase/test/firestore/rules/user-profiles-test.js
Original file line number Diff line number Diff line change
@@ -16,16 +16,39 @@ const {
userProfiles,
getUserProfile,
allowedEmailDomains,
getAnonymousUser,
} = require("./test_utils/test-data");

describe("", async () => {
const uid = "1";
const collection = "user_profiles";
const passwordProviderAllowedEmailApp = await getApplicationWith(
getAllowedEmailUser(passwordSignInProviderId, true, uid)
)
);

const users = [
{
'describe': 'Authenticated as an anonymous user who is not an owner of the user profile',
'app': await getApplicationWith(getAnonymousUser()),
'can': {
'create': false,
'list': false,
'read': false,
'update': false,
'delete': false,
}
},
{
'describe': 'Authenticated as an anonymous user who is an owner of the user profile',
'app': await getApplicationWith(getAnonymousUser(uid)),
'can': {
'create': true,
'list': false,
'get': true,
'update': true,
'delete': false,
}
},
{
'describe': 'Authenticated with a password, allowed email domain, and not a verified email user who is not an owner of the user profile',
'app': await getApplicationWith(