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
Show file tree
Hide file tree
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
Expand Up @@ -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();
solid-maksymtielnyi marked this conversation as resolved.
Show resolved Hide resolved
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;
}
Expand Down Expand Up @@ -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() {
solid-maksymtielnyi marked this conversation as resolved.
Show resolved Hide resolved
return checkSignInProvider('anonymous');
}

/// Checks whether project group data from the request is valid.
Expand Down
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
Expand Up @@ -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(
Expand Down
12 changes: 12 additions & 0 deletions metrics/firebase/test/firestore/rules/builds-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const {
getBuild,
allowedEmailDomains,
projects,
getAnonymousUser,
} = require("./test_utils/test-data");
const firestore = require("firebase").firestore;

Expand All @@ -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(
Expand Down
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
Expand Up @@ -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,
Expand Down
12 changes: 12 additions & 0 deletions metrics/firebase/test/firestore/rules/projects-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
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
Expand Up @@ -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";

Expand All @@ -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"]);
Expand Down
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
Expand Up @@ -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(
Expand Down