Skip to content

Commit

Permalink
Merge pull request #245 from oslokommune/develop
Browse files Browse the repository at this point in the history
Release 2.0.0-rc.4
  • Loading branch information
aulonm authored Feb 19, 2021
2 parents f0f5e27 + 4e42921 commit d2fc2dd
Show file tree
Hide file tree
Showing 33 changed files with 2,195 additions and 1,889 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
node-version: [14.x]

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
Expand Down
36 changes: 35 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,45 @@ All notable changes to this project will be documented in this file. The format

## Unreleased

## [2.0.0-rc.3] 2021-01-26
## [2.0.0-rc.4] 2021-02-19

### BREAKING CHANGE

- Service account private key is not optional - it is required to add the private key json-file to firebase functions config (check out the [README](./README.md#supported-providers))
- Changes to environment variables - please read the [README](./README.md#environment-variables)
- Dropped support for Node < 14.x
- Required Node >= 14.x

### Experimental

- Keycloak integration: sign in using keycloak and use the keycloak token to create a custom token for firebase

### Added

- Keycloak integration
- Functions: tokenCreator - create custom tokens for custom auth integration with firebase
- LoadingSmall-component: a loading-animation that is smaller than the normal one
- Logout page: user is redirected here if there is a problem with signing in
- Store new unique id's to user-object for later migration to uid-control
- Alert-components

### Changes

- firestore-rules: Not allowed to read the user-objects without being signed in
- Better support to handling errors or if user does not have access to a resource
- Design of login page

### Removed

- Support for Node < 14.x

## [2.0.0-rc.3] 2021-01-26

### BREAKING CHANGE
- Drop IE 11 support

### Added

- Meta-tags for fb/twitter/open graph
- robots.txt

Expand Down
59 changes: 46 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# OKR Tracker

- [Project requirements](#project-requirements)
- [Clone and install](#clone-and-install)
- [Set up new instance](#set-up-new-instance)
- [Create Firebase project](#create-firebase-project)
Expand All @@ -23,6 +24,13 @@
- [Automated Restore with Cloud Functions](#automated-restore-with-cloud-functions)
- [Slack Integration](#slack-integration)
- [Set up](#set-up)
- [Supported Providers](#supported-providers)
- [Keycloak integration](#keycloak-integration)

## Project requirements

- Node 14.x
- Firebase >9.x

## Clone and install

Expand Down Expand Up @@ -57,6 +65,7 @@ firebase functions:config:set
sheets.email="<service account email>"
sheets.key="<service account private key>"
sheets.impersonator="email-address" (optional)
service_account="<service account private key json-file>"
```

**Note: The private key string needs to have actual line breaks as opposed to `\\n` because of an issue with how Firebase stores environment variables. [Read more](https://github.com/firebase/firebase-tools/issues/371).**
Expand All @@ -78,19 +87,25 @@ Get your Firebase SDK snippet from your [Firebase Console](https://console.fireb
- Under **Your apps**, find Firebase SDK snippet and press **Config**
- Copy the following secrets to a `.env.production` file in the root directory.

| Secret | Description |
| -------------------------------- | ------------------------- |
| `VUE_APP_API_KEY` | _from SDK snippet_ |
| `VUE_APP_AUTH_DOMAIN` | _from SDK snippet_ |
| `VUE_APP_DATABASE_URL` | _from SDK snippet_ |
| `VUE_APP_PROJECT_ID` | _from SDK snippet_ |
| `VUE_APP_STORAGE_BUCKET` | _from SDK snippet_ |
| `VUE_APP_MESSAGING_SENDER_ID` | _from SDK snippet_ |
| `VUE_APP_APP_ID` | _from SDK snippet_ |
| `VUE_APP_MEASUREMENT_ID` | _from SDK snippet_ |
| `VUE_APP_SHEETS_SERVICE_ACCOUNT` | \<service account email\> |
| `VUE_APP_I18N_LOCALE` | `nb-NO OR en-US` |
| `VUE_APP_REGION` | `europe-west2` |
| Secret | Description |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `VUE_APP_API_KEY` | _from SDK snippet_ |
| `VUE_APP_AUTH_DOMAIN` | _from SDK snippet_ |
| `VUE_APP_DATABASE_URL` | _from SDK snippet_ |
| `VUE_APP_PROJECT_ID` | _from SDK snippet_ |
| `VUE_APP_STORAGE_BUCKET` | _from SDK snippet_ |
| `VUE_APP_MESSAGING_SENDER_ID` | _from SDK snippet_ |
| `VUE_APP_APP_ID` | _from SDK snippet_ |
| `VUE_APP_MEASUREMENT_ID` | _from SDK snippet_ |
| `VUE_APP_SHEETS_SERVICE_ACCOUNT` | \<service account email\> |
| `VUE_APP_I18N_LOCALE` | `nb-NO OR en-US` |
| `VUE_APP_REGION` | `europe-west2` |
| `VUE_APP_LOGIN_PROVIDERS` | login providers allowed separated with hyphen - only implemented google, email and keycloak. Ex: `google-keycloak-email` |
| `VUE_APP_KEYCLOAK_URL` | _from keycloak server_ (if keycloak provided to `VUE_APP_LOGIN_PROVIDERS`) |
| `VUE_APP_KEYCLOAK_REALM` | _from keycloak server_ (if keycloak provided to `VUE_APP_LOGIN_PROVIDERS`) |
| `VUE_APP_KEYCLOAK_CLIENT_ID` | _from keycloak server_ (if keycloak provided to `VUE_APP_LOGIN_PROVIDERS`) |
| `VUE_APP_KEYCLOAK_LOGOUT_URL` | Where to redirect user after sign out (if keycloak provided to `VUE_APP_LOGIN_PROVIDERS`) |
| `VUE_APP_KEYCLOAK_ERROR_URL` | Where to redirect user when error signing in (if keycloak provided to `VUE_APP_LOGIN_PROVIDERS`) |

### Link project

Expand Down Expand Up @@ -339,3 +354,21 @@ firebase functions:config:set slack.deploymentWebhook="YOUR SLACK WEBHOOK HERE"

Request URL: https://<region>-<firebase-instance>.cloudfunctions.net/slackNotificationInteractiveOnRequest
```

## Supported providers

OKR-tracker supports for the time being only three login providers: Google, email/pass and Keycloak. If you are looking for other providers that firebase support, we would love for you to open up a PR with the needed changes.

### Keycloak integration

We support Keycloak as a login provider, but since Firebase does not support this out of the box, we have set up our own integration with Firebase' SignInWithCustomToken-method that is supplied by them.

For the integration to work you need to give your service account access to create tokens. You can do this by going to your GCP project, under the IAM and admin section and grant the service account access to `Service Account Token Creator` permission. Read more about it [here](https://firebase.google.com/docs/auth/admin/create-custom-tokens).

Download a new service account private key json-file and add it to your firebase config:

```
firebase functions:set service_account="$(cat private-key.json)"
```

DO NOT share your private key with anyone.
32 changes: 24 additions & 8 deletions firestore.rules
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// TODO: Migrate from email-check to uid-check
// Allow both at first and then remove email-check later on
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {

function isAdmin() {
return isSignedIn() && get(/databases/$(database)/documents/users/$(request.auth.token.email)).data.admin == true
let oldUser = get(/databases/$(database)/documents/users/$(request.auth.token.email)).data.admin == true;
let newUser = get(/databases/$(database)/documents/users/$(request.auth.token.uid)).data.admin == true;
return isSignedIn() && (oldUser || newUser);
}

function isSignedIn() {
Expand All @@ -12,27 +16,39 @@ service cloud.firestore {
}

function isTeamMember() {
return /databases/$(database)/documents/users/$(request.auth.token.email) in resource.data.team;
let oldUser = /databases/$(database)/documents/users/$(request.auth.token.email) in resource.data.team;
let newUser = /databases/$(database)/documents/users/$(request.auth.token.uid) in resource.data.team;
return oldUser || newUser;
}

function isMemberOfParent() {
return /databases/$(database)/documents/users/$(request.auth.token.email) in getAfter(request.resource.data.parent).data.team
let oldUser = /databases/$(database)/documents/users/$(request.auth.token.email) in getAfter(request.resource.data.parent).data.team;
let newUser = /databases/$(database)/documents/users/$(request.auth.token.uid) in getAfter(request.resource.data.parent).data.team;
return oldUser || newUser;
}

function isMemberOfParentDelete() {
return /databases/$(database)/documents/users/$(request.auth.token.email) in get(resource.data.parent).data.team
let oldUser = /databases/$(database)/documents/users/$(request.auth.token.email) in get(resource.data.parent).data.team;
let newUser = /databases/$(database)/documents/users/$(request.auth.token.uid) in get(resource.data.parent).data.team;
return oldUser || newUser;
}

function isMemberOfKeyResParent(document) {
return /databases/$(database)/documents/users/$(request.auth.token.email) in getAfter(get(/databases/$(database)/documents/keyResults/$(document)).data.parent).data.team
let oldUser = /databases/$(database)/documents/users/$(request.auth.token.uid) in getAfter(get(/databases/$(database)/documents/keyResults/$(document)).data.parent).data.team;
let newUser = /databases/$(database)/documents/users/$(request.auth.token.uid) in getAfter(get(/databases/$(database)/documents/keyResults/$(document)).data.parent).data.team;
return oldUser || newUser;
}

function isMemberOfKpiParent(document) {
return /databases/$(database)/documents/users/$(request.auth.token.email) in get(get(/databases/$(database)/documents/kpis/$(document)).data.parent).data.team
let oldUser = /databases/$(database)/documents/users/$(request.auth.token.email) in get(get(/databases/$(database)/documents/kpis/$(document)).data.parent).data.team;
let newUser = /databases/$(database)/documents/users/$(request.auth.token.uid) in get(get(/databases/$(database)/documents/kpis/$(document)).data.parent).data.team;
return oldUser || newUser;
}

function isSelf(document) {
return document == request.auth.token.email
let oldUser = document == request.auth.token.email;
let newUser = document == request.auth.token.uid;
return oldUser || newUser;
}


Expand All @@ -46,7 +62,7 @@ service cloud.firestore {
}

match /users/{user} {
allow read: if true;
allow read: if isSignedIn();
allow write: if isAdmin() || isSelf(user);
}

Expand Down
1 change: 1 addition & 0 deletions functions/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules/
.runtimeconfig.json
origo-okr-tracker-private-key.json
7 changes: 6 additions & 1 deletion functions/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const admin = require('firebase-admin');
const functions = require('firebase-functions');

// Initialize the app to get everything started
admin.initializeApp();
admin.initializeApp({
credential: admin.credential.cert(functions.config().service_account),
});

/**
* Functions for backup and restoring the Firestore database
Expand Down Expand Up @@ -59,3 +62,5 @@ exports.handleKeyResultProgressOnObjectiveUpdate = require('./progress').handleK

exports.slackNotificationOnUserRequest = require('./requestAccess').slackNotificationOnUserRequest;
exports.slackNotificationInteractiveOnRequest = require('./requestAccess').slackNotificationInteractiveOnRequest;

exports.createCustomToken = require('./tokenCreator').createCustomToken;
Loading

0 comments on commit d2fc2dd

Please sign in to comment.