Commit 4c1102fc authored by Jacob Priddy's avatar Jacob Priddy 👌
Browse files

It can authenticate a doorcode now! :D. OTA updates is next!

parent e39eddd2
Pipeline #6122 passed with stages
in 3 minutes and 50 seconds
......@@ -95,14 +95,10 @@ services:
networks:
- doorcode
esp32:
build:
context: ./src/embedded
dockerfile: ./esp32.Dockerfile
image: espressif/idf:latest
tty: true
container_name: esp32
volumes:
- ./src/embedded:/project
# This only works on linux
- /dev:/dev
ports:
- "2222:22"
devices:
- /dev/ttyUSB0:/dev/ttyUSB0
......@@ -3,4 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(door)
project(door-controller)
FROM espressif/idf:latest
RUN apt-get update \
&& apt-get install -y ssh \
rsync \
tar \
&& apt-get clean \\
&& pip install --no-cache-dir -r /opt/esp/idf/requirements.txt
RUN ( \
echo 'LogLevel DEBUG2'; \
echo 'PermitRootLogin yes'; \
echo 'PasswordAuthentication yes'; \
echo 'Subsystem sftp /usr/lib/openssh/sftp-server'; \
) > /etc/ssh/sshd_config_container \
&& mkdir /run/sshd
RUN groupmod -g 985 users && useradd -u 1000 -ms /bin/bash -g users builder \
&& yes password | passwd builder
CMD ["/usr/sbin/sshd", "-D", "-e", "-f", "/etc/ssh/sshd_config_container"]
set(COMPONENT_SRCS "wifi.c"
"main.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
# Embed the server root certificate into the final binary
#
# (If this was a component, we would set COMPONENT_EMBED_TXTFILES here.)
set(COMPONENT_EMBED_TXTFILES doorcode_root_cert.pem)
register_component()
idf_component_register(SRCS "wifi.c" "main.c" "http.c" "api.c"
INCLUDE_DIRS "."
EMBED_TXTFILES "doorcode_root_cert.pem")
#include <string.h>
#include "api.h"
#include "http.h"
#include "config.h"
#define ACCESS_PREFIX "/access/"
#define MAX_CODE_COUNT 500
bool door_api_verify_code(const char* code)
{
// ignore null terminators, having 3 or so more bytes won't hurt anything
#define MAX_DOOR_CODE_PATH_SIZE (MAX_CODE_COUNT + sizeof(API_BASE_PATH) + sizeof(ACCESS_PREFIX) + sizeof(API_TOKEN))
if (strlen(code) > MAX_CODE_COUNT)
return false;
char endpoint[MAX_DOOR_CODE_PATH_SIZE] = {0};
strcat(endpoint, API_BASE_PATH ACCESS_PREFIX);
strcat(endpoint, code);
strcat(endpoint, "?" API_TOKEN);
return door_http_get(endpoint, NULL, NULL) == 200;
#undef MAX_DOOR_CODE_PATH_SIZE
}
#ifndef DOOR_CONTROLLER_MAIN_API_H_
#define DOOR_CONTROLLER_MAIN_API_H_
#include <stdbool.h>
// Requires door_wifi to be initialized
/**
* @param code MUST be null terminated
* @return
*/
bool door_api_verify_code(const char* code);
#endif //DOOR_CONTROLLER_MAIN_API_H_
......@@ -3,7 +3,13 @@
#define DOOR_WIFI_SSID "ssid"
#define DOOR_WIFI_PASSWORD "password"
#define API_TOKEN "api token"
#define API_BASE_URL "https://doorcode:port/api"
#define API_TOKEN "api_token=door_1_api_token"
#define API_BASE_PATH "/api/door"
#define API_USE_SSL
//#define API_NO_NAME_VERIFY
#define API_HOST "192.168.2.1"
#define API_PORT 8080
#endif //DOOR_CONFIG_H
#include <esp_http_client.h>
#include <esp_log.h>
#include <esp_tls.h>
#include "http.h"
#include "config.h"
#include "wifi.h"
/**
* @brief Input buffer
*/
static char* _buffer = NULL;
/**
* @brief Input buffer size
*/
static int _buffer_bytes_written = 0;
static const char* TAG = "DOOR HTTP MODULE";
static esp_http_client_handle_t _client = NULL;
void door_http_free_buffer(void)
{
free(_buffer);
_buffer = NULL;
_buffer_bytes_written = 0;
}
/**
* This function assumes that only one request is placed at a time.
* This is fine currently as I am using blocking calls to get data,
* but if this is ever changed the way this function works will also
* need to be changed.
*
* @brief Event handler for the ESP http event handler
* @return
*/
static esp_err_t _http_event_handler(esp_http_client_event_t* evt)
{
switch (evt->event_id)
{
case HTTP_EVENT_ERROR:
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, data: %.*s", evt->data_len, (char*)evt->data);
// If user_data buffer is configured, copy the response into the buffer
if (!esp_http_client_is_chunked_response(evt->client)) {
if (evt->user_data)
{
memcpy(evt->user_data + _buffer_bytes_written, evt->data, evt->data_len);
}
else
{
if (!_buffer)
{
int content_length = esp_http_client_get_content_length(evt->client);
ESP_LOGI(TAG, "Allocating %d bytes for buffer", content_length);
_buffer = (char*)malloc(content_length);
_buffer_bytes_written = 0;
if (!_buffer && content_length != 0)
{
ESP_LOGE(TAG, "Failed to allocate memory for output buffer");
return ESP_FAIL;
}
}
}
} else {
// create new buffer with adequate size.
char* new_buffer = (char*)malloc(_buffer_bytes_written + evt->data_len);
if (!new_buffer) {
ESP_LOGE(TAG, "Failed to allocate memory for output buffer");
return ESP_FAIL;
}
memcpy(new_buffer, _buffer, _buffer_bytes_written);
free(_buffer);
_buffer = new_buffer;
}
memcpy(_buffer + _buffer_bytes_written, evt->data, evt->data_len);
_buffer_bytes_written += evt->data_len;
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
int mbedtls_err = 0;
esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL);
if (err != 0)
{
if (_buffer != NULL)
door_http_free_buffer();
ESP_LOGI(TAG, "Last esp error code: 0x%x", err);
ESP_LOGI(TAG, "Last mbedtls failure: 0x%x", mbedtls_err);
}
break;
default:
break;
}
return ESP_OK;
}
extern const char _door_root_cert_pem_start[] asm("_binary_doorcode_root_cert_pem_start");
static esp_http_client_config_t _http_base_config = {
.event_handler = _http_event_handler,
.host = API_HOST,
.port = API_PORT,
.path = API_BASE_PATH,
#ifdef API_USE_SSL
.transport_type = HTTP_TRANSPORT_OVER_SSL,
#ifdef API_NO_NAME_VERIFY
.skip_cert_common_name_check = false,
#endif
#else
.transport_type = HTTP_TRANSPORT_OVER_TCP,
#endif
};
int door_http_init(void)
{
if (_client)
return DOOR_HTTP_ERR_SUCCESS;
if (!door_wifi_is_connected()) {
ESP_LOGE(TAG, "Not connected to wifi.");
return DOOR_HTTP_ERR_FAIL;
}
_http_base_config.cert_pem = _door_root_cert_pem_start;
_client = esp_http_client_init(&_http_base_config);
if (!_client)
{
ESP_LOGE(TAG, "Failed to initialize the esp http client");
return DOOR_HTTP_ERR_FAIL;
}
return DOOR_HTTP_ERR_SUCCESS;
}
void door_http_destroy(void)
{
door_http_free_buffer();
esp_http_client_cleanup(_client);
_client = NULL;
}
int door_http_get(const char* path, char** buffer, int* buffer_len)
{
ESP_LOGI(TAG, "DOOR_HTTP_SET: sending GET request to: %s", path);
if (!door_wifi_is_connected() || !_client) {
ESP_LOGI(TAG, "Not connected to wifi or client in call to get");
return DOOR_HTTP_ERR_FAIL;
}
esp_http_client_set_url(_client, path);
esp_http_client_set_method(_client, HTTP_METHOD_GET);
esp_http_client_perform(_client);
if (buffer)
*buffer = _buffer;
if (buffer_len)
*buffer_len = _buffer_bytes_written;
ESP_LOGI(TAG, "Total data retrieved from response: %.*s", _buffer_bytes_written, _buffer);
int status_code = esp_http_client_get_status_code(_client);
ESP_LOGI(TAG, "Status code from get call: %d", status_code);
return status_code;
}
#ifndef DOOR_CONTROLLER_MAIN_HTTP_H_
#define DOOR_CONTROLLER_MAIN_HTTP_H_
#define DOOR_HTTP_ERR_SUCCESS 0
#define DOOR_HTTP_ERR_FAIL 1
/**
* @brief Frees the buffer for the next request. Must be called after every request
*/
void door_http_free_buffer(void);
/**
* @brief Initializes the door http system
* @return < 0 on failure
*/
int door_http_init(void);
/**
* @brief De-initalizes the http system. If it is to be used again, door_http_init must be called again
*/
void door_http_destroy(void);
/**
* @brief Performs a get request to the door code server. door_http_free_buffer must be called
* when the callee is done with the data before the next request
* @param[in] path the path of the url to make the request to
* @param[out] buffer the buffer data
* @param[out] buffer_len the number of bytes written
* @return
* - status code on success
* - < 0 on fail
*/
int door_http_get(const char* path, char** buffer, int* buffer_len);
#endif //DOOR_CONTROLLER_MAIN_HTTP_H_
......@@ -7,82 +7,35 @@
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <esp_sntp.h>
#include <esp_http_client.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "config.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include "wifi.h"
#include "http.h"
#include "api.h"
static const char* TAG = "HTTPS_MODULE";
static const char* TAG = "MAIN_MODULE";
extern const char doorcode_root_cert_pem_start[] asm("_binary_doorcode_root_cert_pem_start");
//extern const char doorcode_root_cert_pem_end[] asm("_binary_doorcode_root_cert_pem_end");
esp_err_t _http_event_handler(esp_http_client_event_t* evt)
{
switch (evt->event_id)
{
case HTTP_EVENT_ERROR:
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
ESP_LOGI(TAG, "%.*s", evt->data_len, (char*)evt->data);
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
break;
}
return ESP_OK;
}
static void http_task(void* pvParameters)
static void check_code_task(void* pvParameters)
{
(void)pvParameters;
door_wifi_wait_connected();
door_http_init();
ESP_LOGI(TAG, "Connected to AP, begin connecting to DoorCode API");
esp_http_client_config_t config = {
.url = API_BASE_URL "/door?api_token=" API_TOKEN,
.event_handler = _http_event_handler,
.cert_pem = doorcode_root_cert_pem_start,
.skip_cert_common_name_check = true,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK)
bool allowed = door_api_verify_code("123866");
if (allowed)
{
ESP_LOGI(TAG, "HTTPS Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
ESP_LOGI(TAG, "We are allowed in the door!");
}
else
{
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
ESP_LOGI(TAG, "REJECTED");
}
esp_http_client_cleanup(client);
door_http_destroy();
door_wifi_destroy();
ESP_LOGI(TAG, "Finished Request");
vTaskDelete(NULL);
}
......@@ -100,5 +53,5 @@ void app_main()
door_wifi_initialize();
xTaskCreate(&http_task, "http_task", 8192, NULL, 5, NULL);
xTaskCreate(&check_code_task, "http_task", 8192, NULL, 5, NULL);
}
#include "wifi.h"
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_event.h"
#include "esp_log.h"
#include "freertos/event_groups.h"
#include "config.h"
static const char* TAG = "WIFI_MODULE";
#include "nvs_flash.h"
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
#include "config.h"
#include "wifi.h"
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
to the AP with an IP? */
const int CONNECTED_BIT = BIT0;
/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;
static const char* TAG = "wifi module";
static const unsigned WIFI_CONNECTED_BIT = BIT0;
static esp_event_handler_instance_t instance_any_id;
static esp_event_handler_instance_t instance_got_ip;
static esp_err_t event_handler(void* ctx, system_event_t* event)
static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
(void)ctx;
switch (event->event_id)
(void)arg;
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
{
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently
auto-reassociate. */
}
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_STOP)
{
xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
ESP_LOGI(TAG, "WIFI Module is stopping...");
}
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
{
xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
ESP_LOGI(TAG, "Lost connection to the AP, retrying connection..");
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
ESP_LOGI(TAG, "Lost connection to Wireless AP, Reconnecting...\n");
break;
default:
break;
}
return ESP_OK;
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
{
ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
bool door_wifi_is_connected(void)
{
return xEventGroupGetBits(s_wifi_event_group) & WIFI_CONNECTED_BIT;
}
void door_wifi_destroy(void)
{
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_got_ip));
vEventGroupDelete(s_wifi_event_group);
}
void door_wifi_initialize(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL))
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT()
ESP_ERROR_CHECK(esp_wifi_init(&cfg))
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM))
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = DOOR_WIFI_SSID,
.password = DOOR_WIFI_PASSWORD,
.password = DOOR_WIFI_PASSWORD
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA))
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config))
ESP_ERROR_CHECK(esp_wifi_start())
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "WiFi initialization finished successfully.");
}
void door_wifi_wait_connected(void)
{
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);
EventBits_t bits;
do
{
bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
if (bits & WIFI_CONNECTED_BIT)
{
break;
}
} while (!(bits & WIFI_CONNECTED_BIT));
}
#ifndef DOOR_WIFI_H
#define DOOR_WIFI_H
#ifndef DOOR__WIFI_H_
#define DOOR__WIFI_H_
void door_wifi_initialize(void);
void door_wifi_destroy(void);
bool door_wifi_is_connected(void);
void door_wifi_wait_connected(void);
#endif //DOOR_WIFI_H
#endif //DOOR__WIFI_H_
......@@ -17,6 +17,7 @@ class LocalDoorUserRepository extends InMemoryDoorUserRepository
GroupUserRepository $groupUser,
DoorGroupRepository $doorGroup
) {
// Automatically add all users to all doors, and all doors to all users
foreach ($users->all() as $user) {
try {
foreach ($groupUser->getGroupsForUser($user->getId()) as $group) {
......