Merge branch 'master' of github.com:Human-Connection/Human-Connection into 236-list-social-media-accounts

This commit is contained in:
Matt Rider 2019-03-25 17:32:06 -03:00
commit e409d6508f
22 changed files with 426 additions and 178 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
.env
.idea
*.iml
.vscode
.DS_Store
npm-debug.log*

View File

@ -14,7 +14,7 @@
"lint": "eslint src --config .eslintrc.js",
"test": "nyc --reporter=text-lcov yarn test:jest",
"test:cypress": "run-p --race test:before:*",
"test:before:server": "cross-env GRAPHQL_URI=http://localhost:4123 GRAPHQL_PORT=4123 babel-node src/ 2> /dev/null",
"test:before:server": "cross-env CLIENT_URI=http://localhost:4123 GRAPHQL_URI=http://localhost:4123 GRAPHQL_PORT=4123 babel-node src/ 2> /dev/null",
"test:before:seeder": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 PERMISSIONS=disabled babel-node src/ 2> /dev/null",
"test:jest:cmd": "wait-on tcp:4001 tcp:4123 && jest --forceExit --detectOpenHandles --runInBand",
"test:cucumber:cmd": "wait-on tcp:4001 tcp:4123 && cucumber-js --require-module @babel/register --exit test/",
@ -58,7 +58,7 @@
"graphql-shield": "~5.3.1",
"graphql-tag": "~2.10.1",
"graphql-yoga": "~1.17.4",
"helmet": "~3.15.1",
"helmet": "~3.16.0",
"jsonwebtoken": "~8.5.1",
"linkifyjs": "~2.1.8",
"lodash": "~4.17.11",
@ -76,7 +76,7 @@
},
"devDependencies": {
"@babel/cli": "~7.2.3",
"@babel/core": "~7.3.4",
"@babel/core": "~7.4.0",
"@babel/node": "~7.2.2",
"@babel/plugin-proposal-throw-expressions": "^7.2.0",
"@babel/preset-env": "~7.4.2",

View File

@ -22,9 +22,8 @@ let activityPub = null
export { activityPub }
export default class ActivityPub {
constructor (domain, port, uri) {
if (domain === 'localhost') { this.domain = `${domain}:${port}` } else { this.domain = domain }
this.port = port
constructor (host, uri) {
this.host = host
this.dataSource = new NitroDataSource(uri)
this.collections = new Collections(this.dataSource)
}
@ -32,8 +31,8 @@ export default class ActivityPub {
static init (server) {
if (!activityPub) {
dotenv.config()
const url = new URL(process.env.GRAPHQL_URI)
activityPub = new ActivityPub(url.hostname || 'localhost', url.port || 4000, url.origin)
const url = new URL(process.env.CLIENT_URI)
activityPub = new ActivityPub(url.host || 'localhost:4000', url.origin)
// integrate into running graphql express server
server.express.set('ap', activityPub)
@ -59,7 +58,7 @@ export default class ActivityPub {
}
}, async (err, response, toActorObject) => {
if (err) return reject(err)
debug(`name = ${toActorName}@${this.domain}`)
debug(`name = ${toActorName}@${this.host}`)
// save shared inbox
toActorObject = JSON.parse(toActorObject)
await this.dataSource.addSharedInboxEndpoint(toActorObject.endpoints.sharedInbox)
@ -184,7 +183,7 @@ export default class ActivityPub {
}
generateStatusId (slug) {
return `http://${this.domain}/activitypub/users/${slug}/status/${uuid()}`
return `https://${this.host}/activitypub/users/${slug}/status/${uuid()}`
}
async sendActivity (activity) {

View File

@ -11,14 +11,14 @@ export function createNoteObject (text, name, id, published) {
return {
'@context': 'https://www.w3.org/ns/activitystreams',
'id': `https://${activityPub.domain}/activitypub/users/${name}/status/${createUuid}`,
'id': `https://${activityPub.host}/activitypub/users/${name}/status/${createUuid}`,
'type': 'Create',
'actor': `https://${activityPub.domain}/activitypub/users/${name}`,
'actor': `https://${activityPub.host}/activitypub/users/${name}`,
'object': {
'id': `https://${activityPub.domain}/activitypub/users/${name}/status/${id}`,
'id': `https://${activityPub.host}/activitypub/users/${name}/status/${id}`,
'type': 'Note',
'published': published,
'attributedTo': `https://${activityPub.domain}/activitypub/users/${name}`,
'attributedTo': `https://${activityPub.host}/activitypub/users/${name}`,
'content': text,
'to': 'https://www.w3.org/ns/activitystreams#Public'
}
@ -64,8 +64,8 @@ export async function getActorId (name) {
export function sendAcceptActivity (theBody, name, targetDomain, url) {
as.accept()
.id(`https://${activityPub.domain}/activitypub/users/${name}/status/` + crypto.randomBytes(16).toString('hex'))
.actor(`https://${activityPub.domain}/activitypub/users/${name}`)
.id(`https://${activityPub.host}/activitypub/users/${name}/status/` + crypto.randomBytes(16).toString('hex'))
.actor(`https://${activityPub.host}/activitypub/users/${name}`)
.object(theBody)
.prettyWrite((err, doc) => {
if (!err) {
@ -79,8 +79,8 @@ export function sendAcceptActivity (theBody, name, targetDomain, url) {
export function sendRejectActivity (theBody, name, targetDomain, url) {
as.reject()
.id(`https://${activityPub.domain}/activitypub/users/${name}/status/` + crypto.randomBytes(16).toString('hex'))
.actor(`https://${activityPub.domain}/activitypub/users/${name}`)
.id(`https://${activityPub.host}/activitypub/users/${name}/status/` + crypto.randomBytes(16).toString('hex'))
.actor(`https://${activityPub.host}/activitypub/users/${name}`)
.object(theBody)
.prettyWrite((err, doc) => {
if (!err) {

View File

@ -6,21 +6,21 @@ export function createActor (name, pubkey) {
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
'id': `https://${activityPub.domain}/activitypub/users/${name}`,
'id': `https://${activityPub.host}/activitypub/users/${name}`,
'type': 'Person',
'preferredUsername': `${name}`,
'name': `${name}`,
'following': `https://${activityPub.domain}/activitypub/users/${name}/following`,
'followers': `https://${activityPub.domain}/activitypub/users/${name}/followers`,
'inbox': `https://${activityPub.domain}/activitypub/users/${name}/inbox`,
'outbox': `https://${activityPub.domain}/activitypub/users/${name}/outbox`,
'url': `https://${activityPub.domain}/activitypub/@${name}`,
'following': `https://${activityPub.host}/activitypub/users/${name}/following`,
'followers': `https://${activityPub.host}/activitypub/users/${name}/followers`,
'inbox': `https://${activityPub.host}/activitypub/users/${name}/inbox`,
'outbox': `https://${activityPub.host}/activitypub/users/${name}/outbox`,
'url': `https://${activityPub.host}/activitypub/@${name}`,
'endpoints': {
'sharedInbox': `https://${activityPub.domain}/activitypub/inbox`
'sharedInbox': `https://${activityPub.host}/activitypub/inbox`
},
'publicKey': {
'id': `https://${activityPub.domain}/activitypub/users/${name}#main-key`,
'owner': `https://${activityPub.domain}/activitypub/users/${name}`,
'id': `https://${activityPub.host}/activitypub/users/${name}#main-key`,
'owner': `https://${activityPub.host}/activitypub/users/${name}`,
'publicKeyPem': pubkey
}
}
@ -28,12 +28,12 @@ export function createActor (name, pubkey) {
export function createWebFinger (name) {
return {
'subject': `acct:${name}@${activityPub.domain}`,
'subject': `acct:${name}@${activityPub.host}`,
'links': [
{
'rel': 'self',
'type': 'application/activity+json',
'href': `https://${activityPub.domain}/users/${name}`
'href': `https://${activityPub.host}/activitypub/users/${name}`
}
]
}

View File

@ -5,10 +5,10 @@ const debug = require('debug')('ea:utils:collections')
export function createOrderedCollection (name, collectionName) {
return {
'@context': 'https://www.w3.org/ns/activitystreams',
'id': `https://${activityPub.domain}/activitypub/users/${name}/${collectionName}`,
'id': `https://${activityPub.host}/activitypub/users/${name}/${collectionName}`,
'summary': `${name}s ${collectionName} collection`,
'type': 'OrderedCollection',
'first': `https://${activityPub.domain}/activitypub/users/${name}/${collectionName}?page=true`,
'first': `https://${activityPub.host}/activitypub/users/${name}/${collectionName}?page=true`,
'totalItems': 0
}
}
@ -16,11 +16,11 @@ export function createOrderedCollection (name, collectionName) {
export function createOrderedCollectionPage (name, collectionName) {
return {
'@context': 'https://www.w3.org/ns/activitystreams',
'id': `https://${activityPub.domain}/activitypub/users/${name}/${collectionName}?page=true`,
'id': `https://${activityPub.host}/activitypub/users/${name}/${collectionName}?page=true`,
'summary': `${name}s ${collectionName} collection`,
'type': 'OrderedCollectionPage',
'totalItems': 0,
'partOf': `https://${activityPub.domain}/activitypub/users/${name}/${collectionName}`,
'partOf': `https://${activityPub.host}/activitypub/users/${name}/${collectionName}`,
'orderedItems': []
}
}

View File

@ -20,12 +20,12 @@ export function extractIdFromActivityId (uri) {
return splitted[splitted.indexOf('status') + 1]
}
export function constructIdFromName (name, fromDomain = activityPub.domain) {
export function constructIdFromName (name, fromDomain = activityPub.host) {
return `http://${fromDomain}/activitypub/users/${name}`
}
export function extractDomainFromUrl (url) {
return new URL(url).hostname
return new URL(url).host
}
export function throwErrorIfApolloErrorOccurred (result) {
@ -76,7 +76,7 @@ export function signAndSend (activity, fromName, targetDomain, url) {
'Host': targetDomain,
'Date': date,
'Signature': createSignature({ privateKey,
keyId: `http://${activityPub.domain}/activitypub/users/${fromName}#main-key`,
keyId: `http://${activityPub.host}/activitypub/users/${fromName}#main-key`,
url,
headers: {
'Host': targetDomain,

View File

@ -12,6 +12,6 @@ const serverConfig = {
const server = createServer()
server.start(serverConfig, options => {
/* eslint-disable-next-line no-console */
console.log(`Server ready at ${process.env.GRAPHQL_URI} 🚀`)
console.log(`Server ready at ${process.env.CLIENT_URI} 🚀`)
ActivityPub.init(server)
})

View File

@ -49,7 +49,7 @@ export default {
CreateUser: async (resolve, root, args, context, info) => {
const keys = generateRsaKeyPair()
Object.assign(args, keys)
args.actorId = `${process.env.GRAPHQL_URI}/activitypub/users/${args.slug}`
args.actorId = `${activityPub.host}/activitypub/users/${args.slug}`
return resolve(root, args, context, info)
}
}

View File

@ -19,7 +19,7 @@ Feature: Webfinger discovery
{
"rel": "self",
"type": "application/activity+json",
"href": "https://localhost:4123/users/peter-lustiger"
"href": "https://localhost:4123/activitypub/users/peter-lustiger"
}
]
}

View File

@ -38,18 +38,18 @@
dependencies:
"@babel/highlight" "^7.0.0"
"@babel/core@^7.1.0":
version "7.3.3"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.3.3.tgz#d090d157b7c5060d05a05acaebc048bd2b037947"
integrity sha512-w445QGI2qd0E0GlSnq6huRZWPMmQGCp5gd5ZWS4hagn0EiwzxD5QMFkpchyusAyVC1n27OKXzQ0/88aVU9n4xQ==
"@babel/core@^7.1.0", "@babel/core@~7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.4.0.tgz#248fd6874b7d755010bfe61f557461d4f446d9e9"
integrity sha512-Dzl7U0/T69DFOTwqz/FJdnOSWS57NpjNfCwMKHABr589Lg8uX1RrlBIJ7L5Dubt/xkLsx0xH5EBFzlBVes1ayA==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/generator" "^7.3.3"
"@babel/helpers" "^7.2.0"
"@babel/parser" "^7.3.3"
"@babel/template" "^7.2.2"
"@babel/traverse" "^7.2.2"
"@babel/types" "^7.3.3"
"@babel/generator" "^7.4.0"
"@babel/helpers" "^7.4.0"
"@babel/parser" "^7.4.0"
"@babel/template" "^7.4.0"
"@babel/traverse" "^7.4.0"
"@babel/types" "^7.4.0"
convert-source-map "^1.1.0"
debug "^4.1.0"
json5 "^2.1.0"
@ -58,49 +58,7 @@
semver "^5.4.1"
source-map "^0.5.0"
"@babel/core@~7.3.4":
version "7.3.4"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.3.4.tgz#921a5a13746c21e32445bf0798680e9d11a6530b"
integrity sha512-jRsuseXBo9pN197KnDwhhaaBzyZr2oIcLHHTt2oDdQrej5Qp57dCCJafWx5ivU8/alEYDpssYqv1MUqcxwQlrA==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/generator" "^7.3.4"
"@babel/helpers" "^7.2.0"
"@babel/parser" "^7.3.4"
"@babel/template" "^7.2.2"
"@babel/traverse" "^7.3.4"
"@babel/types" "^7.3.4"
convert-source-map "^1.1.0"
debug "^4.1.0"
json5 "^2.1.0"
lodash "^4.17.11"
resolve "^1.3.2"
semver "^5.4.1"
source-map "^0.5.0"
"@babel/generator@^7.0.0", "@babel/generator@^7.2.2", "@babel/generator@^7.3.3":
version "7.3.3"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.3.3.tgz#185962ade59a52e00ca2bdfcfd1d58e528d4e39e"
integrity sha512-aEADYwRRZjJyMnKN7llGIlircxTCofm3dtV5pmY6ob18MSIuipHpA2yZWkPlycwu5HJcx/pADS3zssd8eY7/6A==
dependencies:
"@babel/types" "^7.3.3"
jsesc "^2.5.1"
lodash "^4.17.11"
source-map "^0.5.0"
trim-right "^1.0.1"
"@babel/generator@^7.3.4":
version "7.3.4"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.3.4.tgz#9aa48c1989257877a9d971296e5b73bfe72e446e"
integrity sha512-8EXhHRFqlVVWXPezBW5keTiQi/rJMQTg/Y9uVCEZ0CAF3PKtCCaVRnp64Ii1ujhkoDhhF1fVsImoN4yJ2uz4Wg==
dependencies:
"@babel/types" "^7.3.4"
jsesc "^2.5.1"
lodash "^4.17.11"
source-map "^0.5.0"
trim-right "^1.0.1"
"@babel/generator@^7.4.0":
"@babel/generator@^7.0.0", "@babel/generator@^7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.4.0.tgz#c230e79589ae7a729fd4631b9ded4dc220418196"
integrity sha512-/v5I+a1jhGSKLgZDcmAUZ4K/VePi43eRkUs3yePW1HB1iANOD5tqJXwGSG4BZhSksP8J9ejSlwGeTiiOFZOrXQ==
@ -283,14 +241,14 @@
"@babel/traverse" "^7.1.0"
"@babel/types" "^7.2.0"
"@babel/helpers@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.2.0.tgz#8335f3140f3144270dc63c4732a4f8b0a50b7a21"
integrity sha512-Fr07N+ea0dMcMN8nFpuK6dUIT7/ivt9yKQdEEnjVS83tG2pHwPi03gYmk/tyuwONnZ+sY+GFFPlWGgCtW1hF9A==
"@babel/helpers@^7.4.0":
version "7.4.2"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.4.2.tgz#3bdfa46a552ca77ef5a0f8551be5f0845ae989be"
integrity sha512-gQR1eQeroDzFBikhrCccm5Gs2xBjZ57DNjGbqTaHo911IpmSxflOQWMAHPw/TXk8L3isv7s9lYzUkexOeTQUYg==
dependencies:
"@babel/template" "^7.1.2"
"@babel/traverse" "^7.1.5"
"@babel/types" "^7.2.0"
"@babel/template" "^7.4.0"
"@babel/traverse" "^7.4.0"
"@babel/types" "^7.4.0"
"@babel/highlight@^7.0.0":
version "7.0.0"
@ -312,17 +270,7 @@
lodash "^4.17.10"
v8flags "^3.1.1"
"@babel/parser@^7.0.0", "@babel/parser@^7.2.2", "@babel/parser@^7.2.3", "@babel/parser@^7.3.3":
version "7.3.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.3.tgz#092d450db02bdb6ccb1ca8ffd47d8774a91aef87"
integrity sha512-xsH1CJoln2r74hR+y7cg2B5JCPaTh+Hd+EbBRk9nWGSNspuo6krjhX0Om6RnRQuIvFq8wVXCLKH3kwKDYhanSg==
"@babel/parser@^7.1.0", "@babel/parser@^7.3.4":
version "7.3.4"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.4.tgz#a43357e4bbf4b92a437fb9e465c192848287f27c"
integrity sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ==
"@babel/parser@^7.4.0":
"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.0":
version "7.4.2"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.2.tgz#b4521a400cb5a871eab3890787b4bc1326d38d91"
integrity sha512-9fJTDipQFvlfSVdD/JBtkiY0br9BtfvW2R8wo6CX/Ej2eMuV0gWPk1M67Mt3eggQvBqYW1FCEk8BN7WvGm/g5g==
@ -720,46 +668,16 @@
dependencies:
regenerator-runtime "^0.13.2"
"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2":
version "7.2.2"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907"
integrity sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==
"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.0.tgz#12474e9c077bae585c5d835a95c0b0b790c25c8b"
integrity sha512-SOWwxxClTTh5NdbbYZ0BmaBVzxzTh2tO/TeLTbF6MO6EzVhHTnff8CdBXx3mEtazFBoysmEM6GU/wF+SuSx4Fw==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/parser" "^7.2.2"
"@babel/types" "^7.2.2"
"@babel/parser" "^7.4.0"
"@babel/types" "^7.4.0"
"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.5", "@babel/traverse@^7.2.2":
version "7.2.3"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.2.3.tgz#7ff50cefa9c7c0bd2d81231fdac122f3957748d8"
integrity sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/generator" "^7.2.2"
"@babel/helper-function-name" "^7.1.0"
"@babel/helper-split-export-declaration" "^7.0.0"
"@babel/parser" "^7.2.3"
"@babel/types" "^7.2.2"
debug "^4.1.0"
globals "^11.1.0"
lodash "^4.17.10"
"@babel/traverse@^7.3.4":
version "7.3.4"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.3.4.tgz#1330aab72234f8dea091b08c4f8b9d05c7119e06"
integrity sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/generator" "^7.3.4"
"@babel/helper-function-name" "^7.1.0"
"@babel/helper-split-export-declaration" "^7.0.0"
"@babel/parser" "^7.3.4"
"@babel/types" "^7.3.4"
debug "^4.1.0"
globals "^11.1.0"
lodash "^4.17.11"
"@babel/traverse@^7.4.0":
"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.4.0.tgz#14006967dd1d2b3494cdd650c686db9daf0ddada"
integrity sha512-/DtIHKfyg2bBKnIN+BItaIlEg5pjAnzHOIQe5w+rHAw/rg9g0V7T4rqPX8BJPfW11kt3koyjAnTNwCzb28Y1PA==
@ -774,7 +692,7 @@
globals "^11.1.0"
lodash "^4.17.11"
"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.3.4", "@babel/types@^7.4.0":
"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.4.0.tgz#670724f77d24cce6cc7d8cf64599d511d164894c"
integrity sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==
@ -4011,10 +3929,10 @@ helmet-csp@2.7.1:
dasherize "2.0.0"
platform "1.3.5"
helmet@~3.15.1:
version "3.15.1"
resolved "https://registry.yarnpkg.com/helmet/-/helmet-3.15.1.tgz#2c80d1a59138b6f23929605afca4b1c88b3298ec"
integrity sha512-hgoNe/sjKlKNvJ3g9Gz149H14BjMMWOCmW/DTXl7IfyKGtIK37GePwZrHNfr4aPXdKVyXcTj26RgRFbPKDy9lw==
helmet@~3.16.0:
version "3.16.0"
resolved "https://registry.yarnpkg.com/helmet/-/helmet-3.16.0.tgz#7df41a4bfe4c83d90147c1e30d70893f92a9d97c"
integrity sha512-rsTKRogc5OYGlvSHuq5QsmOsOzF6uDoMqpfh+Np8r23+QxDq+SUx90Rf8HyIKQVl7H6NswZEwfcykinbAeZ6UQ==
dependencies:
depd "2.0.0"
dns-prefetch-control "0.1.0"
@ -4026,8 +3944,8 @@ helmet@~3.15.1:
helmet-csp "2.7.1"
hide-powered-by "1.0.0"
hpkp "2.0.0"
hsts "2.1.0"
ienoopen "1.0.0"
hsts "2.2.0"
ienoopen "1.1.0"
nocache "2.0.0"
referrer-policy "1.1.0"
x-xss-protection "1.1.0"
@ -4064,10 +3982,12 @@ hpkp@2.0.0:
resolved "https://registry.yarnpkg.com/hpkp/-/hpkp-2.0.0.tgz#10e142264e76215a5d30c44ec43de64dee6d1672"
integrity sha1-EOFCJk52IVpdMMROxD3mTe5tFnI=
hsts@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/hsts/-/hsts-2.1.0.tgz#cbd6c918a2385fee1dd5680bfb2b3a194c0121cc"
integrity sha512-zXhh/DqgrTXJ7erTN6Fh5k/xjMhDGXCqdYN3wvxUvGUQvnxcFfUd8E+6vLg/nk3ss1TYMb+DhRl25fYABioTvA==
hsts@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/hsts/-/hsts-2.2.0.tgz#09119d42f7a8587035d027dda4522366fe75d964"
integrity sha512-ToaTnQ2TbJkochoVcdXYm4HOCliNozlviNsg+X2XQLQvZNI/kCHR9rZxVYpJB3UPcHz80PgxRyWQ7PdU1r+VBQ==
dependencies:
depd "2.0.0"
html-encoding-sniffer@^1.0.2:
version "1.0.2"
@ -4142,10 +4062,10 @@ ieee754@^1.1.4:
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b"
integrity sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==
ienoopen@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ienoopen/-/ienoopen-1.0.0.tgz#346a428f474aac8f50cf3784ea2d0f16f62bda6b"
integrity sha1-NGpCj0dKrI9QzzeE6i0PFvYr2ms=
ienoopen@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/ienoopen/-/ienoopen-1.1.0.tgz#411e5d530c982287dbdc3bb31e7a9c9e32630974"
integrity sha512-MFs36e/ca6ohEKtinTJ5VvAJ6oDRAYFdYXweUnGY9L9vcoqFOU4n2ZhmJ0C4z/cwGZ3YIQRSB3XZ1+ghZkY5NQ==
ignore-by-default@^1.0.1:
version "1.0.1"

View File

@ -5,7 +5,7 @@ import { getLangByName } from '../../support/helpers'
let lastPost = {}
const loginCredentials = {
let loginCredentials = {
email: 'peterpan@example.org',
password: '1234'
}
@ -244,3 +244,48 @@ Then(
cy.get('.error').should('contain', message)
}
)
Given('my user account has the following login credentials:', table => {
loginCredentials = table.hashes()[0]
cy.debug()
cy.factory().create('User', loginCredentials)
})
When('I fill the password form with:', table => {
table = table.rowsHash()
cy.get('input[id=oldPassword]')
.type(table['Your old password'])
.get('input[id=newPassword]')
.type(table['Your new passsword'])
.get('input[id=confirmPassword]')
.type(table['Confirm new password'])
})
When('submit the form', () => {
cy.get('form').submit()
})
Then('I cannot login anymore with password {string}', password => {
cy.reload()
const { email } = loginCredentials
cy.visit(`/login`)
cy.get('input[name=email]')
.trigger('focus')
.type(email)
cy.get('input[name=password]')
.trigger('focus')
.type(password)
cy.get('button[name=submit]')
.as('submitButton')
.click()
cy.get('.iziToast-wrapper').should('contain', 'Incorrect email address or password.')
})
Then('I can login successfully with password {string}', password => {
cy.reload()
cy.login({
...loginCredentials,
...{password}
})
cy.get('.iziToast-wrapper').should('contain', "You are logged in!")
})

View File

@ -0,0 +1,31 @@
Feature: Change password
As a user
I want to change my password in my settings
For security, e.g. if I exposed my password by accident
Login via email and password is a well-known authentication procedure and you
can assure to the server that you are who you claim to be. Either if you
exposed your password by acccident and you want to invalidate the exposed
password or just out of an good habit, you want to change your password.
Background:
Given my user account has the following login credentials:
| email | password |
| user@example.org | exposed |
And I am logged in
Scenario: Change my password
Given I am on the "settings" page
And I click on "Security"
When I fill the password form with:
| Your old password | exposed |
| Your new passsword | secure |
| Confirm new password | secure |
And submit the form
And I see a success message:
"""
Password successfully changed!
"""
And I log out through the menu in the top right corner
Then I cannot login anymore with password "exposed"
But I can login successfully with password "secure"

View File

@ -4,8 +4,6 @@
metadata:
name: nitro-backend
namespace: human-connection
labels:
commit: "COMMIT"
spec:
replicas: 1
minReadySeconds: 15
@ -20,6 +18,7 @@
template:
metadata:
labels:
human-connection.org/commit: COMMIT
human-connection.org/selector: deployment-human-connection-backend
name: "nitro-backend"
spec:

View File

@ -3,8 +3,6 @@ kind: Deployment
metadata:
name: nitro-web
namespace: human-connection
labels:
commit: "COMMIT"
spec:
replicas: 2
minReadySeconds: 15
@ -15,6 +13,7 @@ spec:
template:
metadata:
labels:
human-connection.org/commit: COMMIT
human-connection.org/selector: deployment-human-connection-web
name: nitro-web
spec:

View File

@ -1,3 +1,5 @@
metadata:
labels:
commit: <COMMIT>
spec:
template:
metadata:
labels:
human-connection.org/commit: <COMMIT>

View File

@ -0,0 +1,154 @@
import { mount, createLocalVue } from '@vue/test-utils'
import ChangePassword from './ChangePassword.vue'
import Vue from 'vue'
import Styleguide from '@human-connection/styleguide'
const localVue = createLocalVue()
localVue.use(Styleguide)
describe('ChangePassword.vue', () => {
let mocks
let wrapper
beforeEach(() => {
mocks = {
validate: jest.fn(),
$toast: {
error: jest.fn(),
success: jest.fn()
},
$t: jest.fn(),
$store: {
commit: jest.fn()
},
$apollo: {
mutate: jest
.fn()
.mockRejectedValue({ message: 'Ouch!' })
.mockResolvedValueOnce({ data: { changePassword: 'NEWTOKEN' } })
}
}
})
describe('mount', () => {
let wrapper
const Wrapper = () => {
return mount(ChangePassword, { mocks, localVue })
}
beforeEach(() => {
wrapper = Wrapper()
})
it('renders three input fields', () => {
expect(wrapper.findAll('input')).toHaveLength(3)
})
describe('validations', () => {
it('invalid', () => {
expect(wrapper.vm.disabled).toBe(true)
})
describe('old password and new password', () => {
describe('match', () => {
beforeEach(() => {
wrapper.find('input#oldPassword').setValue('some secret')
wrapper.find('input#newPassword').setValue('some secret')
})
it('invalid', () => {
expect(wrapper.vm.disabled).toBe(true)
})
it.skip('displays a warning', () => {
const calls = mocks.validate.mock.calls
const expected = [
['change-password.validations.old-and-new-password-match']
]
expect(calls).toEqual(expect.arrayContaining(expected))
})
})
})
describe('new password and confirmation', () => {
describe('mismatch', () => {
it.todo('invalid')
it.todo('displays a warning')
})
describe('match', () => {
describe('and old password mismatch', () => {
it.todo('valid')
})
describe('clicked', () => {
it.todo('sets loading')
})
})
})
})
describe('given valid input', () => {
beforeEach(() => {
wrapper.find('input#oldPassword').setValue('supersecret')
wrapper.find('input#newPassword').setValue('superdupersecret')
wrapper.find('input#confirmPassword').setValue('superdupersecret')
})
describe('submit form', () => {
beforeEach(() => {
wrapper.find('form').trigger('submit')
})
it('calls changePassword mutation', () => {
expect(mocks.$apollo.mutate).toHaveBeenCalled()
})
it('passes form data as variables', () => {
expect(mocks.$apollo.mutate.mock.calls[0][0]).toEqual(
expect.objectContaining({
variables: {
oldPassword: 'supersecret',
newPassword: 'superdupersecret',
confirmPassword: 'superdupersecret'
}
})
)
})
describe('mutation resolves', () => {
beforeEach(() => {
mocks.$apollo.mutate = jest.fn().mockResolvedValue()
wrapper = Wrapper()
})
it('calls auth/SET_TOKEN with response', () => {
expect(mocks.$store.commit).toHaveBeenCalledWith(
'auth/SET_TOKEN',
'NEWTOKEN'
)
})
it('displays success message', () => {
expect(mocks.$t).toHaveBeenCalledWith(
'settings.security.change-password.success'
)
expect(mocks.$toast.success).toHaveBeenCalled()
})
})
describe('mutation rejects', () => {
beforeEach(() => {
// second call will reject
wrapper.find('form').trigger('submit')
})
it('displays error message', () => {
expect(mocks.$toast.error).toHaveBeenCalledWith('Ouch!')
})
})
})
})
})
})

View File

@ -0,0 +1,83 @@
<template>
<ds-form
v-model="formData"
:schema="formSchema"
@submit="handleSubmit"
>
<template>
<ds-input
id="oldPassword"
model="oldPassword"
type="password"
label="Your old password"
/>
<ds-input
id="newPassword"
model="newPassword"
type="password"
label="Your new password"
/>
<ds-input
id="confirmPassword"
model="confirmPassword"
type="password"
label="Confirm new password"
/>
<ds-space margin-top="base">
<ds-button
:loading="loading"
primary
>
{{ $t('settings.security.change-password.button') }}
</ds-button>
</ds-space>
</template>
</ds-form>
</template>
<script>
import gql from 'graphql-tag'
export default {
name: 'ChangePassword',
data() {
return {
formData: {
oldPassword: '',
newPassword: '',
confirmPassword: ''
},
formSchema: {
oldPassword: { required: true },
newPassword: { required: true },
confirmPassword: { required: true }
},
loading: false,
disabled: true
}
},
methods: {
async handleSubmit(data) {
this.loading = true
const mutation = gql`
mutation($oldPassword: String!, $newPassword: String!) {
changePassword(oldPassword: $oldPassword, newPassword: $newPassword)
}
`
const variables = this.formData
try {
const { data } = await this.$apollo.mutate({ mutation, variables })
this.$store.commit('auth/SET_TOKEN', data.changePassword)
this.$toast.success(
this.$t('settings.security.change-password.success')
)
} catch (err) {
this.$toast.error(err.message)
} finally {
this.loading = false
}
}
}
}
</script>

View File

@ -31,7 +31,11 @@
"labelBio": "Über dich"
},
"security": {
"name": "Sicherheit"
"name": "Sicherheit",
"change-password": {
"button": "Passwort ändern",
"success": "Passwort erfolgreich geändert!"
}
},
"invites": {
"name": "Einladungen"

View File

@ -31,7 +31,11 @@
"labelBio": "About You"
},
"security": {
"name": "Security"
"name": "Security",
"change-password": {
"button": "Change password",
"success": "Password successfully changed!"
}
},
"invites": {
"name": "Invites"

View File

@ -121,12 +121,21 @@ module.exports = {
proxy: true
},
proxy: {
'/.well-known/webfinger': {
target: process.env.GRAPHQL_URI || 'http://localhost:4000',
toProxy: true // cloudflare needs that
},
'/activityPub': {
// make this configurable (nuxt-dotenv)
target: process.env.GRAPHQL_URI || 'http://localhost:4000',
pathRewrite: { '^/activityPub': '' },
toProxy: true // cloudflare needs that
},
'/api': {
// make this configurable (nuxt-dotenv)
target: process.env.GRAPHQL_URI || 'http://localhost:4000',
pathRewrite: { '^/api': '' },
toProxy: true, // cloudflare needs that
changeOrigin: true,
headers: {
Accept: 'application/json',
'X-UI-Request': true,

View File

@ -1,18 +1,16 @@
<template>
<ds-card :header="$t('settings.security.name')">
<hc-empty
icon="tasks"
message="Coming Soon…"
/>
<change-password />
</ds-card>
</template>
<script>
import HcEmpty from '~/components/Empty.vue'
import ChangePassword from '~/components/ChangePassword.vue'
export default {
components: {
HcEmpty
ChangePassword
}
}
</script>