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

Add first concept for link-sharing #1831

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
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
105 changes: 105 additions & 0 deletions docs/auth/public_sharing_of_statistics.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,108 @@ From [Slack Canvas](https://boxwise.slack.com/docs/T99PBKNTU/F06QQS70XQT):
4. need to generate many endpoints for large data sets
5. OR: more open /organisation/<id> endpoint but needs server-side permissioning
6. consider rate-limiting or DDoS protection


# Draft for prototypical implementation

The following serves as a concept for a first implementation of the "link-sharing" functionality.

## Idea

1. Authenticated user of base X creates link for statviz page
1. Authenticated user shares link with external person
1. External person opens link
1. External person sees statviz page for base X without any navigation options
1. After one week, the link expires. When the link is opened, a short message is displayed.

## Architecture

### Fundamental assumptions

- the shared data will be live (as opposed to "freezing" the data to the time of link creation)
- link format is `.../<code> (can't use `.../bases/X/statviz`: insecure because it's easy to navigate to other bases and view their data; also not unique (can't expire))
- the expiration time of the link is one week. Later we can make is customizable, and/or add an action to invalidate a created link
- we expose the public app in the `api` GAE service. Hence it won't interfer with the main `app` service
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this already the case? Or something we're doing specifically for link sharing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we're not doing this yet. It's a suggestion for the link-sharing use case.


### Effects on full-stack

- there has to be a new action-based permission to allow link-creation for certain usergroups
- there has to be a public FE and a public GraphQL endpoint to fetch data
- the public FE is hosted under `api.boxtribute.org/shared` and deployed with the `deploy-api` CircleCI job (depends a new `build-statviz` job)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you share context on why you're suggesting this? It seems strange to me to host the FE on what appears to be an API endpoint?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggested this for simplicity (less devops effort because we don't need to establish a new subdomain nor any new GAE services). The endpoints we currently have for the api subdomains are the GraphQL one at / and the documentation at /docs.
For a separation of concerns it probably makes sense to have a dedicated subdomain for link-sharing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pylipp got it. Is there any other way we can achieve that? If the back end link sharing is going to be part of the standard API on the api subdomain, then any reason the link sharing can't just operate as part of the same FE code base? Or am I misunderstanding here?

- the public GraphQL endpoint is exposed for the `api` GAE service
- new DB view and user to avoid access to sensitive data

### Front-end

- the `statviz` folder already contains a public FE (showing the Dashboard at the route `/bases/X`)
- when a shared-link URL is requested, FE first issues a BE query to ask for link validitity. If positive, route to the corresponding page (under `.../shared/<code>/`, and query required data for display

#### UI considerations

- public FE: there won't be any authentication (login) in the FE when the external person opens the link
- public FE: no menues or navigation options are available
- v2: action button for creating link on statviz page (only viewable for users with resp. permission)
- v2: copy created link directly to clipboard, or display it for copying?

### Back-end

- a public GraphQL endpoint is already available at `/public` (development only)
- when statistics data is requested, the link code must be part of the request. It is validated that the link exists and has not expired, and that the link page type matches the requested data

#### GraphQL API

For v2:

```graphql
type Mutation {
createShareableLink(creationInput: LinkCreationInput): ShareableLinkCreationResult
}

input LinkCreationInput {
baseId: Int!
# everything after /
urlTail: String
page: ShareablePage!
}

enum ShareablePage {
StatvizDashboard
}

union ShareableLinkCreationResult = ShareableLink | InsufficientPermissionError

type ShareableLink {
id: ID!
code: String!
# to be inserted after /shared/<code>/
urlTail: String!
validUntil: Datetime
createdOn: Datetime!
createdBy: User
}
```

For the public GraphQL schema:

```graphql
type Query {
resolveShareableLink(code: String!): ShareableLinkResult

" All statistics queries must have a `code` parameter for validation in the public BE "
createdBoxes(baseId: Int!, code: String): CreatedBoxesData
}

union ShareableLinkResult = ShareableLink | ExpiredLinkError | UnknownLinkError

```

#### Database

- new `link_sharing` table to store meta-data (base ID, URL query parameters, expiration date, creation date, creator)
- corresponding peewee model

### Open questions

- how do avoid misuse of the link, or the exposed public GraphQL endpoint (DDoS mitigation)?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could look at rate limiting server side based on the link hash being passed in. Could also consider https://cloud.google.com/armor/docs

- can FE resolve a URL like `.../<code>` into a route like `.../<code>/bases/X/statviz` on the FE **and** process the returned data as if it was a GraphQL response?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when you say resolve, does the FE actually need to map to a different route? Is it just crafting a graphql query directly? I wasn't clear why the FE would need to "process the returned data as if it was a GraphQL response?" - or specifically, why it therefore wouldn't be a graphql response in the first place?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We (Hans, Felipe, me) talked about this yesterday; it seems to be feasible but requires adjustments to existing queries.

I'll try to explain it like this: in v2, when the user visits e.g. /bases/2/statviz, it will load the page and send a GraphQL request to the BE. A GraphQL response is returned and used to build the page.
For the scenario of link-sharing I was thinking whether the following is possible:

  1. user visits link at .../<code>
  2. FE sends request incl the code
  3. BE checks the code. Since it has saved the type of requested data at the time of link creation, it can now look up the data and return it in the response, incl the corresponding route for FE
  4. FE goes to the returned route and plugs in the returned data (instead of issuing another GraphQL request)
    You can see this as a mind experiment of mine rather 😄 does it make sense to you?

The approach we agreed on is different though:

  1. FE sends request with code
  2. BE validates code and sends metadata route
  3. FE goes to route and runs GraphQL queries to fetch data

- how do deal with routing changes (target of link becomes outdated)?
Loading