Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

RFE: remove pusher.com dependency #50

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,13 @@ GITHUB_API_URL=https://api.github.com
GITHUB_CLIENT_ID=redacted
GITHUB_CLIENT_SECRET=redacted
GITHUB_OAUTH_TOKEN=redacted
KEYCLOAK_API_URL=http://localhost:8080/auth
KEYCLOAK_CLIENT_ID=redacted
KEYCLOAK_CLIENT_SECRET=redacted
KEYCLOAK_REALM=redacted
HASH_SECRET=redacted
COTURN_USERNAME=redacted
COTURN_PASSWORD=redacted
ACTIVE_PUB_SUB_GATEWAY=pusher
ACTIVE_ICE_SERVER_PROVIDER=twilio
ACTIVE_IDENTITY_PROVIDER=github
26 changes: 26 additions & 0 deletions .env.local.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
DATABASE_URL=postgres://teletype:password@localhost:5432/teletype-server-dev
TEST_DATABASE_URL=postgres://teletype:password@localhost:5433/teletype-server-test
PORT=3000
PUSHER_APP_ID=348824
PUSHER_KEY=redacted
PUSHER_SECRET=redacted
PUSHER_CLUSTER=mt1
TWILIO_ACCOUNT=redacted
TWILIO_AUTH_TOKEN=redacted
NEW_RELIC_ENABLED=false
NEW_RELIC_APP_NAME=atom-teletype-development
NEW_RELIC_LICENSE_KEY=redacted
GITHUB_API_URL=https://api.github.com
GITHUB_CLIENT_ID=redacted
GITHUB_CLIENT_SECRET=redacted
GITHUB_OAUTH_TOKEN=redacted
KEYCLOAK_API_URL=http://localhost:8080/auth
KEYCLOAK_CLIENT_ID=teletype
KEYCLOAK_CLIENT_SECRET=redacted
KEYCLOAK_REALM=teletype
HASH_SECRET=redacted
COTURN_USERNAME=teletype
COTURN_PASSWORD=password
ACTIVE_PUB_SUB_GATEWAY=socketcluster
ACTIVE_ICE_SERVER_PROVIDER=coturn
ACTIVE_IDENTITY_PROVIDER=keycloak
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
node_modules
.env
newrelic_agent.log
database
test-database
keycloak-database
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,40 @@ To run teletype-server locally, you'll first need to have:
npm test
```

### Running locally using docker

This allows to deploy teletype-server to a full private solution.
Coturn is used instead of Twilio and SocketCluster instead of Pusher.

1. Clone and bootstrap

```
git clone https://github.com/atom/teletype-server.git
cd teletype-server
cp .env.local.example .env
docker-compose build
docker-compose up -d
createdb teletype-server-dev
createdb teletype-server-test
npm install
npm run migrate up
```

2. Copy the client ID and client secret for your OAuth app on github.com, and set those values in your `.env` file

3. Start the server

```
./script/server
```

4. Run the tests

```
npm test
```


## Deploying

Atom core team members can use [this guide](./docs/deployment.md) to test pull requests and deploy changes to production.
27 changes: 27 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,33 @@
},
"TWILIO_AUTH_TOKEN": {
"required": true
},
"COTURN_USERNAME": {
"required": true
},
"COTURN_PASSWORD": {
"required": true
},
"KEYCLOAK_API_URL": {
"required": true
},
"KEYCLOAK_CLIENT_ID": {
"required": true
},
"KEYCLOAK_CLIENT_SECRET": {
"required": true
},
"KEYCLOAK_REALM": {
"required": true
},
"ACTIVE_PUB_SUB_GATEWAY": {
"required": true
},
"ACTIVE_ICE_SERVER_PROVIDER": {
"required": true
},
"ACTIVE_IDENTITY_PROVIDER": {
"required": true
}
},
"formation": {},
Expand Down
10 changes: 10 additions & 0 deletions coturn/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM ubuntu:18.04

RUN apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y coturn

ADD turnserver.conf /etc/turnserver.conf

EXPOSE 3478

CMD exec /bin/bash -c "trap : TERM INT; /usr/bin/turnserver -c /etc/turnserver.conf -v"
6 changes: 6 additions & 0 deletions coturn/turnserver.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
no-stun
verbose
fingerprint
lt-cred-mech
user=teletype:password
realm=teletype
91 changes: 91 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
version: '3'

services:
database:
image: postgres
ports:
- 5432:5432
volumes:
- ./database:/var/lib/postgresql/data
restart: always
environment:
POSTGRES_USER: teletype
POSTGRES_PASSWORD: password
POSTGRES_DB: teletype-server-dev
networks:
teletype:

test-database:
image: postgres
ports:
- 5433:5432
volumes:
- ./test-database:/var/lib/postgresql/data
restart: always
environment:
POSTGRES_USER: teletype
POSTGRES_PASSWORD: password
POSTGRES_DB: teletype-server-test
networks:
teletype:

coturn:
image: coturn
build:
context: coturn
dockerfile: Dockerfile
ports:
- 3478:3478
restart: always
networks:
teletype:
aliases:
- coturn

socketcluster:
image: socketcluster
build:
context: socketcluster
dockerfile: Dockerfile
ports:
- 8000:8000
restart: always
networks:
teletype:
aliases:
- socketcluster

keycloak:
image: jboss/keycloak
ports:
- 8080:8080
restart: always
environment:
DB_VENDOR: postgres
DB_ADDR: keycloak-database
KEYCLOAK_USER: teletype
KEYCLOAK_PASSWORD: password
networks:
teletype:
aliases:
- keycloak

keycloak-database:
image: postgres
ports:
- 5434:5432
volumes:
- ./keycloak-database:/var/lib/postgresql/data
restart: always
environment:
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
POSTGRES_DB: keycloak
networks:
teletype:
aliases:
- keycloak-database


networks:
teletype:
11 changes: 10 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,18 @@ async function startServer (id) {
githubClientId: process.env.GITHUB_CLIENT_ID,
githubClientSecret: process.env.GITHUB_CLIENT_SECRET,
githubOauthToken: process.env.GITHUB_OAUTH_TOKEN,
keycloakApiUrl: process.env.KEYCLOAK_API_URL,
keycloakClientId: process.env.KEYCLOAK_CLIENT_ID,
keycloakClientSecret: process.env.KEYCLOAK_CLIENT_SECRET,
keycloakRealm: process.env.KEYCLOAK_REALM,
boomtownSecret: process.env.BOOMTOWN_SECRET,
hashSecret: process.env.HASH_SECRET,
port: process.env.PORT || 3000
port: process.env.PORT || 3000,
coturnUsername: process.env.COTURN_USERNAME,
coturnPassword: process.env.COTURN_PASSWORD,
activePubSubGateway: process.env.ACTIVE_PUB_SUB_GATEWAY,
activeIceServerProvider: process.env.ACTIVE_ICE_SERVER_PROVIDER,
activeIdentityProvider: process.env.ACTIVE_IDENTITY_PROVIDER
})
await server.start()
console.log(`Worker ${id} (pid: ${process.pid}): listening on port ${server.port}`)
Expand Down
27 changes: 27 additions & 0 deletions lib/coturn-ice-server-provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module.exports =
class CoturnIceServerProvider {
constructor ({coturnUsername, coturnPassword}) {
this.coturnUsername = coturnUsername
this.coturnPassword = coturnPassword
}

async fetchICEServers () {
return {ttl: 86400, servers: [
{
'urls': 'stun:stun.l.google.com:19302'
},
{
'urls': 'turn:localhost:3478?transport=udp',
'username': this.coturnUsername,
'credential': this.coturnPassword
},
{
'urls': 'turn:localhost:3478?transport=tcp',
'username': this.coturnUsername,
'credential': this.coturnPassword
}
]
}

}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const {StatusCodeError} = require('request-promise-core/lib/errors')

module.exports =
class IdentityProvider {
class GithubIdentityProvider {
constructor ({request, apiUrl, oauthToken}) {
this.request = request
this.apiUrl = apiUrl
Expand Down
80 changes: 80 additions & 0 deletions lib/keycloak-identity-provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const {StatusCodeError} = require('request-promise-core/lib/errors')

module.exports =
class KeycloakIdentityProvider {
constructor ({request, apiUrl, clientId, clientSecret, realm}) {
this.request = request
this.apiUrl = apiUrl
this.clientId = clientId
this.clientSecret = clientSecret
this.realm = realm
}

make_base_auth(clientId, clientSecret) {
var token = clientId + ':' + clientSecret
var hash = Buffer.from(token).toString('base64')
return "Basic " + hash
}

async identityForToken (oauthToken) {
try {
var options = {
method: 'POST',
uri: `${this.apiUrl}/realms/${this.realm}/protocol/openid-connect/token/introspect`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'api.teletype.atom.io',
'Authorization': this.make_base_auth(this.clientId, this.clientSecret)
},
body: `token=${oauthToken}`
}
const response = await this.request(options)
const user = JSON.parse(response)

if (!user.active) {
const error = new Error('Token not provided.')
error.statusCode = 400
throw error
}
return {id: user.sub, login: user.username}
} catch (e) {
let errorMessage, statusCode
if (e instanceof StatusCodeError) {
const error = JSON.parse(e.error)
const description = (error.message != null) ? error.message : e.error
errorMessage = `${this.apiUrl} responded with ${e.statusCode}: ${description}`
statusCode = e.statusCode
} else if (e instanceof Error) {
errorMessage = `Failed to query ${this.apiUrl}: ${e.message}`
statusCode = 400
} else {
errorMessage = `Failed to query ${this.apiUrl}: ${e.message}`
statusCode = 500
}

const error = new Error(errorMessage)
error.statusCode = statusCode
throw error
}
}

async isOperational () {
try {
var options = {
method: 'POST',
uri: `${this.apiUrl}/realms/${this.realm}/protocol/openid-connect/token/introspect`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'api.teletype.atom.io',
'Authorization': this.make_base_auth(this.clientId, this.clientSecret)
},
body: `token=`
}

await this.request(options)
return true
} catch (e) {
return false
}
}
}
Loading