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 all 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
150 changes: 150 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,153 @@ 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 on a new `build-statviz` job)
- 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

- [x] how to avoid misuse of the link, or the exposed public GraphQL endpoint (DDoS mitigation)? E.g. by rate limiting server-side based on link code, or https://cloud.google.com/armor/docs
- [x] 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? Yes, in principal it's possible
- [x] how do deal with routing changes (target of link becomes outdated)? We'd have to add redirection

### Alternative back-end

This section discusses the use of an external password-storage service like [PasswordPusher](https://github.com/pglombardo/PasswordPusher?tab=readme-ov-file) as a replacement for an in-house link-sharing back-end implementation.

#### PasswordPusher (PP)

PP is a popular service for sharing secrets over the web: when provided with a secret, PP creates a URL that can be shared with others in order for them to view the secret. The URL can be configured to expire after a certain time or a certain number of views. Also at link creation one can set a passphrase that must be provided when viewing the secret. PP in its basic form can be used for free via pwpush.com.

PP provides a [REST API](https://pwpush.com/api/1.1/pushes/show.en.html) for programmatic interaction with the service.

#### PP Alternatives

According to [this list](https://alternativeto.net/software/passwordpusher/), [onetimesecret](https://docs.onetimesecret.com/docs/rest-api) can be considered an alternative but it has the immense drawback that a secret can only be viewed once.

Other services in the list like vanish.so are either fairly unpopular and/or dont' provide any API.

The following uses PP as exemplatory service.

#### Usage as link-sharing back-end

It's possible to use PP as a back-end for the link-sharing feature:
1. When the user requests a link in v2, the parameters of the to-be-shared page are pushed to PP including appropriate configuration of link expiration and a passphrase. The unique URL code generated by PP is present to the user as the shareable link `api.boxtribute.org/shared/<code>`
1. When entering a shared link, the public FE fetches the page parameters stored for the given code (using the passphrase), and then redirects to this page (e.g. `/shared/<code>/bases/1/statviz`)
1. The page requests data from the public back-end (e.g. the statviz GraphQL requests), sending the code along
1. The BE validates the code and returns the data

#### Advantages

- we can save the entire BE business and database logic around creating and storing shared links (incl. not having to take care of link expiration; plus having the passphrase as extra security mechanism (albeit it can be inspected easily when the public FE sends a request to the public BE))
- PP provides unlimited creation of links, however with rate-limit
- PP is open-source and [trusted by many organisations and considered secure](https://docs.pwpush.com/docs/faq/#trust-is-a-concern--why-should-i-trust-and-use-password-pusher)

#### Disadvantages

- less data insight: we don't have control over created links (no information about amount or which pages are being shared; at least not without further tracking)
- increased complexity: we add a second interface that FE has to communicate with; and we have to define a data structure to store information on PP
- increased complexity: we add an external service for the BE to communicate with
- dependency: using PP as storage, we become depend and have to update boxtribute (FE or BE or both) if the service becomes unavailable

#### Conclusion

Indecisive. If it's not a major concern to depend on the availability of PP, using it as a back-end makes sense since we save implementation work.

Eventually PP can also be self-hosted (to keep take control, and to be independent of an external service) but this would come with increased DevOps effort, and the inconvenience of introducing a second database to store shared links.