Wahl-o-Mat für Personalwahlen https://kandimat.netzbegruenung.verdigado.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
Christoph Lienhard f7a56a0bed Rename candymat -> kandimat everywhere 7 months ago
..
sql Rename candymat -> kandimat everywhere 7 months ago
Dockerfile Fix docker compose setup and add redaktions app 3 years ago
README.md Rename candymat -> kandimat everywhere 7 months ago
backend.env Rename candymat -> kandimat everywhere 7 months ago
deep_reset_db.sh Rename candymat -> kandimat everywhere 7 months ago
security_considerations.md Introduce JWT Authentication 2 years ago

README.md

Kandimat Backend

The kandimat backend consists of a postgres database and a postgraphile instance. Postgraphile generates a Graphql backend based on the underlying postgres schema.

Introduction

Basic structure

There are three "data" tables:

  • category
  • question
  • answer

Questions can belong to categories. Answers belong to questions and candidates.

User management

There are four types of roles:

  • editor
  • candidate
  • user
  • anonymous

Editors handle questions and categories, candidates handle their specific answers and users are only important in setups where there is no public access to the data.

Authentication

Authentication is handled via jwt. The kandimat setup roughly follows the instructions in the postgraphile docu.

Manually test the backend

To test the backend manually an (enhanced) graphiql instance is started in dev mode. To access it navigate to http://localhost:5433/graphiql.

Authenticate (or how to pose as a member of one of the roles)

To pose as one of the three roles authenticate as

  • erika@musterman.de (editor)
  • max@mustermann.de (candidate)
  • happy@user.de (normal user)

The password is always "password".

Use following graphQL mutation to get the jwtToken of an editor:

mutation Authenticate {
  __typename
  authenticate(input: {email: "erika@mustermann.de", password: "password"}) {
    jwtToken
  }
}

The jwtToken in the response has to be added to the headers in the following way:

{
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiY2FuZHltYXRfY2FuZGlkYXRlIiwicGVyc29uX2lkIjoyLCJleHAiOjE1OTEwNDgzMzgsImlhdCI6MTU5MDg3NTUzNywiYXVkIjoicG9zdGdyYXBoaWxlIiwiaXNzIjoicG9zdGdyYXBoaWxlIn0.21Lu51_suJ5O2RU-UKN2Y6fvKw4SYe-oqx_QqlU0-GE"
}

Query the data tables

This is possible as member of any role, including no role (anonymous).

You can use the schema to get familiar with possible queries. As an example, here is a query which retrieves all questions including the category they belong to:

{
  allQuestions {
    nodes {
      categoryByCategoryId {
        title
        description
      }
      text
      description
    }
  }
}

Example response:

{
  "data": {
    "allQuestions": {
      "nodes": [
        {
          "categoryByCategoryId": {
            "title": "Umwelt",
            "description": "Themen rund um Naturschutz usw."
          },
          "text": "Was sagen Sie zur 10H Regel?",
          "description": "In Bayern dürfen Windräder nur ..."
        }
      ]
    }
  }
}

Creating new users

aka "register"

Only possible if no bearer token is set in the headers.

mutation Register {
  registerPerson(input: {firstName: "Ford", lastName: "Prefect", email: "ford@prefect.com", password: "password"}) {
    person {
      id
    }
  }
}

Creating questions

This is only possible as "editor". Use the following mutation:

mutation CreateQuestion($text: String!) {
  createQuestion(input: {question: {text: $text}}) {
    question {
      text
      id
    }
  }
}

with the variables

{
  "text": "Die Antwort auf die Frage nach dem Leben, dem Universum und dem ganzen Rest?"
}

Creating categories

This is only possible as "editor".

Mutation:

mutation CreateCategory($title: String!) {
  createCategory(input: {category: {title: $title}}) {
    category {
      title
      id
    }
  }
}

Variables

{
  "title": "Verkehr"
}
Creating answers

This is only possible as "candidate". Also the personId needs to be 2 (the id of Max Mustermann). It is impossible for a candidate to pose as a different candidate when answering a question.

Mutation:

mutation CreateAnswer($position: Int!, $questionId: Int!, $personId: Int!) {
  createAnswer(input: {answer: {position: $position, questionId: $questionId, personId: $personId}}) {
    answer {
      position
    }
  }
}

}

Variables

{
  "questionId": 1,
  "personId": 2,
  "position": 2
}

Also change the personId to see that the candidate can only answer for themself.

Updating, Deleting

Works the same as creating and has the same restrictions for the specific roles. The exact mutations can be inferred looking at the schema definitions.