Made message signing in Cryptography use SHA512 as the message content for... #1
226 changed files with 24593 additions and 2539 deletions
36
.env
Normal file
36
.env
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# Socialbox Configuration
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
SB_MODE=automated
|
||||||
|
SB_DOMAIN=localhost
|
||||||
|
SB_INSTANCE_NAME=Socialbox
|
||||||
|
SB_RPC_ENDPOINT=http://127.0.0.0:8085/
|
||||||
|
SB_LOGGING_CONSOLE_ENABLED=true
|
||||||
|
SB_LOGGING_CONSOLE_LEVEL=info
|
||||||
|
SB_SECURITY_DISPLAY_INTERNAL_EXCEPTIONS=true
|
||||||
|
SB_CRYPTO_KEYPAIR_EXPIRES=<duration>
|
||||||
|
SB_CRYPTO_ENCRYPTION_KEYS_ALGORITHM=xchacha20
|
||||||
|
SB_CRYPTO_TRANSPORT_ENCRYPTION_ALGORITHM=chacha20
|
||||||
|
SB_CACHE_ENABLED=true
|
||||||
|
SB_CACHE_PORT=6379
|
||||||
|
SB_CACHE_USERNAME=root
|
||||||
|
SB_CACHE_PASSWORD=root
|
||||||
|
SB_CACHE_DATABASE=0
|
||||||
|
|
||||||
|
# MariaDB Configuration
|
||||||
|
MYSQL_ROOT_PASSWORD=sb_root
|
||||||
|
MYSQL_DATABASE=socialbox
|
||||||
|
MYSQL_USER=socialbox
|
||||||
|
MYSQL_PASSWORD=socialbox
|
||||||
|
|
||||||
|
# Redis Configuration
|
||||||
|
REDIS_PASSWORD=root
|
||||||
|
|
||||||
|
# Test Configuration, can be ignored. Used for docker-compose-test.yml
|
||||||
|
SB_COFFEE_NAME=coffee
|
||||||
|
SB_COFFEE_DOMAIN=coffee.com
|
||||||
|
SB_COFFEE_RPC_ENDPOINT=http://coffee_socialbox:8085/
|
||||||
|
SB_INSTANCE_DNS_MOCK_COFFEE="coffee.com v=socialbox;sb-rpc=http://coffee_socialbox:8085/;sb-key=sig:g59Cf8j1wmQmRg1MkveYbpdiZ-1-_hFU9eRRJmQAwmc;sb-exp=0"
|
||||||
|
|
||||||
|
SB_TEAPOT_DOMAIN=teapot.com
|
||||||
|
SB_TEAPOT_RPC_ENDPOINT=http://teapot_socialbox:8085/
|
||||||
|
SB_INSTANCE_DNS_MOCK_TEAPOT="teapot.com v=socialbox;sb-rpc=http://teapot_socialbox:8085/;sb-key=sig:MDXUuripAo_IAv-EZTEoFhpIdhsXxfMLNunSnQzxYiY;sb-exp=0"
|
302
.github/workflows/ncc_workflow.yml
vendored
302
.github/workflows/ncc_workflow.yml
vendored
|
@ -9,7 +9,7 @@ on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: php:8.3
|
image: php:8.3
|
||||||
|
@ -51,14 +51,163 @@ jobs:
|
||||||
|
|
||||||
- name: Build project
|
- name: Build project
|
||||||
run: |
|
run: |
|
||||||
ncc build --config release --log-level debug
|
ncc build --config release --build-source --log-level debug
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Socialbox_build
|
name: release
|
||||||
path: build/release/net.nosial.socialbox.ncc
|
path: build/release/net.nosial.socialbox.ncc
|
||||||
|
debug:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: php:8.3
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
apt update -yqq
|
||||||
|
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
|
||||||
|
|
||||||
|
- name: Install phive
|
||||||
|
run: |
|
||||||
|
wget -O phive.phar https://phar.io/releases/phive.phar
|
||||||
|
wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
|
||||||
|
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
|
||||||
|
gpg --verify phive.phar.asc phive.phar
|
||||||
|
chmod +x phive.phar
|
||||||
|
mv phive.phar /usr/local/bin/phive
|
||||||
|
|
||||||
|
- name: Install phab
|
||||||
|
run: |
|
||||||
|
phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
|
||||||
|
|
||||||
|
- name: Install latest version of NCC
|
||||||
|
run: |
|
||||||
|
git clone https://git.n64.cc/nosial/ncc.git
|
||||||
|
cd ncc
|
||||||
|
make redist
|
||||||
|
NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1)
|
||||||
|
if [ -z "$NCC_DIR" ]; then
|
||||||
|
echo "NCC build directory not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
php "$NCC_DIR/INSTALL" --auto
|
||||||
|
cd .. && rm -rf ncc
|
||||||
|
|
||||||
|
- name: Build project
|
||||||
|
run: |
|
||||||
|
ncc build --config debug --build-source --log-level debug
|
||||||
|
|
||||||
|
- name: Upload build artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: debug
|
||||||
|
path: build/debug/net.nosial.socialbox.ncc
|
||||||
|
release_executable:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: php:8.3
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
apt update -yqq
|
||||||
|
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
|
||||||
|
|
||||||
|
- name: Install phive
|
||||||
|
run: |
|
||||||
|
wget -O phive.phar https://phar.io/releases/phive.phar
|
||||||
|
wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
|
||||||
|
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
|
||||||
|
gpg --verify phive.phar.asc phive.phar
|
||||||
|
chmod +x phive.phar
|
||||||
|
mv phive.phar /usr/local/bin/phive
|
||||||
|
|
||||||
|
- name: Install phab
|
||||||
|
run: |
|
||||||
|
phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
|
||||||
|
|
||||||
|
- name: Install latest version of NCC
|
||||||
|
run: |
|
||||||
|
git clone https://git.n64.cc/nosial/ncc.git
|
||||||
|
cd ncc
|
||||||
|
make redist
|
||||||
|
NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1)
|
||||||
|
if [ -z "$NCC_DIR" ]; then
|
||||||
|
echo "NCC build directory not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
php "$NCC_DIR/INSTALL" --auto
|
||||||
|
cd .. && rm -rf ncc
|
||||||
|
|
||||||
|
- name: Build project
|
||||||
|
run: |
|
||||||
|
ncc build --config release_executable --build-source --log-level debug
|
||||||
|
|
||||||
|
- name: Upload build artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release_executable
|
||||||
|
path: build/release/Socialbox
|
||||||
|
debug_executable:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: php:8.3
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
apt update -yqq
|
||||||
|
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
|
||||||
|
|
||||||
|
- name: Install phive
|
||||||
|
run: |
|
||||||
|
wget -O phive.phar https://phar.io/releases/phive.phar
|
||||||
|
wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
|
||||||
|
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
|
||||||
|
gpg --verify phive.phar.asc phive.phar
|
||||||
|
chmod +x phive.phar
|
||||||
|
mv phive.phar /usr/local/bin/phive
|
||||||
|
|
||||||
|
- name: Install phab
|
||||||
|
run: |
|
||||||
|
phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
|
||||||
|
|
||||||
|
- name: Install latest version of NCC
|
||||||
|
run: |
|
||||||
|
git clone https://git.n64.cc/nosial/ncc.git
|
||||||
|
cd ncc
|
||||||
|
make redist
|
||||||
|
NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1)
|
||||||
|
if [ -z "$NCC_DIR" ]; then
|
||||||
|
echo "NCC build directory not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
php "$NCC_DIR/INSTALL" --auto
|
||||||
|
cd .. && rm -rf ncc
|
||||||
|
|
||||||
|
- name: Build project
|
||||||
|
run: |
|
||||||
|
ncc build --config debug_executable --build-source --log-level debug
|
||||||
|
|
||||||
|
- name: Upload build artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: debug_executable
|
||||||
|
path: build/debug/Socialbox
|
||||||
|
|
||||||
|
|
||||||
|
# Checking for phpunit.xml
|
||||||
check-phpunit:
|
check-phpunit:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
|
@ -74,9 +223,59 @@ jobs:
|
||||||
else
|
else
|
||||||
echo "phpunit-exists=false" >> $GITHUB_OUTPUT
|
echo "phpunit-exists=false" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
# Checking for phpdoc.dist.xml
|
||||||
|
check-phpdoc:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
phpdoc-exists: ${{ steps.check.outputs.phpdoc-exists }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Check for phpdoc.dist.xml
|
||||||
|
id: check
|
||||||
|
run: |
|
||||||
|
if [ -f phpdoc.dist.xml ]; then
|
||||||
|
echo "phpdoc-exists=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "phpdoc-exists=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
generate-phpdoc:
|
||||||
|
needs: [release, check-phpdoc]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: php:8.3
|
||||||
|
if: needs.check-phpdoc.outputs.phpdoc-exists == 'true'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
apt update -yqq
|
||||||
|
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
|
||||||
|
|
||||||
|
- name: Download PHPDocumentor
|
||||||
|
run: |
|
||||||
|
wget https://phpdoc.org/phpDocumentor.phar
|
||||||
|
chmod +x phpDocumentor.phar
|
||||||
|
|
||||||
|
- name: Generate PHPDoc
|
||||||
|
run: |
|
||||||
|
php phpDocumentor.phar -d src -t docs
|
||||||
|
|
||||||
|
- name: Archive PHPDoc
|
||||||
|
run: |
|
||||||
|
zip -r docs.zip docs
|
||||||
|
|
||||||
|
- name: Upload PHPDoc
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: documentation
|
||||||
|
path: docs.zip
|
||||||
|
|
||||||
test:
|
test:
|
||||||
needs: [build, check-phpunit]
|
needs: [release, debug, release_executable, debug_executable, check-phpunit]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: php:8.3
|
image: php:8.3
|
||||||
|
@ -89,8 +288,8 @@ jobs:
|
||||||
- name: Download build artifacts
|
- name: Download build artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Socialbox_build
|
name: release
|
||||||
path: Socialbox_build # Adjust this to download the artifact directly under 'Socialbox_build'
|
path: release
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
@ -98,7 +297,7 @@ jobs:
|
||||||
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
|
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
|
||||||
curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
|
curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
|
||||||
chmod +x /usr/local/bin/install-php-extensions
|
chmod +x /usr/local/bin/install-php-extensions
|
||||||
install-php-extensions zip pdo_mysql mysqli mcrypt
|
install-php-extensions zip
|
||||||
|
|
||||||
- name: Install phive
|
- name: Install phive
|
||||||
run: |
|
run: |
|
||||||
|
@ -128,15 +327,22 @@ jobs:
|
||||||
|
|
||||||
- name: Install NCC packages
|
- name: Install NCC packages
|
||||||
run: |
|
run: |
|
||||||
ncc package install --package="Socialbox_build/net.nosial.socialbox.ncc" --build-source --reinstall -y --log-level debug
|
ncc package install --package="release/net.nosial.socialbox.ncc" --build-source --reinstall -y --log-level debug
|
||||||
|
|
||||||
- name: Run PHPUnit tests
|
- name: Run PHPUnit tests
|
||||||
run: |
|
run: |
|
||||||
wget https://phar.phpunit.de/phpunit-11.3.phar
|
wget https://phar.phpunit.de/phpunit-11.3.phar
|
||||||
php phpunit-11.3.phar --configuration phpunit.xml
|
php phpunit-11.3.phar --configuration phpunit.xml --log-junit reports/junit.xml --log-teamcity reports/teamcity --testdox-html reports/testdox.html --testdox-text reports/testdox.txt
|
||||||
|
|
||||||
release:
|
- name: Upload test reports
|
||||||
needs: [build, test]
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: reports
|
||||||
|
path: reports
|
||||||
|
|
||||||
|
|
||||||
|
release-documentation:
|
||||||
|
needs: generate-phpdoc
|
||||||
permissions: write-all
|
permissions: write-all
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
|
@ -147,16 +353,78 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Download build artifacts
|
- name: Download documentation artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Socialbox_build
|
name: documentation
|
||||||
path: Socialbox_build
|
path: documentation
|
||||||
|
|
||||||
- name: Upload to GitHub Release
|
- name: Upload documentation artifact
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
Socialbox_build/net.nosial.socialbox.ncc
|
documentation/*
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
|
release-artifacts:
|
||||||
|
needs: [release, debug, release_executable, debug_executable]
|
||||||
|
permissions: write-all
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: php:8.3
|
||||||
|
if: github.event_name == 'release'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download release artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release
|
||||||
|
path: release
|
||||||
|
- name: Upload release artifact to release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
release/*
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Download debug artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: debug
|
||||||
|
path: debug
|
||||||
|
- name: Upload debug artifact to release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
debug/*
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Download release_executable artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: release_executable
|
||||||
|
path: release_executable
|
||||||
|
- name: Upload release_executable artifact to release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
release_executable/*
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Download debug_executable artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: debug_executable
|
||||||
|
path: debug_executable
|
||||||
|
- name: Upload debug_executable artifact to release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
debug_executable/*
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,3 +1,11 @@
|
||||||
/build
|
/build
|
||||||
|
/.vscode
|
||||||
/.idea/gbrowser_project.xml
|
/.idea/gbrowser_project.xml
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
|
/socialbox
|
||||||
|
/bob_socialbox
|
||||||
|
/alice_socialbox
|
||||||
|
/coffee_socialbox/data/
|
||||||
|
/coffee_socialbox/logs/
|
||||||
|
/teapot_socialbox/data/
|
||||||
|
/teapot_socialbox/logs/
|
||||||
|
|
162
.gitlab-ci.yml
162
.gitlab-ci.yml
|
@ -1,10 +1,21 @@
|
||||||
image: php:8.1
|
# Global config
|
||||||
|
default:
|
||||||
|
image: php:8.3
|
||||||
|
|
||||||
|
variables:
|
||||||
|
GIT_STRATEGY: clone
|
||||||
|
|
||||||
|
workflow:
|
||||||
|
rules:
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "push"
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "web"
|
||||||
|
- if: $CI_COMMIT_TAG
|
||||||
|
|
||||||
|
# Reusable template for installing common dependencies
|
||||||
|
.setup_template: &setup_definition
|
||||||
before_script:
|
before_script:
|
||||||
# Install some stuff that the image doesn't come with
|
|
||||||
- apt update -yqq
|
- apt update -yqq
|
||||||
- apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
|
- apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
|
||||||
|
|
||||||
# Install phive
|
# Install phive
|
||||||
- wget -O phive.phar https://phar.io/releases/phive.phar
|
- wget -O phive.phar https://phar.io/releases/phive.phar
|
||||||
- wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
|
- wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
|
||||||
|
@ -12,35 +23,148 @@ before_script:
|
||||||
- gpg --verify phive.phar.asc phive.phar
|
- gpg --verify phive.phar.asc phive.phar
|
||||||
- chmod +x phive.phar
|
- chmod +x phive.phar
|
||||||
- mv phive.phar /usr/local/bin/phive
|
- mv phive.phar /usr/local/bin/phive
|
||||||
|
|
||||||
# Install phab
|
# Install phab
|
||||||
- phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
|
- phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
|
||||||
|
# Install NCC
|
||||||
# Install the latest version of ncc (Nosial Code Compiler)
|
|
||||||
- git clone https://git.n64.cc/nosial/ncc.git
|
- git clone https://git.n64.cc/nosial/ncc.git
|
||||||
- cd ncc
|
- cd ncc
|
||||||
- make redist
|
- make redist
|
||||||
- php build/src/INSTALL --auto --install-composer
|
- NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1)
|
||||||
- cd .. && rm -rf ncc
|
- if [ -z "$NCC_DIR" ]; then echo "NCC build directory not found"; exit 1; fi
|
||||||
|
- php "$NCC_DIR/INSTALL" --auto
|
||||||
|
- cd ..
|
||||||
|
- rm -rf ncc
|
||||||
|
|
||||||
build:
|
# Build jobs
|
||||||
|
release:
|
||||||
|
<<: *setup_definition
|
||||||
stage: build
|
stage: build
|
||||||
script:
|
script:
|
||||||
- ncc build --config release --log-level debug
|
- ncc build --config release --build-source --log-level debug
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build/
|
- build/release/net.nosial.socialbox.ncc
|
||||||
rules:
|
expire_in: 1 week
|
||||||
- if: $CI_COMMIT_BRANCH
|
|
||||||
|
|
||||||
release:
|
debug:
|
||||||
stage: deploy
|
<<: *setup_definition
|
||||||
|
stage: build
|
||||||
script:
|
script:
|
||||||
- ncc build --config release --log-level debug
|
- ncc build --config debug --build-source --log-level debug
|
||||||
- >
|
|
||||||
curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/release/net.nosial.socialbox.ncc "$CI_API_V4_URL/projects/$CI_PROJECT_ID/packages/generic/net.nosial.socialbox/$CI_COMMIT_REF_NAME/net.nosial.socialbox.ncc"
|
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build/
|
- build/debug/net.nosial.socialbox.ncc
|
||||||
|
expire_in: 1 week
|
||||||
|
|
||||||
|
release_executable:
|
||||||
|
<<: *setup_definition
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- ncc build --config release_executable --build-source --log-level debug
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- build/release/Socialbox
|
||||||
|
expire_in: 1 week
|
||||||
|
|
||||||
|
debug_executable:
|
||||||
|
<<: *setup_definition
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- ncc build --config debug_executable --build-source --log-level debug
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- build/debug/Socialbox
|
||||||
|
expire_in: 1 week
|
||||||
|
|
||||||
|
# Check for configuration files
|
||||||
|
check_configs:
|
||||||
|
stage: .pre
|
||||||
|
script:
|
||||||
|
- |
|
||||||
|
if [ -f phpunit.xml ]; then
|
||||||
|
echo "PHPUNIT_EXISTS=true" >> build.env
|
||||||
|
else
|
||||||
|
echo "PHPUNIT_EXISTS=false" >> build.env
|
||||||
|
fi
|
||||||
|
if [ -f phpdoc.dist.xml ]; then
|
||||||
|
echo "PHPDOC_EXISTS=true" >> build.env
|
||||||
|
else
|
||||||
|
echo "PHPDOC_EXISTS=false" >> build.env
|
||||||
|
fi
|
||||||
|
artifacts:
|
||||||
|
reports:
|
||||||
|
dotenv: build.env
|
||||||
|
|
||||||
|
# Documentation generation
|
||||||
|
generate-phpdoc:
|
||||||
|
<<: *setup_definition
|
||||||
|
stage: build
|
||||||
|
needs: ["check_configs"]
|
||||||
|
rules:
|
||||||
|
- if: $PHPDOC_EXISTS == "true"
|
||||||
|
script:
|
||||||
|
- wget https://phpdoc.org/phpDocumentor.phar
|
||||||
|
- chmod +x phpDocumentor.phar
|
||||||
|
- php phpDocumentor.phar -d src -t docs
|
||||||
|
- zip -r docs.zip docs
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- docs.zip
|
||||||
|
expire_in: 1 week
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
test:
|
||||||
|
<<: *setup_definition
|
||||||
|
stage: test
|
||||||
|
needs:
|
||||||
|
- job: release
|
||||||
|
- job: check_configs
|
||||||
|
rules:
|
||||||
|
- if: $PHPUNIT_EXISTS == "true"
|
||||||
|
script:
|
||||||
|
- curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
|
||||||
|
- chmod +x /usr/local/bin/install-php-extensions
|
||||||
|
- install-php-extensions zip
|
||||||
|
- ncc package install --package="build/release/net.nosial.socialbox.ncc" --build-source --reinstall -y --log-level debug
|
||||||
|
- wget https://phar.phpunit.de/phpunit-11.3.phar
|
||||||
|
- php phpunit-11.3.phar --configuration phpunit.xml --log-junit reports/junit.xml --log-teamcity reports/teamcity --testdox-html reports/testdox.html --testdox-text reports/testdox.txt
|
||||||
|
artifacts:
|
||||||
|
reports:
|
||||||
|
junit: reports/junit.xml
|
||||||
|
paths:
|
||||||
|
- reports/
|
||||||
|
expire_in: 1 week
|
||||||
|
|
||||||
|
# Release jobs
|
||||||
|
release_upload:
|
||||||
|
stage: deploy
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_TAG
|
- if: $CI_COMMIT_TAG
|
||||||
|
needs:
|
||||||
|
- job: release
|
||||||
|
- job: debug
|
||||||
|
- job: release_executable
|
||||||
|
- job: debug_executable
|
||||||
|
- job: generate-phpdoc
|
||||||
|
optional: true
|
||||||
|
script:
|
||||||
|
- |
|
||||||
|
if [ -f "docs.zip" ]; then
|
||||||
|
echo "Releasing documentation..."
|
||||||
|
curl --request POST \
|
||||||
|
--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
|
||||||
|
--form "file=@docs.zip" \
|
||||||
|
"${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/uploads"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Upload release artifacts
|
||||||
|
for artifact in build/release/net.nosial.socialbox.ncc build/debug/net.nosial.socialbox.ncc build/release/Socialbox build/debug/Socialbox; do
|
||||||
|
if [ -f "$artifact" ]; then
|
||||||
|
echo "Uploading $artifact..."
|
||||||
|
curl --request POST \
|
||||||
|
--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
|
||||||
|
--form "file=@${artifact}" \
|
||||||
|
"${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/uploads"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
environment: production
|
20
.idea/dataSources.xml
generated
20
.idea/dataSources.xml
generated
|
@ -8,5 +8,25 @@
|
||||||
<jdbc-url>jdbc:mariadb://127.0.0.1:3306/socialbox</jdbc-url>
|
<jdbc-url>jdbc:mariadb://127.0.0.1:3306/socialbox</jdbc-url>
|
||||||
<working-dir>$ProjectFileDir$</working-dir>
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
</data-source>
|
</data-source>
|
||||||
|
<data-source source="LOCAL" name="Coffee Database" uuid="d114bae4-44f5-49c3-849a-149881b09342">
|
||||||
|
<driver-ref>mariadb</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:mariadb://127.0.0.1:3308/socialbox</jdbc-url>
|
||||||
|
<jdbc-additional-properties>
|
||||||
|
<property name="database.introspection.mysql.dbe5060" value="true" />
|
||||||
|
</jdbc-additional-properties>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
<data-source source="LOCAL" name="Teapot Database" uuid="64dd3c41-0f4f-4f65-b186-d42813467391">
|
||||||
|
<driver-ref>mariadb</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:mariadb://127.0.0.1:3307/socialbox</jdbc-url>
|
||||||
|
<jdbc-additional-properties>
|
||||||
|
<property name="database.introspection.mysql.dbe5060" value="true" />
|
||||||
|
</jdbc-additional-properties>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
14
.idea/php-test-framework.xml
generated
Normal file
14
.idea/php-test-framework.xml
generated
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="PhpTestFrameworkVersionCache">
|
||||||
|
<tools_cache>
|
||||||
|
<tool tool_name="PHPUnit">
|
||||||
|
<cache>
|
||||||
|
<versions>
|
||||||
|
<info id="Local/home/netkas/phpunit.phar" version="11.3.5" />
|
||||||
|
</versions>
|
||||||
|
</cache>
|
||||||
|
</tool>
|
||||||
|
</tools_cache>
|
||||||
|
</component>
|
||||||
|
</project>
|
42
.idea/php.xml
generated
42
.idea/php.xml
generated
|
@ -10,18 +10,27 @@
|
||||||
<option name="highlightLevel" value="WARNING" />
|
<option name="highlightLevel" value="WARNING" />
|
||||||
<option name="transferred" value="true" />
|
<option name="transferred" value="true" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="PhpCodeSniffer">
|
||||||
|
<phpcs_settings>
|
||||||
|
<phpcs_by_interpreter asDefaultInterpreter="true" interpreter_id="347c1ee9-eeae-4334-9e17-b7e47c2bca71" timeout="30000" />
|
||||||
|
</phpcs_settings>
|
||||||
|
</component>
|
||||||
<component name="PhpIncludePathManager">
|
<component name="PhpIncludePathManager">
|
||||||
<include_path>
|
<include_path>
|
||||||
<path value="$USER_HOME$/phar" />
|
<path value="/var/ncc/packages/net.nosial.loglib2=1.0.0" />
|
||||||
<path value="/var/ncc/packages/com.symfony.uid=v7.1.5" />
|
<path value="/var/ncc/packages/net.nosial.loglib=2.0.0" />
|
||||||
<path value="/var/ncc/packages/com.symfony.filesystem=v7.1.2" />
|
<path value="/var/ncc/packages/com.symfony.finder=2.0.7" />
|
||||||
|
<path value="/var/ncc/packages/com.gregwar.captcha=v1.2.1" />
|
||||||
|
<path value="/var/ncc/packages/com.symfony.filesystem=v7.1.5" />
|
||||||
<path value="/var/ncc/packages/com.symfony.polyfill_ctype=v1.31.0" />
|
<path value="/var/ncc/packages/com.symfony.polyfill_ctype=v1.31.0" />
|
||||||
<path value="/var/ncc/packages/com.symfony.polyfill_mbstring=v1.31.0" />
|
<path value="/var/ncc/packages/com.symfony.polyfill_mbstring=v1.31.0" />
|
||||||
<path value="/var/ncc/packages/com.symfony.process=v7.1.3" />
|
<path value="/var/ncc/packages/com.symfony.polyfill_uuid=v1.31.0" />
|
||||||
<path value="/var/ncc/packages/com.symfony.yaml=v7.1.4" />
|
<path value="/var/ncc/packages/com.symfony.process=v7.1.5" />
|
||||||
|
<path value="/var/ncc/packages/com.symfony.uid=v7.1.5" />
|
||||||
|
<path value="/var/ncc/packages/com.symfony.yaml=v7.1.5" />
|
||||||
<path value="/var/ncc/packages/net.nosial.configlib=1.1.0" />
|
<path value="/var/ncc/packages/net.nosial.configlib=1.1.0" />
|
||||||
<path value="/var/ncc/packages/net.nosial.loglib=1.1.0" />
|
<path value="/var/ncc/packages/net.nosial.optslib=1.1.2" />
|
||||||
<path value="/var/ncc/packages/net.nosial.optslib=1.1.0" />
|
<path value="$USER_HOME$/phar" />
|
||||||
<path value="/usr/share/ncc" />
|
<path value="/usr/share/ncc" />
|
||||||
</include_path>
|
</include_path>
|
||||||
</component>
|
</component>
|
||||||
|
@ -50,7 +59,7 @@
|
||||||
<extension name="enchant" enabled="false" />
|
<extension name="enchant" enabled="false" />
|
||||||
<extension name="fann" enabled="false" />
|
<extension name="fann" enabled="false" />
|
||||||
<extension name="ffmpeg" enabled="false" />
|
<extension name="ffmpeg" enabled="false" />
|
||||||
<extension name="gd" enabled="false" />
|
<extension name="frankenphp" enabled="false" />
|
||||||
<extension name="gearman" enabled="false" />
|
<extension name="gearman" enabled="false" />
|
||||||
<extension name="geoip" enabled="false" />
|
<extension name="geoip" enabled="false" />
|
||||||
<extension name="geos" enabled="false" />
|
<extension name="geos" enabled="false" />
|
||||||
|
@ -69,7 +78,7 @@
|
||||||
<extension name="libevent" enabled="false" />
|
<extension name="libevent" enabled="false" />
|
||||||
<extension name="libsodium" enabled="false" />
|
<extension name="libsodium" enabled="false" />
|
||||||
<extension name="mailparse" enabled="false" />
|
<extension name="mailparse" enabled="false" />
|
||||||
<extension name="memcache" enabled="false" />
|
<extension name="memcached" enabled="false" />
|
||||||
<extension name="ming" enabled="false" />
|
<extension name="ming" enabled="false" />
|
||||||
<extension name="mongo" enabled="false" />
|
<extension name="mongo" enabled="false" />
|
||||||
<extension name="mongodb" enabled="false" />
|
<extension name="mongodb" enabled="false" />
|
||||||
|
@ -86,9 +95,6 @@
|
||||||
<extension name="odbc" enabled="false" />
|
<extension name="odbc" enabled="false" />
|
||||||
<extension name="opentelemetry" enabled="false" />
|
<extension name="opentelemetry" enabled="false" />
|
||||||
<extension name="pdflib" enabled="false" />
|
<extension name="pdflib" enabled="false" />
|
||||||
<extension name="pdo_ibm" enabled="false" />
|
|
||||||
<extension name="pdo_pgsql" enabled="false" />
|
|
||||||
<extension name="pdo_sqlite" enabled="false" />
|
|
||||||
<extension name="pgsql" enabled="false" />
|
<extension name="pgsql" enabled="false" />
|
||||||
<extension name="pspell" enabled="false" />
|
<extension name="pspell" enabled="false" />
|
||||||
<extension name="pthreads" enabled="false" />
|
<extension name="pthreads" enabled="false" />
|
||||||
|
@ -118,14 +124,24 @@
|
||||||
<extension name="zmq" enabled="false" />
|
<extension name="zmq" enabled="false" />
|
||||||
</extensions>
|
</extensions>
|
||||||
</component>
|
</component>
|
||||||
|
<component name="PhpStan">
|
||||||
|
<PhpStan_settings>
|
||||||
|
<phpstan_by_interpreter asDefaultInterpreter="true" interpreter_id="347c1ee9-eeae-4334-9e17-b7e47c2bca71" timeout="60000" />
|
||||||
|
</PhpStan_settings>
|
||||||
|
</component>
|
||||||
<component name="PhpStanOptionsConfiguration">
|
<component name="PhpStanOptionsConfiguration">
|
||||||
<option name="transferred" value="true" />
|
<option name="transferred" value="true" />
|
||||||
</component>
|
</component>
|
||||||
<component name="PhpUnit">
|
<component name="PhpUnit">
|
||||||
<phpunit_settings>
|
<phpunit_settings>
|
||||||
<PhpUnitSettings load_method="PHPUNIT_PHAR" bootstrap_file_path="$PROJECT_DIR$/phpunit.xml" custom_loader_path="" phpunit_phar_path="$USER_HOME$/phpunit.phar" />
|
<PhpUnitSettings load_method="PHPUNIT_PHAR" bootstrap_file_path="$PROJECT_DIR$/bootstrap.php" custom_loader_path="" phpunit_phar_path="$USER_HOME$/phpunit.phar" use_bootstrap_file="true" />
|
||||||
</phpunit_settings>
|
</phpunit_settings>
|
||||||
</component>
|
</component>
|
||||||
|
<component name="Psalm">
|
||||||
|
<Psalm_settings>
|
||||||
|
<psalm_fixer_by_interpreter asDefaultInterpreter="true" interpreter_id="347c1ee9-eeae-4334-9e17-b7e47c2bca71" timeout="60000" />
|
||||||
|
</Psalm_settings>
|
||||||
|
</component>
|
||||||
<component name="PsalmOptionsConfiguration">
|
<component name="PsalmOptionsConfiguration">
|
||||||
<option name="transferred" value="true" />
|
<option name="transferred" value="true" />
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Build" type="MAKEFILE_TARGET_RUN_CONFIGURATION" factoryName="Makefile">
|
<configuration default="false" name="Build Release" type="MAKEFILE_TARGET_RUN_CONFIGURATION" factoryName="Makefile">
|
||||||
<makefile filename="$PROJECT_DIR$/Makefile" target="build" workingDirectory="" arguments="">
|
<makefile filename="$PROJECT_DIR$/Makefile" target="release" workingDirectory="" arguments="">
|
||||||
<envs />
|
<envs />
|
||||||
</makefile>
|
</makefile>
|
||||||
<method v="2">
|
<method v="2">
|
4
.idea/runConfigurations/Install__sudo_.xml
generated
4
.idea/runConfigurations/Install__sudo_.xml
generated
|
@ -12,8 +12,6 @@
|
||||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||||
<envs />
|
<envs />
|
||||||
<method v="2">
|
<method v="2" />
|
||||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Build" run_configuration_type="MAKEFILE_TARGET_RUN_CONFIGURATION" />
|
|
||||||
</method>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
25
.idea/sqldialects.xml
generated
25
.idea/sqldialects.xml
generated
|
@ -1,12 +1,31 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="SqlDialectMappings">
|
<component name="SqlDialectMappings">
|
||||||
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/password_authentication.sql" dialect="MariaDB" />
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/authentication_otp.sql" dialect="MariaDB" />
|
||||||
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/registered_peers.sql" dialect="MariaDB" />
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/authentication_passwords.sql" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/captcha_images.sql" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/channel_com.sql" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/contact_known_keys.sql" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/contacts.sql" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/encryption_channels.sql" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/encryption_channels_com.sql" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/external_sessions.sql" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/peer_information.sql" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/peers.sql" dialect="MariaDB" />
|
||||||
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/sessions.sql" dialect="MariaDB" />
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/sessions.sql" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/signing_keys.sql" dialect="MariaDB" />
|
||||||
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/variables.sql" dialect="MariaDB" />
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/variables.sql" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/CaptchaManager.php" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/ContactManager.php" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/EncryptionChannelManager.php" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/ExternalSessionManager.php" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/OneTimePasswordManager.php" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/PasswordManager.php" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/PeerInformationManager.php" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/RegisteredPeerManager.php" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/ResolvedDnsRecordsManager.php" dialect="MariaDB" />
|
||||||
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/SessionManager.php" dialect="MariaDB" />
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/SessionManager.php" dialect="MariaDB" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/SigningKeysManager.php" dialect="MariaDB" />
|
||||||
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/VariableManager.php" dialect="MariaDB" />
|
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/VariableManager.php" dialect="MariaDB" />
|
||||||
<file url="file:///var/ncc/packages/net.nosial.socialbox=1.0.0/bin/src/Socialbox/Managers/VariableManager.php" dialect="MariaDB" />
|
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
2
.idea/webResources.xml
generated
2
.idea/webResources.xml
generated
|
@ -7,6 +7,8 @@
|
||||||
<resourceRoots>
|
<resourceRoots>
|
||||||
<path value="file://$PROJECT_DIR$/examples" />
|
<path value="file://$PROJECT_DIR$/examples" />
|
||||||
<path value="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources" />
|
<path value="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources" />
|
||||||
|
<path value="file://$PROJECT_DIR$/public" />
|
||||||
|
<path value="file://$PROJECT_DIR$/docker" />
|
||||||
</resourceRoots>
|
</resourceRoots>
|
||||||
</entryData>
|
</entryData>
|
||||||
</entry>
|
</entry>
|
||||||
|
|
132
Dockerfile
Normal file
132
Dockerfile
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Dockerfile for PHP 8.3 + FPM with Cron support and Supervisor
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Base image: Official PHP 8.3 with FPM
|
||||||
|
FROM php:8.3-fpm AS base
|
||||||
|
|
||||||
|
# ----------------------------- Metadata labels ------------------------------
|
||||||
|
LABEL maintainer="Netkas <netkas@n64.cc>" \
|
||||||
|
version="1.0" \
|
||||||
|
description="Socialbox Docker image based off PHP 8.3 FPM and NCC" \
|
||||||
|
application="SocialBox" \
|
||||||
|
base_image="php:8.3-fpm"
|
||||||
|
|
||||||
|
# Environment variable for non-interactive installations
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# ----------------------------- System Dependencies --------------------------
|
||||||
|
# Update system packages and install required dependencies in one step
|
||||||
|
RUN apt-get update -yqq && apt-get install -yqq --no-install-recommends \
|
||||||
|
git \
|
||||||
|
libpq-dev \
|
||||||
|
libzip-dev \
|
||||||
|
zip \
|
||||||
|
make \
|
||||||
|
wget \
|
||||||
|
gnupg \
|
||||||
|
cron \
|
||||||
|
supervisor \
|
||||||
|
mariadb-client \
|
||||||
|
libcurl4-openssl-dev \
|
||||||
|
libmemcached-dev \
|
||||||
|
redis \
|
||||||
|
libgd-dev \
|
||||||
|
nginx \
|
||||||
|
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# ----------------------------- PHP Extensions -------------------------------
|
||||||
|
# Install PHP extensions and enable additional ones
|
||||||
|
RUN docker-php-ext-install -j$(nproc) \
|
||||||
|
pdo \
|
||||||
|
pdo_mysql \
|
||||||
|
mysqli \
|
||||||
|
gd \
|
||||||
|
curl \
|
||||||
|
opcache \
|
||||||
|
zip \
|
||||||
|
sockets \
|
||||||
|
pcntl && \
|
||||||
|
pecl install redis memcached && \
|
||||||
|
docker-php-ext-enable redis memcached
|
||||||
|
|
||||||
|
# ----------------------------- Additional Tools -----------------------------
|
||||||
|
# Install Phive (Package Manager for PHAR libraries) and global tools in one step
|
||||||
|
RUN wget -O /usr/local/bin/phive https://phar.io/releases/phive.phar && \
|
||||||
|
wget -O /usr/local/bin/phive.asc https://phar.io/releases/phive.phar.asc && \
|
||||||
|
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79 && \
|
||||||
|
gpg --verify /usr/local/bin/phive.asc /usr/local/bin/phive && \
|
||||||
|
chmod +x /usr/local/bin/phive && \
|
||||||
|
phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
|
||||||
|
|
||||||
|
# ----------------------------- Clone and Build NCC --------------------------
|
||||||
|
# Clone the NCC repository, build the project, and install it
|
||||||
|
RUN git clone https://git.n64.cc/nosial/ncc.git && \
|
||||||
|
cd ncc && \
|
||||||
|
make redist && \
|
||||||
|
NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1) && \
|
||||||
|
if [ -z "$NCC_DIR" ]; then \
|
||||||
|
echo "NCC build directory not found"; \
|
||||||
|
exit 1; \
|
||||||
|
fi && \
|
||||||
|
php "$NCC_DIR/INSTALL" --auto && \
|
||||||
|
cd .. && rm -rf ncc
|
||||||
|
|
||||||
|
# ----------------------------- Project Build ---------------------------------
|
||||||
|
# Set build directory and copy pre-needed project files
|
||||||
|
WORKDIR /tmp/build
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN ncc build --config release --build-source --log-level debug && \
|
||||||
|
ncc package install --package=build/release/net.nosial.socialbox.ncc --build-source -y --log-level=debug
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
RUN rm -rf /tmp/build && rm -rf /var/www/html/*
|
||||||
|
|
||||||
|
# Copy over the required files
|
||||||
|
COPY docker/nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY public/index.php /var/www/html/index.php
|
||||||
|
RUN chown -R www-data:www-data /var/www/html && chmod -R 755 /var/www/html
|
||||||
|
|
||||||
|
# ----------------------------- Environment Configuration ---------------------------
|
||||||
|
# Configure Cron
|
||||||
|
RUN echo "*/1 * * * * root for i in {1..12}; do /usr/bin/socialbox process-outgoing; sleep 5; done" > /etc/cron.d/socialbox-process-outgoing && \
|
||||||
|
echo "*/1 * * * * root /usr/bin/socialbox session-cleanup" > /etc/cron.d/socialbox-session-cleanup && \
|
||||||
|
echo "*/5 * * * * root /usr/bin/socialbox peer-cleanup" > /etc/cron.d/socialbox-peer-cleanup && \
|
||||||
|
\
|
||||||
|
chmod 0644 /etc/cron.d/socialbox-process-outgoing && \
|
||||||
|
chmod 0644 /etc/cron.d/socialbox-session-cleanup && \
|
||||||
|
chmod 0644 /etc/cron.d/socialbox-peer-cleanup && \
|
||||||
|
\
|
||||||
|
crontab /etc/cron.d/socialbox-process-outgoing && \
|
||||||
|
crontab /etc/cron.d/socialbox-session-cleanup && \
|
||||||
|
crontab /etc/cron.d/socialbox-peer-cleanup
|
||||||
|
|
||||||
|
# Copy Supervisor configuration
|
||||||
|
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||||
|
# Copy docker.conf & zz-docker.conf for PHP-FPM
|
||||||
|
COPY docker/docker.conf /usr/local/etc/php-fpm.d/docker.conf
|
||||||
|
COPY docker/zz-docker.conf /usr/local/etc/php-fpm.d/zz-docker.conf
|
||||||
|
|
||||||
|
# Configure php.ini and enable error and log it to /var/log rather than stdout
|
||||||
|
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini && \
|
||||||
|
sed -i 's/^;error_log = php_errors.log/error_log = \/var\/log\/php_errors.log/' /usr/local/etc/php/php.ini && \
|
||||||
|
sed -i 's/^;log_errors = On/log_errors = On/' /usr/local/etc/php/php.ini && \
|
||||||
|
sed -i 's/^;error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT/error_reporting = E_ALL/' /usr/local/etc/php/php.ini && \
|
||||||
|
sed -i 's/^;display_errors = Off/display_errors = On/' /usr/local/etc/php/php.ini && \
|
||||||
|
sed -i 's/^;date.timezone =/date.timezone = UTC/' /usr/local/etc/php/php.ini
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------- Cleanup ---------------------
|
||||||
|
WORKDIR /
|
||||||
|
|
||||||
|
# ----------------------------- Port Exposing ---------------------------------
|
||||||
|
EXPOSE 8085
|
||||||
|
|
||||||
|
# ----------------------------- Container Startup ----------------------------
|
||||||
|
# Copy over entrypoint script and set it as executable
|
||||||
|
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||||
|
|
||||||
|
# Set the entrypoint
|
||||||
|
ENTRYPOINT ["/usr/bin/bash", "/usr/local/bin/entrypoint.sh"]
|
26
Makefile
26
Makefile
|
@ -1,23 +1,29 @@
|
||||||
# Variables
|
# Variables
|
||||||
CONFIG ?= release
|
DEFAULT_CONFIGURATION ?= release
|
||||||
LOG_LEVEL = debug
|
LOG_LEVEL = debug
|
||||||
OUTDIR = build/$(CONFIG)
|
|
||||||
PACKAGE = $(OUTDIR)/net.nosial.socialbox.ncc
|
|
||||||
|
|
||||||
# Default Target
|
# Default Target
|
||||||
all: build
|
all: release debug release_executable debug_executable
|
||||||
|
|
||||||
# Build Steps
|
# Build Steps
|
||||||
build:
|
release:
|
||||||
ncc build --config=$(CONFIG) --log-level $(LOG_LEVEL)
|
ncc build --config=release --log-level $(LOG_LEVEL)
|
||||||
|
debug:
|
||||||
|
ncc build --config=debug --log-level $(LOG_LEVEL)
|
||||||
|
release_executable:
|
||||||
|
ncc build --config=release_executable --log-level $(LOG_LEVEL)
|
||||||
|
debug_executable:
|
||||||
|
ncc build --config=debug_executable --log-level $(LOG_LEVEL)
|
||||||
|
|
||||||
install: build
|
|
||||||
ncc package install --package=$(PACKAGE) --skip-dependencies --build-source --reinstall -y --log-level $(LOG_LEVEL)
|
|
||||||
|
|
||||||
test: build
|
install: release
|
||||||
|
ncc package install --package=build/release/net.nosial.socialbox.ncc --skip-dependencies --build-source --reinstall -y --log-level $(LOG_LEVEL)
|
||||||
|
|
||||||
|
test: release
|
||||||
|
[ -f phpunit.xml ] || { echo "phpunit.xml not found"; exit 1; }
|
||||||
phpunit
|
phpunit
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf build
|
rm -rf build
|
||||||
|
|
||||||
.PHONY: all build install test clean
|
.PHONY: all install test clean release debug release_executable debug_executable
|
117
coffee_socialbox/config/socialbox.conf
Executable file
117
coffee_socialbox/config/socialbox.conf
Executable file
|
@ -0,0 +1,117 @@
|
||||||
|
{
|
||||||
|
"instance": {
|
||||||
|
"enabled": true,
|
||||||
|
"name": "coffee",
|
||||||
|
"domain": "coffee.com",
|
||||||
|
"rpc_endpoint": "http://coffee_socialbox:8085/",
|
||||||
|
"dns_mocks": {
|
||||||
|
"teapot.com": "v=socialbox;sb-rpc=http://teapot_socialbox:8085/;sb-key=sig:MDXUuripAo_IAv-EZTEoFhpIdhsXxfMLNunSnQzxYiY;sb-exp=0",
|
||||||
|
"coffee.com": "v=socialbox;sb-rpc=http://coffee_socialbox:8085/;sb-key=sig:g59Cf8j1wmQmRg1MkveYbpdiZ-1-_hFU9eRRJmQAwmc;sb-exp=0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"display_internal_exceptions": true,
|
||||||
|
"resolved_servers_ttl": 600,
|
||||||
|
"captcha_ttl": 200,
|
||||||
|
"otp_secret_key_length": 32,
|
||||||
|
"otp_time_step": 30,
|
||||||
|
"otp_digits": 6,
|
||||||
|
"otp_hash_algorithm": "sha512",
|
||||||
|
"otp_window": 1
|
||||||
|
},
|
||||||
|
"cryptography": {
|
||||||
|
"host_keypair_expires": 0,
|
||||||
|
"host_public_key": "sig:g59Cf8j1wmQmRg1MkveYbpdiZ-1-_hFU9eRRJmQAwmc",
|
||||||
|
"host_private_key": "sig:tTVe59Ko5XuwgS8PneR92FAOqbgSHTKYn8U-lQRB9KODn0J_yPXCZCZGDUyS95hul2Jn7X7-EVT15FEmZADCZw",
|
||||||
|
"internal_encryption_keys": [
|
||||||
|
"c2cpdTkYqIWI93cJPpAuCsoQJcHi9l37lYHA2TpUo9A",
|
||||||
|
"XUuWyWcKmtCUNVZ7Y0ZDbCE72klHZIniRihIIo78Vbs",
|
||||||
|
"SGg4GM_0-hO95Q6hBq2UjzGrp9mhVHyklNTHo-OZSNw",
|
||||||
|
"43WrkV6rDyc04S41E4uwJ1nQFhlll_CflsPW_hMOiqE",
|
||||||
|
"QDh9KecIdU-6be5ScPagL_WrWp8hQAersLQvLv9YtNQ",
|
||||||
|
"z4SnLU9Xw9F3yjPH_TmV4HuvZrpaVE0bqxzUGHyXQ-k",
|
||||||
|
"vg7lWOzkL_59u3o2RKcdrdwc7KVh07NrZRQzBPoJXEU",
|
||||||
|
"UW6X3XGGLj_e8xYd1bUwX9KYPTczHFtYTmy4FfiqfG0",
|
||||||
|
"sh-sRIQ3lWgkqR87wcTtZkDrgDKY2FOLuzdtpAvi9Wg",
|
||||||
|
"SDweTV1kNH0s5Ah1pwbfDo3ThAXAVKo9qJ4V9-hsHIs"
|
||||||
|
],
|
||||||
|
"encryption_keys_count": 10,
|
||||||
|
"encryption_keys_algorithm": "xchacha20",
|
||||||
|
"transport_encryption_algorithm": "chacha20"
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"host": "coffee_mariadb",
|
||||||
|
"port": 0,
|
||||||
|
"username": "socialbox",
|
||||||
|
"password": "socialbox",
|
||||||
|
"name": "socialbox"
|
||||||
|
},
|
||||||
|
"logging": {
|
||||||
|
"console_logging_enabled": true,
|
||||||
|
"console_logging_level": "info",
|
||||||
|
"file_logging_enabled": true,
|
||||||
|
"file_logging_level": "debug"
|
||||||
|
},
|
||||||
|
"cache": {
|
||||||
|
"enabled": true,
|
||||||
|
"engine": "redis",
|
||||||
|
"host": "coffee_redis",
|
||||||
|
"port": 6379,
|
||||||
|
"username": "root",
|
||||||
|
"password": "root",
|
||||||
|
"database": "0",
|
||||||
|
"sessions": {
|
||||||
|
"enabled": true,
|
||||||
|
"ttl": 3600,
|
||||||
|
"max": 1000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"registration": {
|
||||||
|
"enabled": true,
|
||||||
|
"privacy_policy_document": null,
|
||||||
|
"privacy_policy_date": 1734985525,
|
||||||
|
"accept_privacy_policy": false,
|
||||||
|
"terms_of_service_document": null,
|
||||||
|
"terms_of_service_date": 1734985525,
|
||||||
|
"accept_terms_of_service": false,
|
||||||
|
"community_guidelines_document": null,
|
||||||
|
"community_guidelines_date": 1734985525,
|
||||||
|
"accept_community_guidelines": false,
|
||||||
|
"password_required": true,
|
||||||
|
"otp_required": false,
|
||||||
|
"display_name_required": true,
|
||||||
|
"display_picture_required": false,
|
||||||
|
"email_address_required": false,
|
||||||
|
"phone_number_required": false,
|
||||||
|
"birthday_required": false,
|
||||||
|
"image_captcha_verification_required": false,
|
||||||
|
"first_name_required": false,
|
||||||
|
"middle_name_required": false,
|
||||||
|
"last_name_required": false,
|
||||||
|
"url_required": false
|
||||||
|
},
|
||||||
|
"authentication": {
|
||||||
|
"enabled": true,
|
||||||
|
"image_captcha_verification_required": true
|
||||||
|
},
|
||||||
|
"policies": {
|
||||||
|
"max_signing_keys": 20,
|
||||||
|
"session_inactivity_expires": 43200,
|
||||||
|
"image_captcha_expires": 300,
|
||||||
|
"peer_sync_interval": 3600,
|
||||||
|
"get_contacts_limit": 100,
|
||||||
|
"default_display_picture_privacy": "PUBLIC",
|
||||||
|
"default_first_name_privacy": "CONTACTS",
|
||||||
|
"default_middle_name_privacy": "PRIVATE",
|
||||||
|
"default_last_name_privacy": "PRIVATE",
|
||||||
|
"default_email_address_privacy": "CONTACTS",
|
||||||
|
"default_phone_number_privacy": "CONTACTS",
|
||||||
|
"default_birthday_privacy": "PRIVATE",
|
||||||
|
"default_url_privacy": "PUBLIC"
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"path": "/etc/socialbox",
|
||||||
|
"user_display_images_path": "user_profiles",
|
||||||
|
"user_display_images_max_size": 3145728
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"name": "vendor_name/socialbox-php",
|
|
||||||
"description": "description",
|
|
||||||
"minimum-stability": "stable",
|
|
||||||
"license": "proprietary",
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Netkas",
|
|
||||||
"email": "netkas@nosial.net"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"require": {
|
|
||||||
"ext-pdo": "*",
|
|
||||||
"ext-openssl": "*",
|
|
||||||
"ext-redis": "*",
|
|
||||||
"ext-memcached": "*"
|
|
||||||
}
|
|
||||||
}
|
|
245
docker-compose.test.yml
Normal file
245
docker-compose.test.yml
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
# Test docker-compose file for SocialBox service to setup two instances of the service:
|
||||||
|
# 1. Teapot Service (teapot.com)
|
||||||
|
# 2. Coffee Service (coffee.com)
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
# Coffee Service (coffee.com test)
|
||||||
|
coffee_socialbox:
|
||||||
|
container_name: coffee_socialbox
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8086:8085"
|
||||||
|
depends_on:
|
||||||
|
coffee_mariadb:
|
||||||
|
condition: service_healthy
|
||||||
|
coffee_redis:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- coffee_network
|
||||||
|
- shared_network
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./coffee_socialbox/config:/etc/config
|
||||||
|
- ./coffee_socialbox/data:/etc/socialbox
|
||||||
|
environment:
|
||||||
|
# No need to change these values
|
||||||
|
LOG_LEVEL: ${LOG_LEVEL:-debug}
|
||||||
|
CONFIGLIB_PATH: /etc/config
|
||||||
|
LOGGING_DIRECTORY: /var/log
|
||||||
|
SB_MODE: automated
|
||||||
|
SB_STORAGE_PATH: /etc/socialbox
|
||||||
|
# Change these values to match your environment or update the .env file
|
||||||
|
SB_INSTANCE_NAME: ${SB_COFFEE_NAME:-coffee} # Instance name SB_COFFEE_NAME
|
||||||
|
SB_INSTANCE_DOMAIN: ${SB_COFFEE_DOMAIN:-coffee.com} # Instance domain SB_COFFEE_DOMAIN
|
||||||
|
SB_INSTANCE_RPC_ENDPOINT: ${SB_COFFEE_RPC_ENDPOINT:-http://coffee_socialbox:8085/} # Instance RPC endpoint SB_COFFEE_RPC_ENDPOINT
|
||||||
|
SB_LOGGING_CONSOLE_ENABLED: ${SB_LOGGING_CONSOLE_ENABLED:-true}
|
||||||
|
SB_LOGGING_CONSOLE_LEVEL: ${SB_LOGGING_CONSOLE_LEVEL:-debug}
|
||||||
|
SB_LOGGING_FILE_ENABLED: ${SB_LOGGING_FILE_ENABLED:-true}
|
||||||
|
SB_LOGGING_FILE_LEVEL: ${SB_LOGGING_FILE_LEVEL:-debug}
|
||||||
|
SB_SECURITY_DISPLAY_INTERNAL_EXCEPTIONS: true
|
||||||
|
SB_CRYPTO_KEYPAIR_EXPIRES: ${SB_CRYPTO_KEYPAIR_EXPIRES}
|
||||||
|
SB_CRYPTO_ENCRYPTION_KEYS_COUNT: ${SB_CRYPTO_ENCRYPTION_KEYS_COUNT:-10}
|
||||||
|
SB_CRYPTO_ENCRYPTION_KEYS_ALGORITHM: ${SB_CRYPTO_ENCRYPTION_KEYS_ALGORITHM:-xchacha20}
|
||||||
|
SB_CRYPTO_TRANSPORT_ENCRYPTION_ALGORITHM: ${SB_CRYPTO_TRANSPORT_ENCRYPTION_ALGORITHM:-chacha20}
|
||||||
|
SB_DATABASE_HOST: coffee_mariadb
|
||||||
|
SB_DATABASE_USERNAME: ${MYSQL_USER:-socialbox}
|
||||||
|
SB_DATABASE_PASSWORD: ${MYSQL_PASSWORD:-socialbox}
|
||||||
|
SB_DATABASE_NAME: ${MYSQL_DATABASE:-socialbox}
|
||||||
|
SB_CACHE_ENABLED: ${SB_CACHE_ENABLED:-true}
|
||||||
|
SB_CACHE_ENGINE: redis
|
||||||
|
SB_CACHE_HOST: coffee_redis
|
||||||
|
SB_CACHE_PORT: ${SB_CACHE_PORT:-6379}
|
||||||
|
SB_CACHE_USERNAME: ${SB_CACHE_USERNAME:-root}
|
||||||
|
SB_CACHE_PASSWORD: ${SB_CACHE_PASSWORD:-root}
|
||||||
|
SB_CACHE_DATABASE: ${SB_CACHE_DATABASE:-0}
|
||||||
|
# Mocking, required for testing without the need for configuring actual DNS records
|
||||||
|
# Usage: SB_INSTANCE_DNS_MOCK_<INSTANCE_NAME>: <DOMAIN> <TXT_RECORD>
|
||||||
|
# Environment Variable name is ignored, only the value is used with the prefix being used to detect
|
||||||
|
# the instance name and the suffix being used to detect the TXT record
|
||||||
|
SB_INSTANCE_DNS_MOCK_COFFEE: ${SB_INSTANCE_DNS_MOCK_COFFEE:-"coffee.com v=socialbox;sb-rpc=http://coffee_socialbox:8085/;sb-key=sig:g59Cf8j1wmQmRg1MkveYbpdiZ-1-_hFU9eRRJmQAwmc;sb-exp=0"}
|
||||||
|
SB_INSTANCE_DNS_MOCK_TEAPOT: ${SB_INSTANCE_DNS_MOCK_TEAPOT:-"teapot.com v=socialbox;sb-rpc=http://teapot_socialbox:8085/;sb-key=sig:MDXUuripAo_IAv-EZTEoFhpIdhsXxfMLNunSnQzxYiY;sb-exp=0"}
|
||||||
|
# UDP Logging, won't cause issues if the server is not available
|
||||||
|
# See https://github.com/nosial/LogLib2/blob/master/server.py for more information
|
||||||
|
LOGLIB_UDP_ENABLED: true
|
||||||
|
LOGLIB_UDP_HOST: 172.17.0.1
|
||||||
|
LOGLIB_UDP_PORT: 5131
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "-H", "Request-Type: ping", "${SB_INSTANCE_RPC_ENDPOINT-http://coffee_socialbox:8085/}"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
coffee_mariadb:
|
||||||
|
container_name: coffee_socialbox_mariadb
|
||||||
|
image: mariadb:10.5
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-sb_root}
|
||||||
|
MYSQL_DATABASE: ${MYSQL_DATABASE:-socialbox}
|
||||||
|
MYSQL_USER: ${MYSQL_USER:-socialbox}
|
||||||
|
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-socialbox}
|
||||||
|
volumes:
|
||||||
|
- coffee_mariadb_data:/var/lib/mysql
|
||||||
|
networks:
|
||||||
|
- coffee_network
|
||||||
|
ports:
|
||||||
|
- "3308:3306"
|
||||||
|
expose:
|
||||||
|
- "3306"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "coffee_mariadb", "-u", "${MYSQL_USER:-socialbox}", "-p${MYSQL_PASSWORD:-socialbox}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
coffee_redis:
|
||||||
|
container_name: coffee_socialbox_redis
|
||||||
|
image: redis:alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes
|
||||||
|
volumes:
|
||||||
|
- coffee_redis_data:/data
|
||||||
|
- ./docker/redis.conf:/usr/local/etc/redis/redis.conf
|
||||||
|
networks:
|
||||||
|
- coffee_network
|
||||||
|
expose:
|
||||||
|
- "6379"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 5s
|
||||||
|
|
||||||
|
# Teapot Service (teapot.com test)
|
||||||
|
teapot_socialbox:
|
||||||
|
container_name: teapot_socialbox
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8087:8085" # Unique port for Teapot instance
|
||||||
|
depends_on:
|
||||||
|
teapot_mariadb:
|
||||||
|
condition: service_healthy
|
||||||
|
teapot_redis:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- teapot_network
|
||||||
|
- shared_network
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./teapot_socialbox/config:/etc/config
|
||||||
|
- ./teapot_socialbox/data:/etc/socialbox
|
||||||
|
environment:
|
||||||
|
# No need to change these values
|
||||||
|
LOG_LEVEL: ${LOG_LEVEL:-debug}
|
||||||
|
CONFIGLIB_PATH: /etc/config
|
||||||
|
LOGGING_DIRECTORY: /var/log
|
||||||
|
SB_MODE: automated
|
||||||
|
SB_STORAGE_PATH: /etc/socialbox
|
||||||
|
# Change these values to match your environment or update the .env file
|
||||||
|
SB_INSTANCE_NAME: ${SB_TEAPOT_NAME:-teapot} # Instance name SB_TEAPOT_NAME
|
||||||
|
SB_INSTANCE_DOMAIN: ${SB_TEAPOT_DOMAIN:-teapot.com} # Instance domain SB_TEAPOT_DOMAIN
|
||||||
|
SB_INSTANCE_RPC_ENDPOINT: ${SB_TEAPOT_RPC_ENDPOINT:-http://teapot_socialbox:8085/} # Instance RPC endpoint SB_TEAPOT_RPC_ENDPOINT
|
||||||
|
SB_LOGGING_CONSOLE_ENABLED: ${SB_LOGGING_CONSOLE_ENABLED:-true}
|
||||||
|
SB_LOGGING_CONSOLE_LEVEL: ${SB_LOGGING_CONSOLE_LEVEL:-debug}
|
||||||
|
SB_LOGGING_FILE_ENABLED: ${SB_LOGGING_FILE_ENABLED:-true}
|
||||||
|
SB_LOGGING_FILE_LEVEL: ${SB_LOGGING_FILE_LEVEL:-debug}
|
||||||
|
SB_SECURITY_DISPLAY_INTERNAL_EXCEPTIONS: true
|
||||||
|
SB_CRYPTO_KEYPAIR_EXPIRES: ${SB_CRYPTO_KEYPAIR_EXPIRES}
|
||||||
|
SB_CRYPTO_ENCRYPTION_KEYS_COUNT: ${SB_CRYPTO_ENCRYPTION_KEYS_COUNT:-10}
|
||||||
|
SB_CRYPTO_ENCRYPTION_KEYS_ALGORITHM: ${SB_CRYPTO_ENCRYPTION_KEYS_ALGORITHM:-xchacha20}
|
||||||
|
SB_CRYPTO_TRANSPORT_ENCRYPTION_ALGORITHM: ${SB_CRYPTO_TRANSPORT_ENCRYPTION_ALGORITHM:-chacha20}
|
||||||
|
SB_DATABASE_HOST: teapot_mariadb
|
||||||
|
SB_DATABASE_USERNAME: ${MYSQL_USER:-socialbox}
|
||||||
|
SB_DATABASE_PASSWORD: ${MYSQL_PASSWORD:-socialbox}
|
||||||
|
SB_DATABASE_NAME: ${MYSQL_DATABASE:-socialbox}
|
||||||
|
SB_CACHE_ENABLED: ${SB_CACHE_ENABLED:-true}
|
||||||
|
SB_CACHE_ENGINE: redis
|
||||||
|
SB_CACHE_HOST: teapot_redis
|
||||||
|
SB_CACHE_PORT: ${SB_CACHE_PORT:-6379}
|
||||||
|
SB_CACHE_USERNAME: ${SB_CACHE_USERNAME:-root}
|
||||||
|
SB_CACHE_PASSWORD: ${SB_CACHE_PASSWORD:-root}
|
||||||
|
SB_CACHE_DATABASE: ${SB_CACHE_DATABASE:-0}
|
||||||
|
# Mocking, required for testing without the need for configuring actual DNS records
|
||||||
|
# Usage: SB_INSTANCE_DNS_MOCK_<INSTANCE_NAME>: <DOMAIN> <TXT_RECORD>
|
||||||
|
# Environment Variable name is ignored, only the value is used with the prefix being used to detect
|
||||||
|
# the instance name and the suffix being used to detect the TXT record
|
||||||
|
SB_INSTANCE_DNS_MOCK_COFFEE: ${SB_INSTANCE_DNS_MOCK_COFFEE:-"coffee.com v=socialbox;sb-rpc=http://coffee_socialbox:8085/;sb-key=sig:g59Cf8j1wmQmRg1MkveYbpdiZ-1-_hFU9eRRJmQAwmc;sb-exp=0"}
|
||||||
|
SB_INSTANCE_DNS_MOCK_TEAPOT: ${SB_INSTANCE_DNS_MOCK_TEAPOT:-"teapot.com v=socialbox;sb-rpc=http://teapot_socialbox:8085/;sb-key=sig:MDXUuripAo_IAv-EZTEoFhpIdhsXxfMLNunSnQzxYiY;sb-exp=0"}
|
||||||
|
# UDP Logging, won't cause issues if the server is not available
|
||||||
|
# See https://github.com/nosial/LogLib2/blob/master/server.py for more information
|
||||||
|
LOGLIB_UDP_ENABLED: true
|
||||||
|
LOGLIB_UDP_HOST: 172.17.0.1
|
||||||
|
LOGLIB_UDP_PORT: 5131
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "-H", "Request-Type: ping", "${SB_INSTANCE_RPC_ENDPOINT-http://teapot_socialbox:8085/}"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
teapot_mariadb:
|
||||||
|
container_name: teapot_socialbox_mariadb
|
||||||
|
image: mariadb:10.5
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-sb_root}
|
||||||
|
MYSQL_DATABASE: ${MYSQL_DATABASE:-socialbox}
|
||||||
|
MYSQL_USER: ${MYSQL_USER:-socialbox}
|
||||||
|
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-socialbox}
|
||||||
|
volumes:
|
||||||
|
- teapot_mariadb_data:/var/lib/mysql
|
||||||
|
networks:
|
||||||
|
- teapot_network
|
||||||
|
ports:
|
||||||
|
- "3307:3306" # Unique port for Teapot instance
|
||||||
|
expose:
|
||||||
|
- "3306"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "teapot_mariadb", "-u", "${MYSQL_USER:-socialbox}", "-p${MYSQL_PASSWORD:-socialbox}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
teapot_redis:
|
||||||
|
container_name: teapot_socialbox_redis
|
||||||
|
image: redis:alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes
|
||||||
|
volumes:
|
||||||
|
- teapot_redis_data:/data
|
||||||
|
- ./docker/redis.conf:/usr/local/etc/redis/redis.conf
|
||||||
|
networks:
|
||||||
|
- teapot_network
|
||||||
|
expose:
|
||||||
|
- "6379"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 5s
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
teapot_mariadb_data:
|
||||||
|
driver: local
|
||||||
|
teapot_redis_data:
|
||||||
|
driver: local
|
||||||
|
coffee_redis_data:
|
||||||
|
driver: local
|
||||||
|
coffee_mariadb_data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
teapot_network:
|
||||||
|
driver: bridge
|
||||||
|
name: teapot_network
|
||||||
|
coffee_network:
|
||||||
|
driver: bridge
|
||||||
|
name: coffee_network
|
||||||
|
shared_network:
|
||||||
|
driver: bridge
|
109
docker-compose.yml
Normal file
109
docker-compose.yml
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
services:
|
||||||
|
socialbox:
|
||||||
|
container_name: socialbox
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8085:8085"
|
||||||
|
depends_on:
|
||||||
|
mariadb:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- internal_network
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./socialbox/config:/etc/config
|
||||||
|
- ./socialbox/logs:/var/log
|
||||||
|
- ./socialbox/data:/etc/socialbox
|
||||||
|
environment:
|
||||||
|
# No need to change these values
|
||||||
|
LOG_LEVEL: ${LOG_LEVEL:-debug}
|
||||||
|
CONFIGLIB_PATH: /etc/config
|
||||||
|
LOGGING_DIRECTORY: /var/log
|
||||||
|
SB_MODE: automated
|
||||||
|
SB_STORAGE_PATH: /etc/socialbox
|
||||||
|
# Change these values to match your environment or update the .env file
|
||||||
|
SB_INSTANCE_NAME: ${SB_INSTANCE_NAME:-socialbox}
|
||||||
|
SB_INSTANCE_DOMAIN: ${SB_DOMAIN:-localhost}
|
||||||
|
SB_INSTANCE_RPC_ENDPOINT: ${SB_RPC_ENDPOINT:-http://127.0.0.0:8085/}
|
||||||
|
SB_LOGGING_CONSOLE_ENABLED: ${SB_LOGGING_CONSOLE_ENABLED:-true}
|
||||||
|
SB_LOGGING_CONSOLE_LEVEL: ${SB_LOGGING_CONSOLE_LEVEL:-info}
|
||||||
|
SB_LOGGING_FILE_ENABLED: ${SB_LOGGING_FILE_ENABLED:-true}
|
||||||
|
SB_LOGGING_FILE_LEVEL: ${SB_LOGGING_FILE_LEVEL:-error}
|
||||||
|
SB_SECURITY_DISPLAY_INTERNAL_EXCEPTIONS: ${SB_SECURITY_DISPLAY_INTERNAL_EXCEPTIONS:-false}
|
||||||
|
SB_CRYPTO_KEYPAIR_EXPIRES: ${SB_CRYPTO_KEYPAIR_EXPIRES}
|
||||||
|
SB_CRYPTO_ENCRYPTION_KEYS_COUNT: ${SB_CRYPTO_ENCRYPTION_KEYS_COUNT:-10}
|
||||||
|
SB_CRYPTO_ENCRYPTION_KEYS_ALGORITHM: ${SB_CRYPTO_ENCRYPTION_KEYS_ALGORITHM:-xchacha20}
|
||||||
|
SB_CRYPTO_TRANSPORT_ENCRYPTION_ALGORITHM: ${SB_CRYPTO_TRANSPORT_ENCRYPTION_ALGORITHM:-chacha20}
|
||||||
|
SB_DATABASE_HOST: mariadb
|
||||||
|
SB_DATABASE_USERNAME: ${MYSQL_USER:-socialbox}
|
||||||
|
SB_DATABASE_PASSWORD: ${MYSQL_PASSWORD:-socialbox}
|
||||||
|
SB_DATABASE_NAME: ${MYSQL_DATABASE:-socialbox}
|
||||||
|
SB_CACHE_ENABLED: ${SB_CACHE_ENABLED:-true}
|
||||||
|
SB_CACHE_ENGINE: redis
|
||||||
|
SB_CACHE_HOST: redis
|
||||||
|
SB_CACHE_PORT: ${SB_CACHE_PORT:-6379}
|
||||||
|
SB_CACHE_USERNAME: ${SB_CACHE_USERNAME:-root}
|
||||||
|
SB_CACHE_PASSWORD: ${SB_CACHE_PASSWORD:-root}
|
||||||
|
SB_CACHE_DATABASE: ${SB_CACHE_DATABASE:-0}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "-H", "Request-Type: ping", "${SB_INSTANCE_RPC_ENDPOINT-http://127.0.0.0:8085/}"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
mariadb:
|
||||||
|
container_name: socialbox_mariadb
|
||||||
|
image: mariadb:10.5
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-sb_root}
|
||||||
|
MYSQL_DATABASE: ${MYSQL_DATABASE:-socialbox}
|
||||||
|
MYSQL_USER: ${MYSQL_USER:-socialbox}
|
||||||
|
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-socialbox}
|
||||||
|
volumes:
|
||||||
|
- mariadb_data:/var/lib/mysql
|
||||||
|
networks:
|
||||||
|
- internal_network
|
||||||
|
expose:
|
||||||
|
- "3306"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "mariadb", "-u", "${MYSQL_USER:-socialbox}", "-p${MYSQL_PASSWORD:-socialbox}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
redis:
|
||||||
|
container_name: socialbox_redis
|
||||||
|
image: redis:alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
- ./docker/redis.conf:/usr/local/etc/redis/redis.conf
|
||||||
|
networks:
|
||||||
|
- internal_network
|
||||||
|
expose:
|
||||||
|
- "6379"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 5s
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mariadb_data:
|
||||||
|
driver: local
|
||||||
|
redis_data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
internal_network:
|
||||||
|
driver: bridge
|
||||||
|
name: socialbox_network
|
2
docker/docker.conf
Normal file
2
docker/docker.conf
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[www]
|
||||||
|
clear_env = no
|
22
docker/entrypoint.sh
Normal file
22
docker/entrypoint.sh
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Banner with cool ASCII art
|
||||||
|
echo "███████╗ ██████╗ ██████╗██╗ █████╗ ██╗ ██████╗ ██████╗ ██╗ ██╗"
|
||||||
|
echo "██╔════╝██╔═══██╗██╔════╝██║██╔══██╗██║ ██╔══██╗██╔═══██╗╚██╗██╔╝"
|
||||||
|
echo "███████╗██║ ██║██║ ██║███████║██║ ██████╔╝██║ ██║ ╚███╔╝ "
|
||||||
|
echo "╚════██║██║ ██║██║ ██║██╔══██║██║ ██╔══██╗██║ ██║ ██╔██╗ "
|
||||||
|
echo "███████║╚██████╔╝╚██████╗██║██║ ██║███████╗██████╔╝╚██████╔╝██╔╝ ██╗"
|
||||||
|
echo "╚══════╝ ╚═════╝ ╚═════╝╚═╝╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝"
|
||||||
|
|
||||||
|
# Check if the environment variable SB_MODE is set to "automated", if not exit.
|
||||||
|
if [ "$SB_MODE" != "automated" ]; then
|
||||||
|
echo "SB_MODE is not set to 'automated', exiting..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Initialize Socialbox
|
||||||
|
echo "Initializing Socialbox..."
|
||||||
|
/usr/bin/socialbox init --log-level=${LOG_LEVEL-INFO}
|
||||||
|
|
||||||
|
# Run supervisord, final command
|
||||||
|
/usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
37
docker/nginx.conf
Normal file
37
docker/nginx.conf
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
http {
|
||||||
|
include mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8085;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
access_log /var/log/access.log;
|
||||||
|
error_log /var/log/error.log;
|
||||||
|
|
||||||
|
root /var/www/html;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
# Handle all requests
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string =503;
|
||||||
|
autoindex off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_pass 127.0.0.1:9000;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Block any .ht* files
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
5
docker/redis.conf
Normal file
5
docker/redis.conf
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
bind 0.0.0.0
|
||||||
|
protected-mode yes
|
||||||
|
port 6379
|
||||||
|
appendonly yes
|
||||||
|
requirepass root
|
52
docker/supervisord.conf
Normal file
52
docker/supervisord.conf
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
[supervisord]
|
||||||
|
logfile=/var/logd.log
|
||||||
|
logfile_maxbytes=50MB
|
||||||
|
logfile_backups=10
|
||||||
|
loglevel=info
|
||||||
|
user=root
|
||||||
|
pidfile=/var/run/supervisord.pid
|
||||||
|
umask=022
|
||||||
|
nodaemon=true
|
||||||
|
minfds=1024
|
||||||
|
minprocs=200
|
||||||
|
|
||||||
|
[program:php-fpm]
|
||||||
|
command=/usr/local/sbin/php-fpm --nodaemonize
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
priority=20
|
||||||
|
stdout_logfile=/var/log/fpm.log
|
||||||
|
stderr_logfile=/var/log/fpm_error.log
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stdout_logfile_backups=5
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
stderr_logfile_backups=5
|
||||||
|
|
||||||
|
[program:php-fpm-log]
|
||||||
|
command=tail -f /var/log/fpm.log /var/log/fpm_error.log
|
||||||
|
stdout_events_enabled=true
|
||||||
|
stderr_events_enabled=true
|
||||||
|
|
||||||
|
[program:nginx]
|
||||||
|
command=/usr/sbin/nginx -g "daemon off;" -c /etc/nginx/nginx.conf
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
priority=10
|
||||||
|
stdout_logfile=/var/log/nginx.log
|
||||||
|
stderr_logfile=/var/log/nginx_error.log
|
||||||
|
stdout_logfile_maxbytes=20MB
|
||||||
|
stdout_logfile_backups=5
|
||||||
|
stderr_logfile_maxbytes=20MB
|
||||||
|
stderr_logfile_backups=5
|
||||||
|
|
||||||
|
[program:cron]
|
||||||
|
command=cron -f -L 15
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
priority=30
|
||||||
|
stdout_logfile=/var/log/cron.log
|
||||||
|
stderr_logfile=/var/log/cron_error.log
|
||||||
|
stdout_logfile_maxbytes=20MB
|
||||||
|
stdout_logfile_backups=5
|
||||||
|
stderr_logfile_maxbytes=20MB
|
||||||
|
stderr_logfile_backups=5
|
10
docker/zz-docker.conf
Normal file
10
docker/zz-docker.conf
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
[global]
|
||||||
|
daemonize=no
|
||||||
|
error_log = /var/log/php_fpm_error.log
|
||||||
|
log_limit = 1048576
|
||||||
|
log_level = notice
|
||||||
|
|
||||||
|
[www]
|
||||||
|
listen = 9000
|
||||||
|
catch_workers_output = yes
|
||||||
|
decorate_workers_output = no
|
|
@ -1,6 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
require 'ncc';
|
|
||||||
import('net.nosial.socialbox');
|
|
||||||
|
|
||||||
\Socialbox\Socialbox::handleRpc();
|
|
11
project.json
11
project.json
|
@ -36,14 +36,19 @@
|
||||||
"source": "nosial/libs.config=latest@n64"
|
"source": "nosial/libs.config=latest@n64"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "net.nosial.loglib",
|
"name": "net.nosial.loglib2",
|
||||||
"version": "latest",
|
"version": "latest",
|
||||||
"source": "nosial/libs.log=latest@n64"
|
"source": "nosial/loglib2=latest@github"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "net.nosial.optslib",
|
"name": "net.nosial.optslib",
|
||||||
"version": "latest",
|
"version": "latest",
|
||||||
"source": "nosial/libs.opts=latest@n64"
|
"source": "nosial/libs.opts=latest@n64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "com.gregwar.captcha",
|
||||||
|
"version": "latest",
|
||||||
|
"source": "gregwar/captcha=latest@packagist"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"configurations": [
|
"configurations": [
|
||||||
|
@ -88,7 +93,7 @@
|
||||||
"execute": {
|
"execute": {
|
||||||
"working_directory": "%CWD%",
|
"working_directory": "%CWD%",
|
||||||
"silent": false,
|
"silent": false,
|
||||||
"tty": true,
|
"tty": false,
|
||||||
"timeout": null,
|
"timeout": null,
|
||||||
"idle_timeout": null,
|
"idle_timeout": null,
|
||||||
"target": "main"
|
"target": "main"
|
||||||
|
|
21
public/index.php
Normal file
21
public/index.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require 'ncc';
|
||||||
|
import('net.nosial.socialbox');
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
\Socialbox\Socialbox::handleRequest();
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
http_response_code(500);
|
||||||
|
|
||||||
|
if(\Socialbox\Classes\Configuration::getSecurityConfiguration()->isDisplayInternalExceptions())
|
||||||
|
{
|
||||||
|
print_r($e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print('An internal error occurred');
|
||||||
|
}
|
|
@ -69,7 +69,7 @@ abstract class CacheLayer
|
||||||
{
|
{
|
||||||
if (self::$instance === null)
|
if (self::$instance === null)
|
||||||
{
|
{
|
||||||
$engine = Configuration::getConfiguration()['cache']['engine'];
|
$engine = Configuration::getCacheConfiguration()->getEngine();
|
||||||
|
|
||||||
if ($engine === 'redis')
|
if ($engine === 'redis')
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,14 +2,10 @@
|
||||||
|
|
||||||
namespace Socialbox\Abstracts;
|
namespace Socialbox\Abstracts;
|
||||||
|
|
||||||
use Socialbox\Enums\StandardError;
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
use Socialbox\Exceptions\DatabaseOperationException;
|
|
||||||
use Socialbox\Exceptions\StandardException;
|
|
||||||
use Socialbox\Interfaces\SerializableInterface;
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
use Socialbox\Managers\SessionManager;
|
|
||||||
use Socialbox\Objects\ClientRequest;
|
use Socialbox\Objects\ClientRequest;
|
||||||
use Socialbox\Objects\RpcRequest;
|
use Socialbox\Objects\RpcRequest;
|
||||||
use Socialbox\Objects\SessionRecord;
|
|
||||||
|
|
||||||
abstract class Method
|
abstract class Method
|
||||||
{
|
{
|
||||||
|
@ -19,30 +15,7 @@ abstract class Method
|
||||||
* @param ClientRequest $request The full client request object, used to identify the client & it's requests
|
* @param ClientRequest $request The full client request object, used to identify the client & it's requests
|
||||||
* @param RpcRequest $rpcRequest The selected RPC request for the method to handle
|
* @param RpcRequest $rpcRequest The selected RPC request for the method to handle
|
||||||
* @return SerializableInterface|null Returns RpcResponse/RpcError on success, null if the request is a notification
|
* @return SerializableInterface|null Returns RpcResponse/RpcError on success, null if the request is a notification
|
||||||
* @throws StandardException If a standard exception is thrown, it will be handled by the engine.
|
* @throws StandardRpcException If a standard exception is thrown, it will be handled by the engine.
|
||||||
*/
|
*/
|
||||||
public static abstract function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface;
|
public static abstract function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ClientRequest $request The client request object
|
|
||||||
* @return SessionRecord|null Returns null if the client has not provided a Session UUID
|
|
||||||
* @throws StandardException Thrown if standard exceptions are to be thrown regarding this
|
|
||||||
*/
|
|
||||||
protected static function getSession(ClientRequest $request): ?SessionRecord
|
|
||||||
{
|
|
||||||
if($request->getSessionUuid() === null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// NOTE: If the session UUID was provided, it has already been validated up until this point
|
|
||||||
return SessionManager::getSession($request->getSessionUuid());
|
|
||||||
}
|
|
||||||
catch(DatabaseOperationException $e)
|
|
||||||
{
|
|
||||||
throw new StandardException("There was an error while retrieving the session from the server", StandardError::INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,114 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Socialbox\Classes;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
|
|
||||||
class Base32
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Array with all 32 characters for decoding from/encoding to base32.
|
|
||||||
*/
|
|
||||||
private const LOOKUP_TABLE = [
|
|
||||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7
|
|
||||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
|
|
||||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
|
|
||||||
'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
|
|
||||||
'=' // padding char
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allowed padding lengths for base32 data.
|
|
||||||
*/
|
|
||||||
private const ALLOWED_PADDING = [6, 4, 3, 1, 0];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decodes base32 data.
|
|
||||||
*
|
|
||||||
* @param string $data
|
|
||||||
* @return string
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public static function decode(string $data): string
|
|
||||||
{
|
|
||||||
if (empty($data))
|
|
||||||
{
|
|
||||||
throw new InvalidArgumentException('No data provided to decode');
|
|
||||||
}
|
|
||||||
|
|
||||||
$chars = self::LOOKUP_TABLE;
|
|
||||||
$chars_flipped = array_flip($chars);
|
|
||||||
$char_count = substr_count($data, $chars[32]);
|
|
||||||
|
|
||||||
if (!in_array($char_count, self::ALLOWED_PADDING, true))
|
|
||||||
{
|
|
||||||
throw new InvalidArgumentException(sprintf('Invalid padding in the base32 data: %s', $data));
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($i = 0; $i < 4; ++$i)
|
|
||||||
{
|
|
||||||
if ($char_count === self::ALLOWED_PADDING[$i] &&
|
|
||||||
substr($data, -self::ALLOWED_PADDING[$i]) !== str_repeat($chars[32], self::ALLOWED_PADDING[$i]))
|
|
||||||
{
|
|
||||||
throw new InvalidArgumentException(sprintf('Invalid padding in the base32 data: %s', $data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove padding characters
|
|
||||||
$data = str_replace('=', (string)null, $data);
|
|
||||||
$data = str_split($data);
|
|
||||||
$binary_string = (string)null;
|
|
||||||
|
|
||||||
for ($i = 0, $data_count = count($data); $i < $data_count; $i += 8)
|
|
||||||
{
|
|
||||||
$x = (string)null;
|
|
||||||
for ($j = 0; $j < 8; ++$j)
|
|
||||||
{
|
|
||||||
$char = $data[$i + $j] ?? null;
|
|
||||||
if ($char === null || !isset($chars_flipped[$char]))
|
|
||||||
{
|
|
||||||
throw new InvalidArgumentException(sprintf('Invalid character in the base32 data: %s', $char));
|
|
||||||
}
|
|
||||||
|
|
||||||
$x .= str_pad(base_convert((string)$chars_flipped[$char], 10, 2), 5, '0', STR_PAD_LEFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
$eight_bits = str_split($x, 8);
|
|
||||||
|
|
||||||
foreach ($eight_bits as $bits)
|
|
||||||
{
|
|
||||||
$binary_string .= ($y = chr((int)base_convert($bits, 2, 10))) !== false ? $y : (string)null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $binary_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function encode(string $data): string
|
|
||||||
{
|
|
||||||
$binaryLength = strlen($data);
|
|
||||||
$base32String = '';
|
|
||||||
|
|
||||||
$buffer = 0;
|
|
||||||
$bitsLeft = 0;
|
|
||||||
|
|
||||||
for ($i = 0; $i < $binaryLength; $i++)
|
|
||||||
{
|
|
||||||
$buffer = ($buffer << 8) | (ord($data[$i]) & 0xFF);
|
|
||||||
$bitsLeft += 8;
|
|
||||||
|
|
||||||
while ($bitsLeft >= 5)
|
|
||||||
{
|
|
||||||
$base32String .= self::LOOKUP_TABLE[($buffer >> ($bitsLeft - 5)) & 0x1F];
|
|
||||||
$bitsLeft -= 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($bitsLeft > 0)
|
|
||||||
{
|
|
||||||
$base32String .= self::LOOKUP_TABLE[($buffer << (5 - $bitsLeft)) & 0x1F];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $base32String;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,10 +22,10 @@ class MemcachedCacheLayer extends CacheLayer
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->memcached = new Memcached();
|
$this->memcached = new Memcached();
|
||||||
$this->memcached->addServer(Configuration::getConfiguration()['cache']['host'], (int)Configuration::getConfiguration()['cache']['port']);
|
$this->memcached->addServer(Configuration::getCacheConfiguration()->getHost(), Configuration::getCacheConfiguration()->getPort());
|
||||||
if(Configuration::getConfiguration()['cache']['username'] !== null || Configuration::getConfiguration()['cache']['password'] !== null)
|
if(Configuration::getCacheConfiguration()->getUsername() !== null || Configuration::getCacheConfiguration()->getPassword() !== null)
|
||||||
{
|
{
|
||||||
$this->memcached->setSaslAuthData(Configuration::getConfiguration()['cache']['username'], Configuration::getConfiguration()['cache']['password']);
|
$this->memcached->setSaslAuthData(Configuration::getCacheConfiguration()->getUsername(), Configuration::getCacheConfiguration()->getPassword());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,15 +26,15 @@ class RedisCacheLayer extends CacheLayer
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$this->redis->connect(Configuration::getConfiguration()['cache']['host'], (int)Configuration::getConfiguration()['cache']['port']);
|
$this->redis->connect(Configuration::getCacheConfiguration()->getHost(), Configuration::getCacheConfiguration()->getPort());
|
||||||
if (Configuration::getConfiguration()['cache']['password'] !== null)
|
if (Configuration::getCacheConfiguration()->getPassword() !== null)
|
||||||
{
|
{
|
||||||
$this->redis->auth(Configuration::getConfiguration()['cache']['password']);
|
$this->redis->auth(Configuration::getCacheConfiguration()->getPassword());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Configuration::getConfiguration()['cache']['database'] !== 0)
|
if (Configuration::getCacheConfiguration()->getDatabase() !== null)
|
||||||
{
|
{
|
||||||
$this->redis->select((int)Configuration::getConfiguration()['cache']['database']);
|
$this->redis->select(Configuration::getCacheConfiguration()->getDatabase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (RedisException $e)
|
catch (RedisException $e)
|
||||||
|
|
41
src/Socialbox/Classes/CliCommands/DnsRecordCommand.php
Normal file
41
src/Socialbox/Classes/CliCommands/DnsRecordCommand.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\CliCommands;
|
||||||
|
|
||||||
|
use Socialbox\Classes\Configuration;
|
||||||
|
use Socialbox\Classes\Logger;
|
||||||
|
use Socialbox\Interfaces\CliCommandInterface;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
|
||||||
|
class DnsRecordCommand implements CliCommandInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(array $args): int
|
||||||
|
{
|
||||||
|
Logger::getLogger()->info('Please set the following DNS TXT record for the domain:');
|
||||||
|
Logger::getLogger()->info(sprintf(' %s', Socialbox::getDnsRecord()));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function getHelpMessage(): string
|
||||||
|
{
|
||||||
|
return <<<HELP
|
||||||
|
Usage: socialbox dns-record
|
||||||
|
|
||||||
|
Displays the DNS TXT record that should be set for the domain.
|
||||||
|
HELP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function getShortHelpMessage(): string
|
||||||
|
{
|
||||||
|
return 'Displays the DNS TXT record that should be set for the domain.';
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,17 +3,20 @@
|
||||||
namespace Socialbox\Classes\CliCommands;
|
namespace Socialbox\Classes\CliCommands;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use LogLib\Log;
|
use ncc\CLI\Main;
|
||||||
|
use ncc\ThirdParty\Symfony\Process\Exception\InvalidArgumentException;
|
||||||
use PDOException;
|
use PDOException;
|
||||||
use Socialbox\Abstracts\CacheLayer;
|
use Socialbox\Abstracts\CacheLayer;
|
||||||
use Socialbox\Classes\Configuration;
|
use Socialbox\Classes\Configuration;
|
||||||
use Socialbox\Classes\Cryptography;
|
use Socialbox\Classes\Cryptography;
|
||||||
use Socialbox\Classes\Database;
|
use Socialbox\Classes\Database;
|
||||||
|
use Socialbox\Classes\Logger;
|
||||||
use Socialbox\Classes\Resources;
|
use Socialbox\Classes\Resources;
|
||||||
use Socialbox\Enums\DatabaseObjects;
|
use Socialbox\Enums\DatabaseObjects;
|
||||||
use Socialbox\Exceptions\DatabaseOperationException;
|
use Socialbox\Exceptions\CryptographyException;
|
||||||
use Socialbox\Interfaces\CliCommandInterface;
|
use Socialbox\Interfaces\CliCommandInterface;
|
||||||
use Socialbox\Managers\VariableManager;
|
use Socialbox\Program;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
|
||||||
class InitializeCommand implements CliCommandInterface
|
class InitializeCommand implements CliCommandInterface
|
||||||
{
|
{
|
||||||
|
@ -22,23 +25,79 @@ class InitializeCommand implements CliCommandInterface
|
||||||
*/
|
*/
|
||||||
public static function execute(array $args): int
|
public static function execute(array $args): int
|
||||||
{
|
{
|
||||||
if(Configuration::getConfiguration()['instance']['enabled'] === false && !isset($args['force']))
|
if(Configuration::getInstanceConfiguration()->isEnabled() === false && !isset($args['force']) && getenv('SB_MODE') !== 'automated')
|
||||||
|
{
|
||||||
|
$required_configurations = [
|
||||||
|
'database.host', 'database.port', 'database.username', 'database.password', 'database.name',
|
||||||
|
'instance.enabled', 'instance.domain', 'registration.*'
|
||||||
|
];
|
||||||
|
|
||||||
|
Program::getLogger()->error('Socialbox is disabled. Use --force to initialize the instance or set `instance.enabled` to True in the configuration');
|
||||||
|
Program::getLogger()->info('The reason you are required to do this is to allow you to configure the instance before enabling it');
|
||||||
|
Program::getLogger()->info('The following configurations are required to be set before enabling the instance:');
|
||||||
|
foreach($required_configurations as $config)
|
||||||
|
{
|
||||||
|
Program::getLogger()->info(sprintf(' - %s', $config));
|
||||||
|
}
|
||||||
|
|
||||||
|
Program::getLogger()->info('instance.private_key & instance.public_key are automatically generated if not set');
|
||||||
|
Program::getLogger()->info('instance.domain is required to be set to the domain name of the instance');
|
||||||
|
Program::getLogger()->info('instance.rpc_endpoint is required to be set to the publicly accessible http rpc endpoint of this server');
|
||||||
|
Program::getLogger()->info('registration.* are required to be set to allow users to register to the instance');
|
||||||
|
Program::getLogger()->info('You will be given a DNS TXT record to set for the public key after the initialization process');
|
||||||
|
Program::getLogger()->info('The configuration file can be edited using ConfigLib:');
|
||||||
|
Program::getLogger()->info(' configlib --conf socialbox -e nano');
|
||||||
|
Program::getLogger()->info('Or manually at:');
|
||||||
|
Program::getLogger()->info(sprintf(' %s', Configuration::getConfigurationLib()->getPath()));
|
||||||
|
|
||||||
|
if(getenv('SB_MODE') === 'automated')
|
||||||
|
{
|
||||||
|
// Wait & Reload the configuration
|
||||||
|
while(!Configuration::getInstanceConfiguration()->isEnabled())
|
||||||
|
{
|
||||||
|
Program::getLogger()->info('Waiting for configuration, retrying in 5 seconds...');
|
||||||
|
sleep(5);
|
||||||
|
Configuration::reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Log::info('net.nosial.socialbox', 'Socialbox is disabled. Use --force to initialize the instance or set `instance.enabled` to True in the configuration');
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::info('net.nosial.socialbox', 'Initializing Socialbox...');
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if(Configuration::getConfiguration()['cache']['enabled'])
|
// Overwrite the configuration if the automated setup procedure is detected
|
||||||
|
// This is useful for CI/CD pipelines & Docker
|
||||||
|
if(getenv('SB_MODE') === 'automated')
|
||||||
{
|
{
|
||||||
Log::verbose('net.nosial.socialbox', 'Clearing cache layer...');
|
Program::getLogger()->info('Automated Setup Procedure is detected');
|
||||||
|
self::applyEnvironmentVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Configuration::getInstanceConfiguration()->getDomain() === null)
|
||||||
|
{
|
||||||
|
Program::getLogger()->error('instance.domain is required but was not set');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Configuration::getInstanceConfiguration()->getRpcEndpoint() === null)
|
||||||
|
{
|
||||||
|
Program::getLogger()->error('instance.rpc_endpoint is required but was not set');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Program::getLogger()->info('Initializing Socialbox...');
|
||||||
|
if(Configuration::getCacheConfiguration()->isEnabled())
|
||||||
|
{
|
||||||
|
Program::getLogger()->verbose('Clearing cache layer...');
|
||||||
CacheLayer::getInstance()->clear();
|
CacheLayer::getInstance()->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(DatabaseObjects::casesOrdered() as $object)
|
foreach(DatabaseObjects::casesOrdered() as $object)
|
||||||
{
|
{
|
||||||
Log::verbose('net.nosial.socialbox', "Initializing database object {$object->value}");
|
Program::getLogger()->verbose("Initializing database object {$object->value}");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -49,45 +108,320 @@ class InitializeCommand implements CliCommandInterface
|
||||||
// Check if the error code is for "table already exists"
|
// Check if the error code is for "table already exists"
|
||||||
if ($e->getCode() === '42S01')
|
if ($e->getCode() === '42S01')
|
||||||
{
|
{
|
||||||
Log::warning('net.nosial.socialbox', "Database object {$object->value} already exists, skipping...");
|
Program::getLogger()->warning("Database object {$object->value} already exists, skipping...");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log::error('net.nosial.socialbox', "Failed to initialize database object {$object->value}: {$e->getMessage()}", $e);
|
Program::getLogger()->error("Failed to initialize database object {$object->value}: {$e->getMessage()}", $e);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception $e)
|
catch(Exception $e)
|
||||||
{
|
{
|
||||||
Log::error('net.nosial.socialbox', "Failed to initialize database object {$object->value}: {$e->getMessage()}", $e);
|
Program::getLogger()->error("Failed to initialize database object {$object->value}: {$e->getMessage()}", $e);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(
|
||||||
|
!Configuration::getCryptographyConfiguration()->getHostPublicKey() ||
|
||||||
|
!Configuration::getCryptographyConfiguration()->getHostPrivateKey() ||
|
||||||
|
!Configuration::getCryptographyConfiguration()->getHostPublicKey()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$expires = time() + 31536000;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Program::getLogger()->info('Generating new key pair (expires ' . date('Y-m-d H:i:s', $expires) . ')...');
|
||||||
|
$signingKeyPair = Cryptography::generateSigningKeyPair();
|
||||||
|
}
|
||||||
|
catch (CryptographyException $e)
|
||||||
|
{
|
||||||
|
Program::getLogger()->error('Failed to generate cryptography values', $e);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Configuration::getConfigurationLib()->set('cryptography.host_keypair_expires', $expires);
|
||||||
|
Configuration::getConfigurationLib()->set('cryptography.host_private_key', $signingKeyPair->getPrivateKey());
|
||||||
|
Configuration::getConfigurationLib()->set('cryptography.host_public_key', $signingKeyPair->getPublicKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If Internal Encryption keys are null or has less keys than configured, populate the configuration
|
||||||
|
// property with encryption keys.
|
||||||
|
if(
|
||||||
|
Configuration::getCryptographyConfiguration()->getInternalEncryptionKeys() === null ||
|
||||||
|
count(Configuration::getCryptographyConfiguration()->getInternalEncryptionKeys()) < Configuration::getCryptographyConfiguration()->getEncryptionKeysCount())
|
||||||
|
{
|
||||||
|
Program::getLogger()->info('Generating internal encryption keys...');
|
||||||
|
$encryptionKeys = Configuration::getCryptographyConfiguration()->getInternalEncryptionKeys() ?? [];
|
||||||
|
while(count($encryptionKeys) < Configuration::getCryptographyConfiguration()->getEncryptionKeysCount())
|
||||||
|
{
|
||||||
|
$encryptionKeys[] = Cryptography::generateEncryptionKey(Configuration::getCryptographyConfiguration()->getEncryptionKeysAlgorithm());
|
||||||
|
}
|
||||||
|
|
||||||
|
Configuration::getConfigurationLib()->set('cryptography.internal_encryption_keys', $encryptionKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
Program::getLogger()->info('Updating configuration...');
|
||||||
|
Configuration::getConfigurationLib()->save();
|
||||||
|
Configuration::reload();
|
||||||
|
|
||||||
|
Program::getLogger()->info('Socialbox has been initialized successfully');
|
||||||
|
Program::getLogger()->info(sprintf('Set the DNS TXT record for the domain %s to the following value:', Configuration::getInstanceConfiguration()->getDomain()));
|
||||||
|
Program::getLogger()->info(Socialbox::getDnsRecord());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies environment variables to the application's configuration system.
|
||||||
|
* This method maps predefined environment variables to their corresponding
|
||||||
|
* configuration keys, validates their values, and updates the configuration
|
||||||
|
* library accordingly. If expected environment variables are missing and
|
||||||
|
* critical for certain components, warning logs are generated.
|
||||||
|
* Additionally, the configuration changes are saved and reloaded after being applied.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function applyEnvironmentVariables(): void
|
||||||
|
{
|
||||||
|
// Always set the 'instance.enabled' to true if the automated setup procedure is detected
|
||||||
|
Configuration::getConfigurationLib()->set('instance.enabled', true);
|
||||||
|
$configurationMap = [
|
||||||
|
// Instance Configuration
|
||||||
|
'SB_INSTANCE_NAME' => 'instance.name',
|
||||||
|
'SB_INSTANCE_DOMAIN' => 'instance.domain',
|
||||||
|
'SB_INSTANCE_RPC_ENDPOINT' => 'instance.rpc_endpoint',
|
||||||
|
'SB_STORAGE_PATH' => 'storage.path',
|
||||||
|
|
||||||
|
// Logging Configuration
|
||||||
|
'SB_LOGGING_CONSOLE_ENABLED' => 'logging.console_logging_enabled',
|
||||||
|
'SB_LOGGING_CONSOLE_LEVEL' => 'logging.console_logging_level',
|
||||||
|
'SB_LOGGING_FILE_ENABLED' => 'logging.file_logging_enabled',
|
||||||
|
'SB_LOGGING_FILE_LEVEL' => 'logging.file_logging_level',
|
||||||
|
|
||||||
|
// Security & Cryptography Configuration
|
||||||
|
'SB_SECURITY_DISPLAY_INTERNAL_EXCEPTIONS' => 'security.display_internal_exceptions',
|
||||||
|
'SB_CRYPTO_KEYPAIR_EXPIRES' => 'cryptography.host_keypair_expires',
|
||||||
|
'SB_CRYPTO_ENCRYPTION_KEYS_COUNT' => 'cryptography.encryption_keys_count',
|
||||||
|
'SB_CRYPTO_ENCRYPTION_KEYS_ALGORITHM' => 'cryptography.encryption_keys_algorithm',
|
||||||
|
'SB_CRYPTO_TRANSPORT_ENCRYPTION_ALGORITHM' => 'cryptography.transport_encryption_algorithm',
|
||||||
|
|
||||||
|
// Database Configuration
|
||||||
|
'SB_DATABASE_HOST' => 'database.host',
|
||||||
|
'SB_DATABASE_PORT' => 'database.port',
|
||||||
|
'SB_DATABASE_USERNAME' => 'database.username',
|
||||||
|
'SB_DATABASE_PASSWORD' => 'database.password',
|
||||||
|
'SB_DATABASE_NAME' => 'database.name',
|
||||||
|
|
||||||
|
'SB_CACHE_ENABLED' => 'cache.enabled',
|
||||||
|
'SB_CACHE_ENGINE' => 'cache.engine',
|
||||||
|
'SB_CACHE_HOST' => 'cache.host',
|
||||||
|
'SB_CACHE_PORT' => 'cache.port',
|
||||||
|
'SB_CACHE_USERNAME' => 'cache.username',
|
||||||
|
'SB_CACHE_PASSWORD' => 'cache.password',
|
||||||
|
'SB_CACHE_DATABASE' => 'cache.database',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach($configurationMap as $env => $config)
|
||||||
|
{
|
||||||
|
$variable = getenv($env);
|
||||||
|
Program::getLogger()->info(sprintf('Checking environment variable %s...', $env));
|
||||||
|
|
||||||
|
switch($env)
|
||||||
|
{
|
||||||
|
case 'SB_STORAGE_PATH':
|
||||||
|
case 'SB_LOGGING_FILE_LEVEL':
|
||||||
|
case 'SB_LOGGING_CONSOLE_LEVEL':
|
||||||
|
case 'SB_INSTANCE_NAME':
|
||||||
|
case 'SB_CRYPTO_ENCRYPTION_KEYS_ALGORITHM':
|
||||||
|
case 'SB_CRYPTO_TRANSPORT_ENCRYPTION_ALGORITHM':
|
||||||
|
case 'SB_CACHE_ENGINE':
|
||||||
|
case 'SB_CACHE_HOST':
|
||||||
|
case 'SB_CACHE_USERNAME':
|
||||||
|
case 'SB_CACHE_PASSWORD':
|
||||||
|
case 'SB_CACHE_DATABASE':
|
||||||
|
if($variable !== false)
|
||||||
|
{
|
||||||
|
Configuration::getConfigurationLib()->set($config, $variable);
|
||||||
|
Program::getLogger()->info(sprintf('Set %s to %s', $config, $variable));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SB_INSTANCE_DOMAIN':
|
||||||
|
if($variable === false && Configuration::getInstanceConfiguration()->getDomain() === null)
|
||||||
|
{
|
||||||
|
Program::getLogger()->warning(sprintf('%s is not set, expected %s environment variable', $config, $env));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Configuration::getConfigurationLib()->set($config, $variable);
|
||||||
|
Program::getLogger()->info(sprintf('Set %s to %s', $config, $variable));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SB_DATABASE_HOST':
|
||||||
|
if($variable === false && Configuration::getDatabaseConfiguration()->getHost() === null)
|
||||||
|
{
|
||||||
|
Program::getLogger()->warning(sprintf('%s is not set, expected %s environment variable', $config, $env));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Configuration::getConfigurationLib()->set($config, $variable);
|
||||||
|
Program::getLogger()->info(sprintf('Set %s to %s', $config, $variable));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SB_DATABASE_PORT':
|
||||||
|
if($variable === false && Configuration::getDatabaseConfiguration()->getPort() === null)
|
||||||
|
{
|
||||||
|
Program::getLogger()->warning(sprintf('%s is not set, expected %s environment variable', $config, $env));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Configuration::getConfigurationLib()->set($config, (int) $variable);
|
||||||
|
Program::getLogger()->info(sprintf('Set %s to %s', $config, $variable));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SB_DATABASE_USERNAME':
|
||||||
|
if($variable === false && Configuration::getDatabaseConfiguration()->getUsername() === null)
|
||||||
|
{
|
||||||
|
Program::getLogger()->warning(sprintf('%s is not set, expected %s environment variable', $config, $env));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Configuration::getConfigurationLib()->set($config, $variable);
|
||||||
|
Program::getLogger()->info(sprintf('Set %s to %s', $config, $variable));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SB_DATABASE_PASSWORD':
|
||||||
|
if($variable === false && Configuration::getDatabaseConfiguration()->getPassword() === null)
|
||||||
|
{
|
||||||
|
Program::getLogger()->warning(sprintf('%s is not set, expected %s environment variable', $config, $env));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Configuration::getConfigurationLib()->set($config, $variable);
|
||||||
|
Program::getLogger()->info(sprintf('Set %s to %s', $config, $variable));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SB_DATABASE_NAME':
|
||||||
|
if($variable === false && Configuration::getDatabaseConfiguration()->getName() === null)
|
||||||
|
{
|
||||||
|
Program::getLogger()->warning(sprintf('%s is not set, expected %s environment variable', $config, $env));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Configuration::getConfigurationLib()->set($config, $variable);
|
||||||
|
Program::getLogger()->info(sprintf('Set %s to %s', $config, $variable));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SB_INSTANCE_RPC_ENDPOINT':
|
||||||
|
if($variable === false && Configuration::getInstanceConfiguration()->getRpcEndpoint() === null)
|
||||||
|
{
|
||||||
|
Program::getLogger()->warning(sprintf('%s is not set, expected %s environment variable', $config, $env));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Configuration::getConfigurationLib()->set($config, $variable);
|
||||||
|
Program::getLogger()->info(sprintf('Set %s to %s', $config, $variable));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SB_LOGGING_CONSOLE_ENABLED':
|
||||||
|
case 'SB_SECURITY_DISPLAY_INTERNAL_EXCEPTIONS':
|
||||||
|
case 'SB_LOGGING_FILE_ENABLED':
|
||||||
|
case 'SB_CACHE_ENABLED':
|
||||||
|
if($variable !== false)
|
||||||
|
{
|
||||||
|
Configuration::getConfigurationLib()->set($config, filter_var($variable, FILTER_VALIDATE_BOOLEAN));
|
||||||
|
Program::getLogger()->info(sprintf('Set %s to %s', $config, $variable));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SB_CRYPTO_KEYPAIR_EXPIRES':
|
||||||
|
case 'SB_CRYPTO_ENCRYPTION_KEYS_COUNT':
|
||||||
|
case 'SB_CACHE_PORT':
|
||||||
|
if($variable !== false)
|
||||||
|
{
|
||||||
|
Configuration::getConfigurationLib()->set($config, (int) $variable);
|
||||||
|
Program::getLogger()->info(sprintf('Set %s to %s', $config, $variable));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Program::getLogger()->warning("Environment variable $env is not supported");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Mock Servers environment variables (SB_INSTANCE_DNS_MOCK_*)
|
||||||
|
$mockServers = [];
|
||||||
|
foreach(self::getMockServerValues() as $mockServer)
|
||||||
|
{
|
||||||
|
$mockServer = explode(' ', $mockServer);
|
||||||
|
if(count($mockServer) !== 2)
|
||||||
|
{
|
||||||
|
Program::getLogger()->warning(sprintf('Invalid DNS Mock Server format: %s', implode(' ', $mockServer)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$domain = $mockServer[0] ?? null;
|
||||||
|
$txt = $mockServer[1] ?? null;
|
||||||
|
if($domain === null || $txt === null)
|
||||||
|
{
|
||||||
|
Program::getLogger()->warning(sprintf('Invalid DNS Mock Server format, domain or txt missing: %s', implode(' ', $mockServer)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
$mockServers[$domain] = $txt;
|
||||||
if(!VariableManager::variableExists('PUBLIC_KEY') || !VariableManager::variableExists('PRIVATE_KEY'))
|
Program::getLogger()->info(sprintf('Added Mock Server %s: %s', $domain, $txt));
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
{
|
{
|
||||||
Log::info('net.nosial.socialbox', 'Generating new key pair...');
|
Program::getLogger()->error(sprintf('Invalid TXT record format for %s', $domain), $e);
|
||||||
|
continue;
|
||||||
$keyPair = Cryptography::generateKeyPair();
|
|
||||||
VariableManager::setVariable('PUBLIC_KEY', $keyPair->getPublicKey());
|
|
||||||
VariableManager::setVariable('PRIVATE_KEY', $keyPair->getPrivateKey());
|
|
||||||
|
|
||||||
Log::info('net.nosial.socialbox', 'Set the DNS TXT record for the public key to the following value:');
|
|
||||||
Log::info('net.nosial.socialbox', "socialbox-key={$keyPair->getPublicKey()}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(DatabaseOperationException $e)
|
|
||||||
|
if(count($mockServers) > 0)
|
||||||
{
|
{
|
||||||
Log::error('net.nosial.socialbox', "Failed to generate key pair: {$e->getMessage()}", $e);
|
Program::getLogger()->info('Setting Mock Servers...');
|
||||||
return 1;
|
Configuration::getConfigurationLib()->set('instance.dns_mocks', $mockServers);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::info('net.nosial.socialbox', 'Socialbox has been initialized successfully');
|
// Apply changes & reload the configuration
|
||||||
return 0;
|
Program::getLogger()->info('Updating configuration...');
|
||||||
|
Configuration::getConfigurationLib()->save(); // Save
|
||||||
|
Configuration::reload(); // Reload
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all environment variable values that start with the prefix 'SB_INSTANCE_DNS_MOCK_'.
|
||||||
|
*
|
||||||
|
* @return array An array of environment variable values filtered by the specified prefix.
|
||||||
|
*/
|
||||||
|
private static function getMockServerValues(): array
|
||||||
|
{
|
||||||
|
// Fetch all environment variables
|
||||||
|
$envVars = getenv();
|
||||||
|
|
||||||
|
// Filter variables that start with the specified prefix
|
||||||
|
$filtered = array_filter($envVars, function ($key)
|
||||||
|
{
|
||||||
|
return str_starts_with($key, 'SB_INSTANCE_DNS_MOCK_');
|
||||||
|
}, ARRAY_FILTER_USE_KEY);
|
||||||
|
|
||||||
|
// Return only the values as an array
|
||||||
|
return array_values($filtered);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,7 +432,34 @@ class InitializeCommand implements CliCommandInterface
|
||||||
return "Initialize Command - Initializes Socialbox for first-runs\n" .
|
return "Initialize Command - Initializes Socialbox for first-runs\n" .
|
||||||
"Usage: socialbox init [arguments]\n\n" .
|
"Usage: socialbox init [arguments]\n\n" .
|
||||||
"Arguments:\n" .
|
"Arguments:\n" .
|
||||||
" --force - Forces the initialization process to run even the instance is disabled\n";
|
" --force - Forces the initialization process to run even the instance is disabled\n\n" .
|
||||||
|
"Environment Variables:\n" .
|
||||||
|
" SB_MODE - Set to 'automated' to enable automated setup procedure (Must be set to enable environment variables)\n" .
|
||||||
|
" SB_INSTANCE_DOMAIN - The domain name of the instance (eg; Socialbox)\n" .
|
||||||
|
" SB_INSTANCE_RPC_ENDPOINT - The public RPC endpoint of the instance (eg; https://rpc.teapot.com/)\n" .
|
||||||
|
" SB_STORAGE_PATH - The path to store files (default: /etc/socialbox)\n" .
|
||||||
|
" SB_LOGGING_CONSOLE_ENABLED - Enable console logging (default: true)\n" .
|
||||||
|
" SB_LOGGING_CONSOLE_LEVEL - Console logging level (default: info)\n" .
|
||||||
|
" SB_LOGGING_FILE_ENABLED - Enable file logging (default: true)\n" .
|
||||||
|
" SB_LOGGING_FILE_LEVEL - File logging level (default: error)\n" .
|
||||||
|
" SB_SECURITY_DISPLAY_INTERNAL_EXCEPTIONS - Display internal exceptions (default: false)\n" .
|
||||||
|
" SB_CRYPTO_KEYPAIR_EXPIRES - The expiration date of the key pair in Unix timestamp (default: current time + 1 year)\n" .
|
||||||
|
" SB_CRYPTO_ENCRYPTION_KEYS_COUNT - The number of internal encryption keys to generate (default: 5)\n" .
|
||||||
|
" SB_CRYPTO_ENCRYPTION_KEYS_ALGORITHM - The algorithm to use for encryption keys (default: xchacha20)\n" .
|
||||||
|
" SB_CRYPTO_TRANSPORT_ENCRYPTION_ALGORITHM - The algorithm to use for transport encryption (default: chacha20)\n" .
|
||||||
|
" SB_DATABASE_HOST - The database host (default: localhost)\n" .
|
||||||
|
" SB_DATABASE_PORT - The database port (default: 3306)\n" .
|
||||||
|
" SB_DATABASE_USERNAME - The database username (default: root)\n" .
|
||||||
|
" SB_DATABASE_PASSWORD - The database password (default: null)\n" .
|
||||||
|
" SB_DATABASE_NAME - The database name (default: socialbox)\n" .
|
||||||
|
" SB_CACHE_ENABLED - Enable cache layer (default: false)\n" .
|
||||||
|
" SB_CACHE_ENGINE - The cache engine to use (default: redis)\n" .
|
||||||
|
" SB_CACHE_HOST - The cache host (default: localhost)\n" .
|
||||||
|
" SB_CACHE_PORT - The cache port (default: 6379)\n" .
|
||||||
|
" SB_CACHE_USERNAME - The cache username (default: null)\n" .
|
||||||
|
" SB_CACHE_PASSWORD - The cache password (default: null)\n" .
|
||||||
|
" SB_CACHE_DATABASE - The cache database (default: 0)\n" .
|
||||||
|
" SB_INSTANCE_DNS_MOCK_* - Mock server environment variables, format: (<domain> <txt>), eg; SB_INSTANCE_DNS_MOCK_N64: teapot.com <txt>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,27 +2,112 @@
|
||||||
|
|
||||||
namespace Socialbox\Classes;
|
namespace Socialbox\Classes;
|
||||||
|
|
||||||
|
use Socialbox\Classes\Configuration\AuthenticationConfiguration;
|
||||||
|
use Socialbox\Classes\Configuration\CacheConfiguration;
|
||||||
|
use Socialbox\Classes\Configuration\CryptographyConfiguration;
|
||||||
|
use Socialbox\Classes\Configuration\DatabaseConfiguration;
|
||||||
|
use Socialbox\Classes\Configuration\InstanceConfiguration;
|
||||||
|
use Socialbox\Classes\Configuration\LoggingConfiguration;
|
||||||
|
use Socialbox\Classes\Configuration\PoliciesConfiguration;
|
||||||
|
use Socialbox\Classes\Configuration\RegistrationConfiguration;
|
||||||
|
use Socialbox\Classes\Configuration\SecurityConfiguration;
|
||||||
|
use Socialbox\Classes\Configuration\StorageConfiguration;
|
||||||
|
|
||||||
class Configuration
|
class Configuration
|
||||||
{
|
{
|
||||||
private static ?array $configuration = null;
|
private static ?\ConfigLib\Configuration $configuration = null;
|
||||||
|
private static ?InstanceConfiguration $instanceConfiguration = null;
|
||||||
|
private static ?SecurityConfiguration $securityConfiguration = null;
|
||||||
|
private static ?CryptographyConfiguration $cryptographyConfiguration = null;
|
||||||
|
private static ?DatabaseConfiguration $databaseConfiguration = null;
|
||||||
|
private static ?LoggingConfiguration $loggingConfiguration = null;
|
||||||
|
private static ?CacheConfiguration $cacheConfiguration = null;
|
||||||
|
private static ?RegistrationConfiguration $registrationConfiguration = null;
|
||||||
|
private static ?AuthenticationConfiguration $authenticationConfiguration = null;
|
||||||
|
private static ?PoliciesConfiguration $policiesConfiguration = null;
|
||||||
|
private static ?StorageConfiguration $storageConfiguration = null;
|
||||||
|
|
||||||
public static function getConfiguration(): array
|
/**
|
||||||
{
|
* Initializes the configuration settings for the application. This includes
|
||||||
if(self::$configuration === null)
|
* settings for the instance, security, database, cache layer, and registration.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function initializeConfiguration(): void
|
||||||
{
|
{
|
||||||
$config = new \ConfigLib\Configuration('socialbox');
|
$config = new \ConfigLib\Configuration('socialbox');
|
||||||
|
|
||||||
// False by default, requires the user to enable it.
|
// Instance configuration
|
||||||
$config->setDefault('instance.enabled', false);
|
$config->setDefault('instance.enabled', false); // False by default, requires the user to enable it.
|
||||||
|
$config->setDefault('instance.name', "Socialbox Server");
|
||||||
|
$config->setDefault('instance.domain', null);
|
||||||
|
$config->setDefault('instance.rpc_endpoint', null);
|
||||||
|
// DNS Mocking Configuration, usually used for testing purposes
|
||||||
|
// Allows the user to mock a domain to use a specific TXT record
|
||||||
|
$config->setDefault('instance.dns_mocks', []);
|
||||||
|
|
||||||
|
// Security Configuration
|
||||||
$config->setDefault('security.display_internal_exceptions', false);
|
$config->setDefault('security.display_internal_exceptions', false);
|
||||||
|
$config->setDefault('security.resolved_servers_ttl', 600);
|
||||||
|
$config->setDefault('security.captcha_ttl', 200);
|
||||||
|
// Server-side OTP Security options
|
||||||
|
// The time step in seconds for the OTP generation
|
||||||
|
// Default: 30 seconds
|
||||||
|
$config->setDefault('security.otp_secret_key_length', 32);
|
||||||
|
$config->setDefault('security.otp_time_step', 30);
|
||||||
|
// The number of digits in the OTP
|
||||||
|
$config->setDefault('security.otp_digits', 6);
|
||||||
|
// The hash algorithm to use for the OTP generation (sha1, sha256, sha512)
|
||||||
|
$config->setDefault('security.otp_hash_algorithm', 'sha512');
|
||||||
|
// The window of time steps to allow for OTP verification
|
||||||
|
$config->setDefault('security.otp_window', 1);
|
||||||
|
|
||||||
|
// Cryptography Configuration
|
||||||
|
// The Unix Timestamp for when the host's keypair should expire
|
||||||
|
// Setting this value to 0 means the keypair never expires
|
||||||
|
// Setting this value to null will automatically set the current unix timestamp + 1 year as the value
|
||||||
|
// This means at initialization, the key is automatically set to expire in a year.
|
||||||
|
$config->setDefault('cryptography.host_keypair_expires', null);
|
||||||
|
// The host's public/private keypair in base64 encoding, when null; the initialization process
|
||||||
|
// will automatically generate a new keypair
|
||||||
|
$config->setDefault('cryptography.host_public_key', null);
|
||||||
|
$config->setDefault('cryptography.host_private_key', null);
|
||||||
|
|
||||||
|
// The internal encryption keys used for encrypting data in the database when needed.
|
||||||
|
// When null, the initialization process will automatically generate a set of keys
|
||||||
|
// based on the `encryption_keys_count` and `encryption_keys_algorithm` configuration.
|
||||||
|
// This is an array of base64 encoded keys.
|
||||||
|
$config->setDefault('cryptography.internal_encryption_keys', null);
|
||||||
|
|
||||||
|
// The number of encryption keys to generate and set to `instance.encryption_keys` this will be used
|
||||||
|
// to randomly encrypt/decrypt sensitive data in the database, this includes hashes.
|
||||||
|
// The higher the number the higher performance impact it will have on the server
|
||||||
|
$config->setDefault('cryptography.encryption_keys_count', 10);
|
||||||
|
// The host's encryption algorithm, this will be used to generate a set of encryption keys
|
||||||
|
// This is for internal encryption, these keys are never shared outside this configuration.
|
||||||
|
// Recommendation: Higher security over performance
|
||||||
|
$config->setDefault('cryptography.encryption_keys_algorithm', 'xchacha20');
|
||||||
|
|
||||||
|
// The encryption algorithm to use for encrypted message transport between the client aand the server
|
||||||
|
// This is the encryption the server tells the client to use and the client must support it.
|
||||||
|
// Recommendation: Good balance between security and performance
|
||||||
|
// For universal support & performance, use aes256gcm for best performance or for best security use xchacha20
|
||||||
|
$config->setDefault('cryptography.transport_encryption_algorithm', 'chacha20');
|
||||||
|
|
||||||
|
// Database configuration
|
||||||
$config->setDefault('database.host', '127.0.0.1');
|
$config->setDefault('database.host', '127.0.0.1');
|
||||||
$config->setDefault('database.port', 3306);
|
$config->setDefault('database.port', 3306);
|
||||||
$config->setDefault('database.username', 'root');
|
$config->setDefault('database.username', 'root');
|
||||||
$config->setDefault('database.password', 'root');
|
$config->setDefault('database.password', 'root');
|
||||||
$config->setDefault('database.name', 'test');
|
$config->setDefault('database.name', 'test');
|
||||||
|
|
||||||
|
// Logging configuration
|
||||||
|
$config->setDefault('logging.console_logging_enabled', true);
|
||||||
|
$config->setDefault('logging.console_logging_level', 'info');
|
||||||
|
$config->setDefault('logging.file_logging_enabled', true);
|
||||||
|
$config->setDefault('logging.file_logging_level', 'error');
|
||||||
|
|
||||||
|
// Cache layer configuration
|
||||||
$config->setDefault('cache.enabled', false);
|
$config->setDefault('cache.enabled', false);
|
||||||
$config->setDefault('cache.engine', 'redis');
|
$config->setDefault('cache.engine', 'redis');
|
||||||
$config->setDefault('cache.host', '127.0.0.1');
|
$config->setDefault('cache.host', '127.0.0.1');
|
||||||
|
@ -30,15 +115,308 @@ class Configuration
|
||||||
$config->setDefault('cache.username', null);
|
$config->setDefault('cache.username', null);
|
||||||
$config->setDefault('cache.password', null);
|
$config->setDefault('cache.password', null);
|
||||||
$config->setDefault('cache.database', 0);
|
$config->setDefault('cache.database', 0);
|
||||||
$config->setDefault('cache.variables.enabled', true);
|
$config->setDefault('cache.sessions.enabled', true);
|
||||||
$config->setDefault('cache.variables.ttl', 3600);
|
$config->setDefault('cache.sessions.ttl', 3600);
|
||||||
$config->setDefault('cache.variables.max', 1000);
|
$config->setDefault('cache.sessions.max', 1000);
|
||||||
|
|
||||||
|
// Registration configuration
|
||||||
|
$config->setDefault('registration.enabled', true);
|
||||||
|
$config->setDefault('registration.privacy_policy_document', null);
|
||||||
|
$config->setDefault('registration.privacy_policy_date', 1734985525);
|
||||||
|
$config->setDefault('registration.accept_privacy_policy', true);
|
||||||
|
$config->setDefault('registration.terms_of_service_document', null);
|
||||||
|
$config->setDefault('registration.terms_of_service_date', 1734985525);
|
||||||
|
$config->setDefault('registration.accept_terms_of_service', true);
|
||||||
|
$config->setDefault('registration.community_guidelines_document', null);
|
||||||
|
$config->setDefault('registration.community_guidelines_date', 1734985525);
|
||||||
|
$config->setDefault('registration.accept_community_guidelines', true);
|
||||||
|
$config->setDefault('registration.password_required', true);
|
||||||
|
$config->setDefault('registration.otp_required', false);
|
||||||
|
$config->setDefault('registration.display_name_required', true);
|
||||||
|
$config->setDefault('registration.first_name_required', false);
|
||||||
|
$config->setDefault('registration.middle_name_required', false);
|
||||||
|
$config->setDefault('registration.last_name_required', false);
|
||||||
|
$config->setDefault('registration.display_picture_required', false);
|
||||||
|
$config->setDefault('registration.email_address_required', false);
|
||||||
|
$config->setDefault('registration.phone_number_required', false);
|
||||||
|
$config->setDefault('registration.birthday_required', false);
|
||||||
|
$config->setDefault('registration.url_required', false);
|
||||||
|
$config->setDefault('registration.image_captcha_verification_required', true);
|
||||||
|
|
||||||
|
// Authentication configuration
|
||||||
|
$config->setDefault('authentication.enabled', true);
|
||||||
|
$config->setDefault('authentication.image_captcha_verification_required', true);
|
||||||
|
|
||||||
|
// Server Policies
|
||||||
|
// The maximum number of signing keys a peer can register onto the server at once
|
||||||
|
$config->setDefault('policies.max_signing_keys', 20);
|
||||||
|
$config->setDefault('policies.max_contact_signing_keys', 50);
|
||||||
|
// The amount of time in seconds it takes before a session is considered expired due to inactivity
|
||||||
|
// Default: 12hours
|
||||||
|
$config->setDefault('policies.session_inactivity_expires', 43200);
|
||||||
|
// The amount of time in seconds it takes before an image captcha is considered expired due to lack of
|
||||||
|
// answer within the time-frame that the captcha was generated
|
||||||
|
// If expired; client is expected to request for a new captcha which will generate a new random answer.
|
||||||
|
$config->setDefault('policies.image_captcha_expires', 300);
|
||||||
|
// The amount of time in seconds it takes before a peer's external address is resolved again
|
||||||
|
// When a peer's external address is resolved, it is cached for this amount of time before resolving again.
|
||||||
|
// This reduces the amount of times a resolution request is made to the external server.
|
||||||
|
$config->setDefault('policies.peer_sync_interval', 3600);
|
||||||
|
// The maximum number of contacts a peer can retrieve from the server at once, if the client puts a
|
||||||
|
// value that exceeds this limit, the server will use this limit instead.
|
||||||
|
// recommendation: 100
|
||||||
|
$config->setDefault('policies.get_contacts_limit', 100);
|
||||||
|
$config->setDefault('policies.get_encryption_channel_requests_limit', 100);
|
||||||
|
$config->setDefault('policies.get_encryption_channels_limit', 100);
|
||||||
|
$config->setDefault('policies.get_encryption_channel_incoming_limit', 100);
|
||||||
|
$config->setDefault('policies.get_encryption_channel_outgoing_limit', 100);
|
||||||
|
|
||||||
|
// Default privacy states for information fields associated with the peer
|
||||||
|
$config->setDefault('policies.default_display_picture_privacy', 'PUBLIC');
|
||||||
|
$config->setDefault('policies.default_first_name_privacy', 'CONTACTS');
|
||||||
|
$config->setDefault('policies.default_middle_name_privacy', 'PRIVATE');
|
||||||
|
$config->setDefault('policies.default_last_name_privacy', 'PRIVATE');
|
||||||
|
$config->setDefault('policies.default_email_address_privacy', 'CONTACTS');
|
||||||
|
$config->setDefault('policies.default_phone_number_privacy', 'CONTACTS');
|
||||||
|
$config->setDefault('policies.default_birthday_privacy', 'PRIVATE');
|
||||||
|
$config->setDefault('policies.default_url_privacy', 'PUBLIC');
|
||||||
|
|
||||||
|
// Storage configuration
|
||||||
|
$config->setDefault('storage.path', '/etc/socialbox'); // The main path for file storage
|
||||||
|
$config->setDefault('storage.user_display_images_path', 'user_profiles'); // eg; `/etc/socialbox/user_profiles`
|
||||||
|
$config->setDefault('storage.user_display_images_max_size', 3145728); // 3MB
|
||||||
|
|
||||||
$config->save();
|
$config->save();
|
||||||
|
|
||||||
self::$configuration = $config->getConfiguration();
|
self::$configuration = $config;
|
||||||
|
self::$instanceConfiguration = new InstanceConfiguration(self::$configuration->getConfiguration()['instance']);
|
||||||
|
self::$securityConfiguration = new SecurityConfiguration(self::$configuration->getConfiguration()['security']);
|
||||||
|
self::$cryptographyConfiguration = new CryptographyConfiguration(self::$configuration->getConfiguration()['cryptography']);
|
||||||
|
self::$databaseConfiguration = new DatabaseConfiguration(self::$configuration->getConfiguration()['database']);
|
||||||
|
self::$loggingConfiguration = new LoggingConfiguration(self::$configuration->getConfiguration()['logging']);
|
||||||
|
self::$cacheConfiguration = new CacheConfiguration(self::$configuration->getConfiguration()['cache']);
|
||||||
|
self::$registrationConfiguration = new RegistrationConfiguration(self::$configuration->getConfiguration()['registration']);
|
||||||
|
self::$authenticationConfiguration = new AuthenticationConfiguration(self::$configuration->getConfiguration()['authentication']);
|
||||||
|
self::$policiesConfiguration = new PoliciesConfiguration(self::$configuration->getConfiguration()['policies']);
|
||||||
|
self::$storageConfiguration = new StorageConfiguration(self::$configuration->getConfiguration()['storage']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets all configuration instances by setting them to null and then
|
||||||
|
* reinitializes the configurations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function reload(): void
|
||||||
|
{
|
||||||
|
self::$configuration = null;
|
||||||
|
self::$instanceConfiguration = null;
|
||||||
|
self::$securityConfiguration = null;
|
||||||
|
self::$databaseConfiguration = null;
|
||||||
|
self::$loggingConfiguration = null;
|
||||||
|
self::$cacheConfiguration = null;
|
||||||
|
self::$registrationConfiguration = null;
|
||||||
|
|
||||||
|
self::initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current configuration array. If the configuration is not initialized,
|
||||||
|
* it triggers the initialization process.
|
||||||
|
*
|
||||||
|
* @return array The current configuration array.
|
||||||
|
*/
|
||||||
|
public static function getConfiguration(): array
|
||||||
|
{
|
||||||
|
if(self::$configuration === null)
|
||||||
|
{
|
||||||
|
self::initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$configuration->getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the configuration library instance.
|
||||||
|
*
|
||||||
|
* This method returns the current Configuration instance from the ConfigLib namespace.
|
||||||
|
* If the configuration has not been initialized yet, it initializes it first.
|
||||||
|
*
|
||||||
|
* @return \ConfigLib\Configuration The configuration library instance.
|
||||||
|
*/
|
||||||
|
public static function getConfigurationLib(): \ConfigLib\Configuration
|
||||||
|
{
|
||||||
|
if(self::$configuration === null)
|
||||||
|
{
|
||||||
|
self::initializeConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$configuration;
|
return self::$configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current instance configuration.
|
||||||
|
*
|
||||||
|
* @return InstanceConfiguration The current instance configuration instance.
|
||||||
|
*/
|
||||||
|
public static function getInstanceConfiguration(): InstanceConfiguration
|
||||||
|
{
|
||||||
|
if(self::$instanceConfiguration === null)
|
||||||
|
{
|
||||||
|
self::initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$instanceConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current security configuration.
|
||||||
|
*
|
||||||
|
* @return SecurityConfiguration The current security configuration instance.
|
||||||
|
*/
|
||||||
|
public static function getSecurityConfiguration(): SecurityConfiguration
|
||||||
|
{
|
||||||
|
if(self::$securityConfiguration === null)
|
||||||
|
{
|
||||||
|
self::initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$securityConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the cryptography configuration.
|
||||||
|
*
|
||||||
|
* This method returns the current CryptographyConfiguration instance.
|
||||||
|
* If the configuration has not been initialized yet, it initializes it first.
|
||||||
|
*
|
||||||
|
* @return CryptographyConfiguration|null The cryptography configuration instance or null if not available.
|
||||||
|
*/
|
||||||
|
public static function getCryptographyConfiguration(): ?CryptographyConfiguration
|
||||||
|
{
|
||||||
|
if(self::$cryptographyConfiguration === null)
|
||||||
|
{
|
||||||
|
self::initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$cryptographyConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current database configuration.
|
||||||
|
*
|
||||||
|
* @return DatabaseConfiguration The configuration settings for the database.
|
||||||
|
*/
|
||||||
|
public static function getDatabaseConfiguration(): DatabaseConfiguration
|
||||||
|
{
|
||||||
|
if(self::$databaseConfiguration === null)
|
||||||
|
{
|
||||||
|
self::initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$databaseConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current logging configuration.
|
||||||
|
*
|
||||||
|
* @return LoggingConfiguration The current logging configuration instance.
|
||||||
|
*/
|
||||||
|
public static function getLoggingConfiguration(): LoggingConfiguration
|
||||||
|
{
|
||||||
|
if(self::$loggingConfiguration === null)
|
||||||
|
{
|
||||||
|
self::initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$loggingConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current cache configuration. If the cache configuration
|
||||||
|
* has not been initialized, it will initialize it first.
|
||||||
|
*
|
||||||
|
* @return CacheConfiguration The current cache configuration instance.
|
||||||
|
*/
|
||||||
|
public static function getCacheConfiguration(): CacheConfiguration
|
||||||
|
{
|
||||||
|
if(self::$cacheConfiguration === null)
|
||||||
|
{
|
||||||
|
self::initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$cacheConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the registration configuration.
|
||||||
|
*
|
||||||
|
* This method returns the current RegistrationConfiguration instance.
|
||||||
|
* If the configuration has not been initialized yet, it initializes it first.
|
||||||
|
*
|
||||||
|
* @return RegistrationConfiguration The registration configuration instance.
|
||||||
|
*/
|
||||||
|
public static function getRegistrationConfiguration(): RegistrationConfiguration
|
||||||
|
{
|
||||||
|
if(self::$registrationConfiguration === null)
|
||||||
|
{
|
||||||
|
self::initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$registrationConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the authentication configuration.
|
||||||
|
*
|
||||||
|
* This method returns the current AuthenticationConfiguration instance.
|
||||||
|
* If the configuration has not been initialized yet, it initializes it first.
|
||||||
|
*
|
||||||
|
* @return AuthenticationConfiguration The authentication configuration instance.
|
||||||
|
*/
|
||||||
|
public static function getAuthenticationConfiguration(): AuthenticationConfiguration
|
||||||
|
{
|
||||||
|
if(self::$authenticationConfiguration === null)
|
||||||
|
{
|
||||||
|
self::initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$authenticationConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the policies configuration.
|
||||||
|
*
|
||||||
|
* This method returns the current PoliciesConfiguration instance.
|
||||||
|
* If the configuration has not been initialized yet, it initializes it first.
|
||||||
|
*
|
||||||
|
* @return PoliciesConfiguration The policies configuration instance.
|
||||||
|
*/
|
||||||
|
public static function getPoliciesConfiguration(): PoliciesConfiguration
|
||||||
|
{
|
||||||
|
if(self::$policiesConfiguration === null)
|
||||||
|
{
|
||||||
|
self::initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$policiesConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the storage configuration.
|
||||||
|
*
|
||||||
|
* This method returns the current StorageConfiguration instance.
|
||||||
|
* If the configuration has not been initialized yet, it initializes it first.
|
||||||
|
*
|
||||||
|
* @return StorageConfiguration The storage configuration instance.
|
||||||
|
*/
|
||||||
|
public static function getStorageConfiguration(): StorageConfiguration
|
||||||
|
{
|
||||||
|
if(self::$storageConfiguration === null)
|
||||||
|
{
|
||||||
|
self::initializeConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$storageConfiguration;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\Configuration;
|
||||||
|
|
||||||
|
class AuthenticationConfiguration
|
||||||
|
{
|
||||||
|
private bool $enabled;
|
||||||
|
private bool $imageCaptchaVerificationRequired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public Constructor for the AuthenticationConfiguration class
|
||||||
|
*
|
||||||
|
* @param array $data The array data configuration
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->enabled = (bool)$data['enabled'];
|
||||||
|
$this->imageCaptchaVerificationRequired = (bool)$data['image_captcha_verification_required'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isEnabled(): bool
|
||||||
|
{
|
||||||
|
return $this->enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isImageCaptchaVerificationRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->imageCaptchaVerificationRequired;
|
||||||
|
}
|
||||||
|
}
|
151
src/Socialbox/Classes/Configuration/CacheConfiguration.php
Normal file
151
src/Socialbox/Classes/Configuration/CacheConfiguration.php
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\Configuration;
|
||||||
|
|
||||||
|
class CacheConfiguration
|
||||||
|
{
|
||||||
|
private bool $enabled;
|
||||||
|
private string $engine;
|
||||||
|
private string $host;
|
||||||
|
private int $port;
|
||||||
|
private ?string $username;
|
||||||
|
private ?string $password;
|
||||||
|
private ?int $database;
|
||||||
|
|
||||||
|
private bool $sessionsEnabled;
|
||||||
|
private int $sessionsTtl;
|
||||||
|
private int $sessionsMax;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor to initialize configuration values.
|
||||||
|
*
|
||||||
|
* @param array $data An associative array containing configuration data.
|
||||||
|
* Keys include:
|
||||||
|
* - enabled (bool): Whether the feature is enabled.
|
||||||
|
* - engine (string): The engine type.
|
||||||
|
* - host (string): The host address.
|
||||||
|
* - port (int): The port number.
|
||||||
|
* - username (string|null): The username for authentication.
|
||||||
|
* - password (string|null): The password for authentication.
|
||||||
|
* - database (int|null): The database ID.
|
||||||
|
* - sessions (array): Session-specific settings. Keys include:
|
||||||
|
* - enabled (bool): Whether sessions are enabled.
|
||||||
|
* - ttl (int): Session time-to-live in seconds.
|
||||||
|
* - max (int): Maximum number of concurrent sessions.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->enabled = (bool)$data['enabled'];
|
||||||
|
$this->engine = (string)$data['engine'];
|
||||||
|
$this->host = (string)$data['host'];
|
||||||
|
$this->port = (int)$data['port'];
|
||||||
|
$this->username = $data['username'] ? (string)$data['username'] : null;
|
||||||
|
$this->password = $data['password'] ? (string)$data['password'] : null;
|
||||||
|
$this->database = $data['database'] ? (int)$data['database'] : null;
|
||||||
|
$this->sessionsEnabled = (bool)$data['sessions']['enabled'];
|
||||||
|
$this->sessionsTtl = (int)$data['sessions']['ttl'];
|
||||||
|
$this->sessionsMax = (int)$data['sessions']['max'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the feature is enabled.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if the feature is enabled, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isEnabled(): bool
|
||||||
|
{
|
||||||
|
return $this->enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the engine name.
|
||||||
|
*
|
||||||
|
* @return string Returns the name of the engine.
|
||||||
|
*/
|
||||||
|
public function getEngine(): string
|
||||||
|
{
|
||||||
|
return $this->engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the host value.
|
||||||
|
*
|
||||||
|
* @return string The host as a string.
|
||||||
|
*/
|
||||||
|
public function getHost(): string
|
||||||
|
{
|
||||||
|
return $this->host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the port value.
|
||||||
|
*
|
||||||
|
* @return int The port number.
|
||||||
|
*/
|
||||||
|
public function getPort(): int
|
||||||
|
{
|
||||||
|
return $this->port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the username value.
|
||||||
|
*
|
||||||
|
* @return string|null The username, or null if not set.
|
||||||
|
*/
|
||||||
|
public function getUsername(): ?string
|
||||||
|
{
|
||||||
|
return $this->username;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the password value.
|
||||||
|
*
|
||||||
|
* @return string|null The password as a string or null if not set.
|
||||||
|
*/
|
||||||
|
public function getPassword(): ?string
|
||||||
|
{
|
||||||
|
return $this->password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the database identifier.
|
||||||
|
*
|
||||||
|
* @return int|null The database identifier or null if not set.
|
||||||
|
*/
|
||||||
|
public function getDatabase(): ?int
|
||||||
|
{
|
||||||
|
return $this->database;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether sessions are enabled.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if sessions are enabled, otherwise false.
|
||||||
|
*/
|
||||||
|
public function isSessionsEnabled(): bool
|
||||||
|
{
|
||||||
|
return $this->sessionsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the time-to-live (TTL) value for sessions.
|
||||||
|
*
|
||||||
|
* @return int The TTL value for sessions.
|
||||||
|
*/
|
||||||
|
public function getSessionsTtl(): int
|
||||||
|
{
|
||||||
|
return $this->sessionsTtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the maximum number of sessions allowed.
|
||||||
|
*
|
||||||
|
* @return int Returns the maximum number of sessions.
|
||||||
|
*/
|
||||||
|
public function getSessionsMax(): int
|
||||||
|
{
|
||||||
|
return $this->sessionsMax;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\Configuration;
|
||||||
|
|
||||||
|
class CryptographyConfiguration
|
||||||
|
{
|
||||||
|
private ?int $hostKeyPairExpires;
|
||||||
|
private ?string $hostPublicKey;
|
||||||
|
private ?string $hostPrivateKey;
|
||||||
|
private ?array $internalEncryptionKeys;
|
||||||
|
private int $encryptionKeysCount;
|
||||||
|
private string $encryptionKeysAlgorithm;
|
||||||
|
private string $transportEncryptionAlgorithm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor to initialize encryption and transport keys from provided data.
|
||||||
|
*
|
||||||
|
* @param array $data An associative array containing key-value pairs for encryption keys, algorithms, and expiration settings.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->hostKeyPairExpires = $data['host_keypair_expires'] ?? null;
|
||||||
|
$this->hostPublicKey = $data['host_public_key'] ?? null;
|
||||||
|
$this->hostPrivateKey = $data['host_private_key'] ?? null;
|
||||||
|
$this->internalEncryptionKeys = $data['internal_encryption_keys'] ?? null;
|
||||||
|
$this->encryptionKeysCount = $data['encryption_keys_count'];
|
||||||
|
$this->encryptionKeysAlgorithm = $data['encryption_keys_algorithm'];
|
||||||
|
$this->transportEncryptionAlgorithm = $data['transport_encryption_algorithm'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the expiration timestamp of the host key pair.
|
||||||
|
*
|
||||||
|
* @return int|null The expiration timestamp of the host key pair, or null if not set.
|
||||||
|
*/
|
||||||
|
public function getHostKeyPairExpires(): ?int
|
||||||
|
{
|
||||||
|
return $this->hostKeyPairExpires;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the host's public key.
|
||||||
|
*
|
||||||
|
* @return string|null The host's public key, or null if not set.
|
||||||
|
*/
|
||||||
|
public function getHostPublicKey(): ?string
|
||||||
|
{
|
||||||
|
return $this->hostPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the private key associated with the host.
|
||||||
|
*
|
||||||
|
* @return string|null The host's private key, or null if not set.
|
||||||
|
*/
|
||||||
|
public function getHostPrivateKey(): ?string
|
||||||
|
{
|
||||||
|
return $this->hostPrivateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the internal encryption keys.
|
||||||
|
*
|
||||||
|
* @return array|null Returns an array of internal encryption keys if set, or null if no keys are available.
|
||||||
|
*/
|
||||||
|
public function getInternalEncryptionKeys(): ?array
|
||||||
|
{
|
||||||
|
return $this->internalEncryptionKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a random internal encryption key from the available set of encryption keys.
|
||||||
|
*
|
||||||
|
* @return string|null Returns a randomly selected encryption key as a string, or null if no keys are available.
|
||||||
|
*/
|
||||||
|
public function getRandomInternalEncryptionKey(): ?string
|
||||||
|
{
|
||||||
|
return $this->internalEncryptionKeys[array_rand($this->internalEncryptionKeys)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the total count of encryption keys.
|
||||||
|
*
|
||||||
|
* @return int The number of encryption keys.
|
||||||
|
*/
|
||||||
|
public function getEncryptionKeysCount(): int
|
||||||
|
{
|
||||||
|
return $this->encryptionKeysCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the algorithm used for the encryption keys.
|
||||||
|
*
|
||||||
|
* @return string The encryption keys algorithm.
|
||||||
|
*/
|
||||||
|
public function getEncryptionKeysAlgorithm(): string
|
||||||
|
{
|
||||||
|
return $this->encryptionKeysAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the transport encryption algorithm being used.
|
||||||
|
*
|
||||||
|
* @return string The transport encryption algorithm.
|
||||||
|
*/
|
||||||
|
public function getTransportEncryptionAlgorithm(): string
|
||||||
|
{
|
||||||
|
return $this->transportEncryptionAlgorithm;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\Configuration;
|
||||||
|
|
||||||
|
class DatabaseConfiguration
|
||||||
|
{
|
||||||
|
private string $host;
|
||||||
|
private int $port;
|
||||||
|
private string $username;
|
||||||
|
private ?string $password;
|
||||||
|
private string $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method to initialize properties from the provided data array.
|
||||||
|
*
|
||||||
|
* @param array $data Associative array containing the keys 'host', 'port', 'username', 'password', and 'name'.
|
||||||
|
* - 'host' (string): The host of the server.
|
||||||
|
* - 'port' (int): The port number.
|
||||||
|
* - 'username' (string): The username for authentication.
|
||||||
|
* - 'password' (string|null): The password for authentication, optional.
|
||||||
|
* - 'name' (string): The name associated with the connection or resource.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->host = (string)$data['host'];
|
||||||
|
$this->port = (int)$data['port'];
|
||||||
|
$this->username = (string)$data['username'];
|
||||||
|
$this->password = $data['password'] ? (string)$data['password'] : null;
|
||||||
|
$this->name = (string)$data['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the host value.
|
||||||
|
*
|
||||||
|
* @return string The value of the host.
|
||||||
|
*/
|
||||||
|
public function getHost(): string
|
||||||
|
{
|
||||||
|
return $this->host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the port value.
|
||||||
|
*
|
||||||
|
* @return int The value of the port.
|
||||||
|
*/
|
||||||
|
public function getPort(): int
|
||||||
|
{
|
||||||
|
return $this->port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the username value.
|
||||||
|
*
|
||||||
|
* @return string The value of the username.
|
||||||
|
*/
|
||||||
|
public function getUsername(): string
|
||||||
|
{
|
||||||
|
return $this->username;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the password value.
|
||||||
|
*
|
||||||
|
* @return string|null The value of the password, or null if not set.
|
||||||
|
*/
|
||||||
|
public function getPassword(): ?string
|
||||||
|
{
|
||||||
|
return $this->password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the name value.
|
||||||
|
*
|
||||||
|
* @return string The value of the name
|
||||||
|
*/
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs and retrieves the Data Source Name (DSN) string.
|
||||||
|
*
|
||||||
|
* @return string The DSN string for the database connection.
|
||||||
|
*/
|
||||||
|
public function getDsn(): string
|
||||||
|
{
|
||||||
|
return sprintf('mysql:host=%s;dbname=%s;port=%s;charset=utf8mb4', $this->host, $this->name, $this->port);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\Configuration;
|
||||||
|
|
||||||
|
class InstanceConfiguration
|
||||||
|
{
|
||||||
|
private bool $enabled;
|
||||||
|
private string $name;
|
||||||
|
private ?string $domain;
|
||||||
|
private ?string $rpcEndpoint;
|
||||||
|
private array $dnsMocks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor that initializes object properties with the provided data.
|
||||||
|
*
|
||||||
|
* @param array $data An associative array with keys 'enabled', 'domain', 'private_key', and 'public_key'.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->enabled = (bool)$data['enabled'];
|
||||||
|
$this->name = $data['name'];
|
||||||
|
$this->domain = $data['domain'];
|
||||||
|
$this->rpcEndpoint = $data['rpc_endpoint'];
|
||||||
|
$this->dnsMocks = $data['dns_mocks'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current object is enabled.
|
||||||
|
*
|
||||||
|
* @return bool True if the object is enabled, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isEnabled(): bool
|
||||||
|
{
|
||||||
|
return $this->enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the domain.
|
||||||
|
*
|
||||||
|
* @return string|null The domain.
|
||||||
|
*/
|
||||||
|
public function getDomain(): ?string
|
||||||
|
{
|
||||||
|
return $this->domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getRpcEndpoint(): ?string
|
||||||
|
{
|
||||||
|
return $this->rpcEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getDnsMocks(): array
|
||||||
|
{
|
||||||
|
return $this->dnsMocks;
|
||||||
|
}
|
||||||
|
}
|
121
src/Socialbox/Classes/Configuration/LoggingConfiguration.php
Normal file
121
src/Socialbox/Classes/Configuration/LoggingConfiguration.php
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\Configuration;
|
||||||
|
|
||||||
|
use LogLib\Enums\LogLevel;
|
||||||
|
|
||||||
|
class LoggingConfiguration
|
||||||
|
{
|
||||||
|
private bool $consoleLoggingEnabled;
|
||||||
|
private string $consoleLoggingLevel;
|
||||||
|
private bool $fileLoggingEnabled;
|
||||||
|
private string $fileLoggingLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a new instance of the class with the given configuration data.
|
||||||
|
*
|
||||||
|
* @param array $data An associative array containing logging configuration settings.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->consoleLoggingEnabled = (bool) $data['console_logging_enabled'];
|
||||||
|
$this->consoleLoggingLevel = $data['console_logging_level'];
|
||||||
|
$this->fileLoggingEnabled = (bool) $data['file_logging_enabled'];
|
||||||
|
$this->fileLoggingLevel = $data['file_logging_level'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if console logging is enabled.
|
||||||
|
*
|
||||||
|
* @return bool True if console logging is enabled, otherwise false.
|
||||||
|
*/
|
||||||
|
public function isConsoleLoggingEnabled(): bool
|
||||||
|
{
|
||||||
|
return $this->consoleLoggingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the logging level for console output.
|
||||||
|
*
|
||||||
|
* @return LogLevel The logging level configured for console output.
|
||||||
|
*/
|
||||||
|
public function getConsoleLoggingLevel(): LogLevel
|
||||||
|
{
|
||||||
|
return $this->parseLogLevel($this->consoleLoggingLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if file logging is enabled.
|
||||||
|
*
|
||||||
|
* @return bool True if file logging is enabled, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isFileLoggingEnabled(): bool
|
||||||
|
{
|
||||||
|
return $this->fileLoggingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the logging level for file logging.
|
||||||
|
*
|
||||||
|
* @return LogLevel The logging level set for file logging.
|
||||||
|
*/
|
||||||
|
public function getFileLoggingLevel(): LogLevel
|
||||||
|
{
|
||||||
|
return $this->parseLogLevel($this->fileLoggingLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given log level from string format to a LogLevel enumeration.
|
||||||
|
*
|
||||||
|
* @param string $logLevel The log level as a string.
|
||||||
|
* @return LogLevel The corresponding LogLevel enumeration.
|
||||||
|
*/
|
||||||
|
private function parseLogLevel(string $logLevel): LogLevel
|
||||||
|
{
|
||||||
|
switch (strtolower($logLevel))
|
||||||
|
{
|
||||||
|
case LogLevel::DEBUG:
|
||||||
|
case 'debug':
|
||||||
|
case '6':
|
||||||
|
case 'dbg':
|
||||||
|
return LogLevel::DEBUG;
|
||||||
|
|
||||||
|
case LogLevel::VERBOSE:
|
||||||
|
case 'verbose':
|
||||||
|
case '5':
|
||||||
|
case 'vrb':
|
||||||
|
return LogLevel::VERBOSE;
|
||||||
|
|
||||||
|
default:
|
||||||
|
case LogLevel::INFO:
|
||||||
|
case 'info':
|
||||||
|
case '4':
|
||||||
|
case 'inf':
|
||||||
|
return LogLevel::INFO;
|
||||||
|
|
||||||
|
case LogLevel::WARNING:
|
||||||
|
case 'warning':
|
||||||
|
case '3':
|
||||||
|
case 'wrn':
|
||||||
|
return LogLevel::WARNING;
|
||||||
|
case LogLevel::ERROR:
|
||||||
|
case 'error':
|
||||||
|
case '2':
|
||||||
|
case 'err':
|
||||||
|
return LogLevel::ERROR;
|
||||||
|
|
||||||
|
case LogLevel::FATAL:
|
||||||
|
case 'fatal':
|
||||||
|
case '1':
|
||||||
|
case 'crt':
|
||||||
|
return LogLevel::FATAL;
|
||||||
|
|
||||||
|
case LogLevel::SILENT:
|
||||||
|
case 'silent':
|
||||||
|
case '0':
|
||||||
|
case 'sil':
|
||||||
|
return LogLevel::SILENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
240
src/Socialbox/Classes/Configuration/PoliciesConfiguration.php
Normal file
240
src/Socialbox/Classes/Configuration/PoliciesConfiguration.php
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\Configuration;
|
||||||
|
|
||||||
|
use Socialbox\Enums\PrivacyState;
|
||||||
|
|
||||||
|
class PoliciesConfiguration
|
||||||
|
{
|
||||||
|
private int $maxSigningKeys;
|
||||||
|
private int $maxContactSigningKeys;
|
||||||
|
private int $sessionInactivityExpires;
|
||||||
|
private int $imageCaptchaExpires;
|
||||||
|
private int $peerSyncInterval;
|
||||||
|
private int $getContactsLimit;
|
||||||
|
private int $getEncryptionChannelRequestsLimit;
|
||||||
|
private int $getEncryptionChannelsLimit;
|
||||||
|
private int $getEncryptionChannelIncomingLimit;
|
||||||
|
private int $getEncryptionChannelOutgoingLimit;
|
||||||
|
private PrivacyState $defaultDisplayPicturePrivacy;
|
||||||
|
private PrivacyState $defaultFirstNamePrivacy;
|
||||||
|
private PrivacyState $defaultMiddleNamePrivacy;
|
||||||
|
private PrivacyState $defaultLastNamePrivacy;
|
||||||
|
private PrivacyState $defaultEmailAddressPrivacy;
|
||||||
|
private PrivacyState $defaultPhoneNumberPrivacy;
|
||||||
|
private PrivacyState $defaultBirthdayPrivacy;
|
||||||
|
private PrivacyState $defaultUrlPrivacy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method for initializing the policies configuration
|
||||||
|
*
|
||||||
|
* @param array $data An associative array containing the following keys:
|
||||||
|
* 'max_signing_keys', 'session_inactivity_expires',
|
||||||
|
* 'image_captcha_expires', 'peer_sync_interval',
|
||||||
|
* 'get_contacts_limit', 'default_display_picture_privacy',
|
||||||
|
* 'default_first_name_privacy', 'default_middle_name_privacy',
|
||||||
|
* 'default_last_name_privacy', 'default_email_address_privacy',
|
||||||
|
* 'default_phone_number_privacy', 'default_birthday_privacy',
|
||||||
|
* 'default_url_privacy'.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->maxSigningKeys = $data['max_signing_keys'];
|
||||||
|
$this->maxContactSigningKeys = $data['max_contact_signing_keys'];
|
||||||
|
$this->sessionInactivityExpires = $data['session_inactivity_expires'];
|
||||||
|
$this->imageCaptchaExpires = $data['image_captcha_expires'];
|
||||||
|
$this->peerSyncInterval = $data['peer_sync_interval'];
|
||||||
|
$this->getContactsLimit = $data['get_contacts_limit'];
|
||||||
|
$this->getEncryptionChannelRequestsLimit = $data['get_encryption_channel_requests_limit'];
|
||||||
|
$this->getEncryptionChannelsLimit = $data['get_encryption_channels_limit'];
|
||||||
|
$this->getEncryptionChannelIncomingLimit = $data['get_encryption_channel_incoming_limit'];
|
||||||
|
$this->getEncryptionChannelOutgoingLimit = $data['get_encryption_channel_outgoing_limit'];
|
||||||
|
$this->defaultDisplayPicturePrivacy = PrivacyState::tryFrom($data['default_display_picture_privacy']) ?? PrivacyState::PRIVATE;
|
||||||
|
$this->defaultFirstNamePrivacy = PrivacyState::tryFrom($data['default_first_name_privacy']) ?? PrivacyState::PRIVATE;
|
||||||
|
$this->defaultMiddleNamePrivacy = PrivacyState::tryFrom($data['default_middle_name_privacy']) ?? PrivacyState::PRIVATE;
|
||||||
|
$this->defaultLastNamePrivacy = PrivacyState::tryFrom($data['default_last_name_privacy']) ?? PrivacyState::PRIVATE;
|
||||||
|
$this->defaultEmailAddressPrivacy = PrivacyState::tryFrom($data['default_email_address_privacy']) ?? PrivacyState::PRIVATE;
|
||||||
|
$this->defaultPhoneNumberPrivacy = PrivacyState::tryFrom($data['default_phone_number_privacy']) ?? PrivacyState::PRIVATE;
|
||||||
|
$this->defaultBirthdayPrivacy = PrivacyState::tryFrom($data['default_birthday_privacy']) ?? PrivacyState::PRIVATE;
|
||||||
|
$this->defaultUrlPrivacy = PrivacyState::tryFrom($data['default_url_privacy']) ?? PrivacyState::PRIVATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum amount of signing keys a peer can register with the server at once
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getMaxSigningKeys(): int
|
||||||
|
{
|
||||||
|
return $this->maxSigningKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMaxContactSigningKeys(): int
|
||||||
|
{
|
||||||
|
return $this->maxContactSigningKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum amount of seconds before the session is considered expired due to inactivity
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getSessionInactivityExpires(): int
|
||||||
|
{
|
||||||
|
return $this->sessionInactivityExpires;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum amount of seconds before a captcha is considered expired due to the amount of time
|
||||||
|
* that has passed since the answer was generated, if a user fails to answer the captcha during the time
|
||||||
|
* period then the user must request for a new captcha with a new answer.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getImageCaptchaExpires(): int
|
||||||
|
{
|
||||||
|
return $this->imageCaptchaExpires;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum amount of seconds before the external peer resolve cache is considered expired
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getPeerSyncInterval(): int
|
||||||
|
{
|
||||||
|
return $this->peerSyncInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum amount of contacts that can be retrieved in a single request
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getGetContactsLimit(): int
|
||||||
|
{
|
||||||
|
return $this->getContactsLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum number of encryption channel requests that can be retrieved in a single request
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getEncryptionChannelRequestsLimit(): int
|
||||||
|
{
|
||||||
|
return $this->getEncryptionChannelRequestsLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum number of encryption channels that can be retrieved in a single request
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getEncryptionChannelsLimit(): int
|
||||||
|
{
|
||||||
|
return $this->getEncryptionChannelsLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum number of incoming encryption channels that can be retrieved in a single request
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getEncryptionChannelIncomingLimit(): int
|
||||||
|
{
|
||||||
|
return $this->getEncryptionChannelIncomingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum number of outgoing encryption channels that can be retrieved in a single request
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getEncryptionChannelOutgoingLimit(): int
|
||||||
|
{
|
||||||
|
return $this->getEncryptionChannelOutgoingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default privacy state for the display picture
|
||||||
|
*
|
||||||
|
* @return PrivacyState
|
||||||
|
*/
|
||||||
|
public function getDefaultDisplayPicturePrivacy(): PrivacyState
|
||||||
|
{
|
||||||
|
return $this->defaultDisplayPicturePrivacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default privacy state for the first name
|
||||||
|
*
|
||||||
|
* @return PrivacyState
|
||||||
|
*/
|
||||||
|
public function getDefaultFirstNamePrivacy(): PrivacyState
|
||||||
|
{
|
||||||
|
return $this->defaultFirstNamePrivacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default privacy state for the middle name
|
||||||
|
*
|
||||||
|
* @return PrivacyState
|
||||||
|
*/
|
||||||
|
public function getDefaultMiddleNamePrivacy(): PrivacyState
|
||||||
|
{
|
||||||
|
return $this->defaultMiddleNamePrivacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default privacy state for the last name
|
||||||
|
*
|
||||||
|
* @return PrivacyState
|
||||||
|
*/
|
||||||
|
public function getDefaultLastNamePrivacy(): PrivacyState
|
||||||
|
{
|
||||||
|
return $this->defaultLastNamePrivacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default privacy state for the email address
|
||||||
|
*
|
||||||
|
* @return PrivacyState
|
||||||
|
*/
|
||||||
|
public function getDefaultEmailAddressPrivacy(): PrivacyState
|
||||||
|
{
|
||||||
|
return $this->defaultEmailAddressPrivacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default privacy state for the phone number
|
||||||
|
*
|
||||||
|
* @return PrivacyState
|
||||||
|
*/
|
||||||
|
public function getDefaultPhoneNumberPrivacy(): PrivacyState
|
||||||
|
{
|
||||||
|
return $this->defaultPhoneNumberPrivacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default privacy state for the birthday
|
||||||
|
*
|
||||||
|
* @return PrivacyState
|
||||||
|
*/
|
||||||
|
public function getDefaultBirthdayPrivacy(): PrivacyState
|
||||||
|
{
|
||||||
|
return $this->defaultBirthdayPrivacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default privacy state for the URL
|
||||||
|
*
|
||||||
|
* @return PrivacyState
|
||||||
|
*/
|
||||||
|
public function getDefaultUrlPrivacy(): PrivacyState
|
||||||
|
{
|
||||||
|
return $this->defaultUrlPrivacy;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,286 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\Configuration;
|
||||||
|
|
||||||
|
class RegistrationConfiguration
|
||||||
|
{
|
||||||
|
private bool $registrationEnabled;
|
||||||
|
private ?string $privacyPolicyDocument;
|
||||||
|
private int $privacyPolicyDate;
|
||||||
|
private bool $acceptPrivacyPolicy;
|
||||||
|
private ?string $termsOfServiceDocument;
|
||||||
|
private int $termsOfServiceDate;
|
||||||
|
private bool $acceptTermsOfService;
|
||||||
|
private ?string $communityGuidelinesDocument;
|
||||||
|
private int $communityGuidelinesDate;
|
||||||
|
private bool $acceptCommunityGuidelines;
|
||||||
|
private bool $passwordRequired;
|
||||||
|
private bool $otpRequired;
|
||||||
|
private bool $displayNameRequired;
|
||||||
|
private bool $firstNameRequired;
|
||||||
|
private bool $middleNameRequired;
|
||||||
|
private bool $lastNameRequired;
|
||||||
|
private bool $displayPictureRequired;
|
||||||
|
private bool $emailAddressRequired;
|
||||||
|
private bool $phoneNumberRequired;
|
||||||
|
private bool $birthdayRequired;
|
||||||
|
private bool $urlRequired;
|
||||||
|
private bool $imageCaptchaVerificationRequired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method for initializing verification requirements.
|
||||||
|
*
|
||||||
|
* @param array $data An associative array containing the following keys:
|
||||||
|
* 'registration_enabled', 'password_required',
|
||||||
|
* 'otp_required', 'display_name_required',
|
||||||
|
* 'email_verification_required', 'sms_verification_required',
|
||||||
|
* 'phone_call_verification_required', 'image_captcha_verification_required'.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->registrationEnabled = (bool)$data['enabled'];
|
||||||
|
$this->privacyPolicyDocument = $data['privacy_policy_document'] ?? null;
|
||||||
|
$this->privacyPolicyDate = $data['privacy_policy_date'] ?? 0;
|
||||||
|
$this->acceptPrivacyPolicy = $data['accept_privacy_policy'] ?? true;
|
||||||
|
$this->termsOfServiceDocument = $data['terms_of_service_document'] ?? null;
|
||||||
|
$this->termsOfServiceDate = $data['terms_of_service_date'] ?? 0;
|
||||||
|
$this->acceptTermsOfService = $data['accept_terms_of_service'] ?? true;
|
||||||
|
$this->communityGuidelinesDocument = $data['community_guidelines_document'] ?? null;
|
||||||
|
$this->communityGuidelinesDate = $data['community_guidelines_date'] ?? 0;
|
||||||
|
$this->acceptCommunityGuidelines = $data['accept_community_guidelines'] ?? true;
|
||||||
|
$this->passwordRequired = (bool)$data['password_required'];
|
||||||
|
$this->otpRequired = (bool)$data['otp_required'];
|
||||||
|
$this->displayNameRequired = (bool)$data['display_name_required'];
|
||||||
|
$this->firstNameRequired = (bool)$data['first_name_required'];
|
||||||
|
$this->middleNameRequired = (bool)$data['middle_name_required'];
|
||||||
|
$this->lastNameRequired = (bool)$data['last_name_required'];
|
||||||
|
$this->displayPictureRequired = (bool)$data['display_picture_required'];
|
||||||
|
$this->emailAddressRequired = (bool)$data['email_address_required'];
|
||||||
|
$this->phoneNumberRequired = (bool)$data['phone_number_required'];
|
||||||
|
$this->birthdayRequired = (bool)$data['birthday_required'];
|
||||||
|
$this->urlRequired = (bool)$data['url_required'];
|
||||||
|
$this->imageCaptchaVerificationRequired = (bool)$data['image_captcha_verification_required'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the registration is enabled.
|
||||||
|
*
|
||||||
|
* @return bool True if registration is enabled, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isRegistrationEnabled(): bool
|
||||||
|
{
|
||||||
|
return $this->registrationEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the privacy policy document.
|
||||||
|
*
|
||||||
|
* @return ?string Returns the privacy policy document or null if not set.
|
||||||
|
*/
|
||||||
|
public function getPrivacyPolicyDocument(): ?string
|
||||||
|
{
|
||||||
|
return $this->privacyPolicyDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the date of the privacy policy.
|
||||||
|
*
|
||||||
|
* @return int Returns the date of the privacy policy.
|
||||||
|
*/
|
||||||
|
public function getPrivacyPolicyDate(): int
|
||||||
|
{
|
||||||
|
return $this->privacyPolicyDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if accepting the privacy policy is required.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if the privacy policy must be accepted, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isAcceptPrivacyPolicyRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->acceptPrivacyPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the terms of service document.
|
||||||
|
*
|
||||||
|
* @return ?string Returns the terms of service document or null if not set.
|
||||||
|
*/
|
||||||
|
public function getTermsOfServiceDocument(): ?string
|
||||||
|
{
|
||||||
|
return $this->termsOfServiceDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the date of the terms of service.
|
||||||
|
*
|
||||||
|
* @return int Returns the date of the terms of service.
|
||||||
|
*/
|
||||||
|
public function getTermsOfServiceDate(): int
|
||||||
|
{
|
||||||
|
return $this->termsOfServiceDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if accepting the terms of service is required.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if the terms of service must be accepted, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isAcceptTermsOfServiceRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->acceptTermsOfService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the community guidelines document.
|
||||||
|
*
|
||||||
|
* @return ?string Returns the community guidelines document or null if not set.
|
||||||
|
*/
|
||||||
|
public function getCommunityGuidelinesDocument(): ?string
|
||||||
|
{
|
||||||
|
return $this->communityGuidelinesDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the date of the community guidelines.
|
||||||
|
*
|
||||||
|
* @return int Returns the date of the community guidelines.
|
||||||
|
*/
|
||||||
|
public function getCommunityGuidelinesDate(): int
|
||||||
|
{
|
||||||
|
return $this->communityGuidelinesDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if accepting the community guidelines is required.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if the community guidelines must be accepted, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isAcceptCommunityGuidelinesRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->acceptCommunityGuidelines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a password is required.
|
||||||
|
*
|
||||||
|
* @return bool True if a password is required, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isPasswordRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->passwordRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if OTP (One-Time Password) is required.
|
||||||
|
*
|
||||||
|
* @return bool True if OTP is required, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isOtpRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->otpRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a display name is required.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if a display name is required, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isDisplayNameRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->displayNameRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a first name is required.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if a first name is required, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isFirstNameRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->firstNameRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a middle name is required.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if a middle name is required, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isMiddleNameRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->middleNameRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a last name is required.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if a last name is required, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isLastNameRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->lastNameRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a display picture is required.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if a display picture is required, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isDisplayPictureRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->displayPictureRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether an email address is required.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if an email address is required, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isEmailAddressRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->emailAddressRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a phone number is required.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if a phone number is required, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isPhoneNumberRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->phoneNumberRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a birthday is required.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if a birthday is required, otherwise false.
|
||||||
|
*/
|
||||||
|
public function isBirthdayRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->birthdayRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a URL is required.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if a URL is required, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isUrlRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->urlRequired;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if image CAPTCHA verification is required.
|
||||||
|
*
|
||||||
|
* @return bool Returns true if image CAPTCHA verification is required, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isImageCaptchaVerificationRequired(): bool
|
||||||
|
{
|
||||||
|
return $this->imageCaptchaVerificationRequired;
|
||||||
|
}
|
||||||
|
}
|
114
src/Socialbox/Classes/Configuration/SecurityConfiguration.php
Normal file
114
src/Socialbox/Classes/Configuration/SecurityConfiguration.php
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\Configuration;
|
||||||
|
|
||||||
|
class SecurityConfiguration
|
||||||
|
{
|
||||||
|
private bool $displayInternalExceptions;
|
||||||
|
private int $resolvedServersTtl;
|
||||||
|
private int $captchaTtl;
|
||||||
|
private int $otpSecretKeyLength;
|
||||||
|
private int $otpTimeStep;
|
||||||
|
private int $otpDigits;
|
||||||
|
private string $otpHashAlgorithm;
|
||||||
|
private int $otpWindow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method for initializing class properties.
|
||||||
|
*
|
||||||
|
* @param array $data An associative array containing values for initializing the properties.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->displayInternalExceptions = $data['display_internal_exceptions'];
|
||||||
|
$this->resolvedServersTtl = $data['resolved_servers_ttl'];
|
||||||
|
$this->captchaTtl = $data['captcha_ttl'];
|
||||||
|
$this->otpSecretKeyLength = $data['otp_secret_key_length'];
|
||||||
|
$this->otpTimeStep = $data['otp_time_step'];
|
||||||
|
$this->otpDigits = $data['otp_digits'];
|
||||||
|
$this->otpHashAlgorithm = $data['otp_hash_algorithm'];
|
||||||
|
$this->otpWindow = $data['otp_window'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the display of internal errors is enabled.
|
||||||
|
*
|
||||||
|
* @return bool True if the display of internal errors is enabled, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isDisplayInternalExceptions(): bool
|
||||||
|
{
|
||||||
|
return $this->displayInternalExceptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the time-to-live (TTL) value for resolved servers.
|
||||||
|
*
|
||||||
|
* @return int The TTL value for resolved servers.
|
||||||
|
*/
|
||||||
|
public function getResolvedServersTtl(): int
|
||||||
|
{
|
||||||
|
return $this->resolvedServersTtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the time-to-live (TTL) value for captchas.
|
||||||
|
*
|
||||||
|
* @return int The TTL value for captchas.
|
||||||
|
*/
|
||||||
|
public function getCaptchaTtl(): int
|
||||||
|
{
|
||||||
|
return $this->captchaTtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the length of the secret key used for OTP generation.
|
||||||
|
*
|
||||||
|
* @return int The length of the secret key used for OTP generation.
|
||||||
|
*/
|
||||||
|
public function getOtpSecretKeyLength(): int
|
||||||
|
{
|
||||||
|
return $this->otpSecretKeyLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the time step value for OTP generation.
|
||||||
|
*
|
||||||
|
* @return int The time step value for OTP generation.
|
||||||
|
*/
|
||||||
|
public function getOtpTimeStep(): int
|
||||||
|
{
|
||||||
|
return $this->otpTimeStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the number of digits in the OTP.
|
||||||
|
*
|
||||||
|
* @return int The number of digits in the OTP.
|
||||||
|
*/
|
||||||
|
public function getOtpDigits(): int
|
||||||
|
{
|
||||||
|
return $this->otpDigits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the hash algorithm used for OTP generation.
|
||||||
|
*
|
||||||
|
* @return string The hash algorithm used for OTP generation.
|
||||||
|
*/
|
||||||
|
public function getOtpHashAlgorithm(): string
|
||||||
|
{
|
||||||
|
return $this->otpHashAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the window value for OTP generation.
|
||||||
|
*
|
||||||
|
* @return int The window value for OTP generation.
|
||||||
|
*/
|
||||||
|
public function getOtpWindow(): int
|
||||||
|
{
|
||||||
|
return $this->otpWindow;
|
||||||
|
}
|
||||||
|
}
|
52
src/Socialbox/Classes/Configuration/StorageConfiguration.php
Normal file
52
src/Socialbox/Classes/Configuration/StorageConfiguration.php
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\Configuration;
|
||||||
|
|
||||||
|
class StorageConfiguration
|
||||||
|
{
|
||||||
|
private string $path;
|
||||||
|
private string $userDisplayImagesPath;
|
||||||
|
private int $userDisplayImagesMaxSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method to initialize the class properties with provided data.
|
||||||
|
*
|
||||||
|
* @param array $data An associative array containing configuration values
|
||||||
|
*/
|
||||||
|
public function __construct(array $data)
|
||||||
|
{
|
||||||
|
$this->path = $data['path'];
|
||||||
|
$this->userDisplayImagesPath = $data['user_display_images_path'];
|
||||||
|
$this->userDisplayImagesMaxSize = $data['user_display_images_max_size'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the base path value.
|
||||||
|
*
|
||||||
|
* @return string The base path.
|
||||||
|
*/
|
||||||
|
public function getPath(): string
|
||||||
|
{
|
||||||
|
return $this->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the path for user display images.
|
||||||
|
*
|
||||||
|
* @return string The path where user display images are stored.
|
||||||
|
*/
|
||||||
|
public function getUserDisplayImagesPath(): string
|
||||||
|
{
|
||||||
|
return $this->path . DIRECTORY_SEPARATOR . $this->userDisplayImagesPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the maximum size allowed for user display images.
|
||||||
|
*
|
||||||
|
* @return int The maximum size in bytes.
|
||||||
|
*/
|
||||||
|
public function getUserDisplayImagesMaxSize(): int
|
||||||
|
{
|
||||||
|
return $this->userDisplayImagesMaxSize;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -2,10 +2,9 @@
|
||||||
|
|
||||||
namespace Socialbox\Classes;
|
namespace Socialbox\Classes;
|
||||||
|
|
||||||
use mysqli;
|
|
||||||
use mysqli_sql_exception;
|
|
||||||
use PDO;
|
use PDO;
|
||||||
use PDOException;
|
use PDOException;
|
||||||
|
use Socialbox\Classes\Configuration\DatabaseConfiguration;
|
||||||
use Socialbox\Exceptions\DatabaseOperationException;
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
|
||||||
class Database
|
class Database
|
||||||
|
@ -20,10 +19,11 @@ class Database
|
||||||
{
|
{
|
||||||
if (self::$instance === null)
|
if (self::$instance === null)
|
||||||
{
|
{
|
||||||
|
$dsn = Configuration::getDatabaseConfiguration()->getDsn();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$dsn = 'mysql:host=127.0.0.1;dbname=socialbox;port=3306;charset=utf8mb4';
|
self::$instance = new PDO($dsn, Configuration::getDatabaseConfiguration()->getUsername(), Configuration::getDatabaseConfiguration()->getPassword());
|
||||||
self::$instance = new PDO($dsn, 'root', 'root');
|
|
||||||
|
|
||||||
// Set some common PDO attributes for better error handling
|
// Set some common PDO attributes for better error handling
|
||||||
self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
@ -31,7 +31,7 @@ class Database
|
||||||
}
|
}
|
||||||
catch (PDOException $e)
|
catch (PDOException $e)
|
||||||
{
|
{
|
||||||
throw new DatabaseOperationException('Failed to connect to the database', $e);
|
throw new DatabaseOperationException('Failed to connect to the database using ' . $dsn, $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
41
src/Socialbox/Classes/DnsHelper.php
Normal file
41
src/Socialbox/Classes/DnsHelper.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Objects\DnsRecord;
|
||||||
|
|
||||||
|
class DnsHelper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Generates a TXT formatted string containing the provided RPC endpoint, public key, and expiration time.
|
||||||
|
*
|
||||||
|
* @param string $rpcEndpoint The RPC endpoint to include in the TXT string.
|
||||||
|
* @param string $publicKey The public key to include in the TXT string.
|
||||||
|
* @param int $expirationTime The expiration time in seconds to include in the TXT string.
|
||||||
|
*
|
||||||
|
* @return string A formatted TXT string containing the input data.
|
||||||
|
*/
|
||||||
|
public static function generateTxt(string $rpcEndpoint, string $publicKey, int $expirationTime): string
|
||||||
|
{
|
||||||
|
return sprintf('v=socialbox;sb-rpc=%s;sb-key=%s;sb-exp=%d', $rpcEndpoint, $publicKey, $expirationTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a TXT record string and extracts its components into a DnsRecord object.
|
||||||
|
*
|
||||||
|
* @param string $txtRecord The TXT record string to be parsed.
|
||||||
|
* @return DnsRecord The extracted DnsRecord object containing the RPC endpoint, public key, and expiration time.
|
||||||
|
* @throws InvalidArgumentException If the TXT record format is invalid.
|
||||||
|
*/
|
||||||
|
public static function parseTxt(string $txtRecord): DnsRecord
|
||||||
|
{
|
||||||
|
$pattern = '/v=socialbox;sb-rpc=(?P<rpcEndpoint>https?:\/\/[^;]+);sb-key=(?P<publicSigningKey>[^;]+);sb-exp=(?P<expirationTime>\d+)/';
|
||||||
|
if (preg_match($pattern, $txtRecord, $matches))
|
||||||
|
{
|
||||||
|
return new DnsRecord($matches['rpcEndpoint'], $matches['publicSigningKey'], (int)$matches['expirationTime']);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidArgumentException('Invalid TXT record format.');
|
||||||
|
}
|
||||||
|
}
|
23
src/Socialbox/Classes/Logger.php
Normal file
23
src/Socialbox/Classes/Logger.php
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes;
|
||||||
|
|
||||||
|
|
||||||
|
class Logger
|
||||||
|
{
|
||||||
|
private static ?\LogLib2\Logger $logger = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \LogLib2\Logger
|
||||||
|
*/
|
||||||
|
public static function getLogger(): \LogLib2\Logger
|
||||||
|
{
|
||||||
|
if(self::$logger === null)
|
||||||
|
{
|
||||||
|
self::$logger = new \LogLib2\Logger(Configuration::getInstanceConfiguration()->getName());
|
||||||
|
\LogLib2\Logger::registerHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$logger;
|
||||||
|
}
|
||||||
|
}
|
132
src/Socialbox/Classes/OtpCryptography.php
Normal file
132
src/Socialbox/Classes/OtpCryptography.php
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes;
|
||||||
|
|
||||||
|
use Random\RandomException;
|
||||||
|
use Socialbox\Exceptions\CryptographyException;
|
||||||
|
|
||||||
|
class OtpCryptography
|
||||||
|
{
|
||||||
|
private const string URI_FORMAT = 'otpauth://totp/%s?secret=%s%s&algorithm=%s&digits=%d&period=%d';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random secret key of the specified length.
|
||||||
|
*
|
||||||
|
* @param int $length The length of the secret key in bytes. Default is 32.
|
||||||
|
* @return string Returns the generated secret key as a hexadecimal string.
|
||||||
|
* @throws CryptographyException If the length is less than or equal to 0.
|
||||||
|
* @throws RandomException If an error occurs while generating random bytes.
|
||||||
|
*/
|
||||||
|
public static function generateSecretKey(int $length=32): string
|
||||||
|
{
|
||||||
|
if($length <= 0)
|
||||||
|
{
|
||||||
|
throw new CryptographyException("Invalid secret key length: must be greater than 0.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return bin2hex(random_bytes($length));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a one-time password (OTP) based on the provided parameters.
|
||||||
|
*
|
||||||
|
* @param string $secretKey The secret key used to generate the OTP.
|
||||||
|
* @param int $timeStep The time step in seconds used for OTP generation. Default is 30 seconds.
|
||||||
|
* @param int $digits The number of digits in the OTP. Default is 6.
|
||||||
|
* @param int|null $counter Optional counter value. If not provided, it is calculated based on the current time and time step.
|
||||||
|
* @param string $hashAlgorithm The hash algorithm used for OTP generation. Default is 'sha512'.
|
||||||
|
* @return string Returns the generated OTP as a string with the specified number of digits.
|
||||||
|
* @throws CryptographyException If the generated hash length is less than 20 bytes.
|
||||||
|
*/
|
||||||
|
public static function generateOTP(string $secretKey, int $timeStep=30, int $digits=6, int $counter=null, string $hashAlgorithm='sha512'): string
|
||||||
|
{
|
||||||
|
if ($counter === null)
|
||||||
|
{
|
||||||
|
$counter = floor(time() / $timeStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash = self::hashHmac($hashAlgorithm, pack('J', $counter), $secretKey);
|
||||||
|
|
||||||
|
if (strlen($hash) < 20)
|
||||||
|
{
|
||||||
|
throw new CryptographyException("Invalid hash length: must be at least 20 bytes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the $secretKey
|
||||||
|
if (!ctype_xdigit($secretKey))
|
||||||
|
{
|
||||||
|
throw new CryptographyException("Invalid secret key: must be a hexadecimal string.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$offset = ord($hash[strlen($hash) - 1]) & 0x0F;
|
||||||
|
$binary = unpack('N', substr($hash, $offset, 4))[1] & 0x7FFFFFFF;
|
||||||
|
$otp = $binary % (10 ** $digits);
|
||||||
|
|
||||||
|
return str_pad((string)$otp, $digits, '0', STR_PAD_LEFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a one-time password (OTP) based on the provided parameters.
|
||||||
|
*
|
||||||
|
* @param string $secretKey The secret key used to generate the OTP.
|
||||||
|
* @param string $otp The one-time password to verify.
|
||||||
|
* @param int $timeStep The time step in seconds used for OTP generation. Default is 30 seconds.
|
||||||
|
* @param int $window The allowed window of time steps for verification. Default is 1.
|
||||||
|
* @param int $digits The number of digits in the OTP. Default is 6.
|
||||||
|
* @param string $hashAlgorithm The hash algorithm used for OTP generation. Default is 'sha512'.
|
||||||
|
* @return bool Returns true if the OTP is valid within the provided parameters, otherwise false.
|
||||||
|
* @throws CryptographyException If the generated hash length is less than 20 bytes.
|
||||||
|
*/
|
||||||
|
public static function verifyOTP(string $secretKey, string $otp, int $timeStep=30, int $window=1, int $digits=6, string $hashAlgorithm='sha512'): bool
|
||||||
|
{
|
||||||
|
$currentTime = time();
|
||||||
|
$counter = floor($currentTime / $timeStep);
|
||||||
|
|
||||||
|
for ($i = -$window; $i <= $window; $i++)
|
||||||
|
{
|
||||||
|
$testCounter = $counter + $i;
|
||||||
|
$expectedOtp = self::generateOTP($secretKey, $timeStep, $digits, $testCounter, $hashAlgorithm);
|
||||||
|
|
||||||
|
if (hash_equals($expectedOtp, $otp))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a key URI for use in configuring an authenticator application.
|
||||||
|
*
|
||||||
|
* @param string $label A unique label to identify the account (e.g., user or service name).
|
||||||
|
* @param string $secretKey The secret key used for generating the OTP.
|
||||||
|
* @param string|null $issuer The name of the organization or service issuing the key. Default is null.
|
||||||
|
* @param int $timeStep The time step in seconds used for OTP generation. Default is 30 seconds.
|
||||||
|
* @param int $digits The number of digits in the generated OTP. Default is 6.
|
||||||
|
* @param string $hashAlgorithm The hash algorithm used for OTP generation. Default is 'sha512'.
|
||||||
|
* @return string Returns the URI string formatted
|
||||||
|
*/
|
||||||
|
public static function generateKeyUri(string $label, string $secretKey, ?string $issuer = null, int $timeStep=30, int $digits=6, string $hashAlgorithm='sha512'): string
|
||||||
|
{
|
||||||
|
$issuerPart = $issuer ? "&issuer=" . rawurlencode($issuer) : '';
|
||||||
|
return sprintf(self::URI_FORMAT, rawurlencode($label), $secretKey, $issuerPart, strtoupper($hashAlgorithm), $digits, $timeStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a hash-based message authentication code (HMAC) using the specified algorithm.
|
||||||
|
*
|
||||||
|
* @param string $algorithm The hashing algorithm to be used (e.g., 'sha1', 'sha256', 'sha384', 'sha512').
|
||||||
|
* @param string $data The data to be hashed.
|
||||||
|
* @param string $key The secret key used for the HMAC generation.
|
||||||
|
* @return string The generated HMAC as a raw binary string.
|
||||||
|
* @throws CryptographyException If the algorithm is not supported.
|
||||||
|
*/
|
||||||
|
private static function hashHmac(string $algorithm, string $data, string $key): string
|
||||||
|
{
|
||||||
|
return match($algorithm)
|
||||||
|
{
|
||||||
|
'sha1', 'sha256', 'sha512' => hash_hmac($algorithm, $data, $key, true),
|
||||||
|
default => throw new CryptographyException('Algorithm not supported')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,14 +2,83 @@
|
||||||
|
|
||||||
namespace Socialbox\Classes;
|
namespace Socialbox\Classes;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use Socialbox\Enums\DatabaseObjects;
|
use Socialbox\Enums\DatabaseObjects;
|
||||||
|
|
||||||
class Resources
|
class Resources
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Retrieves the full path to a database resource based on the provided DatabaseObjects instance.
|
||||||
|
*
|
||||||
|
* @param DatabaseObjects $object An instance of DatabaseObjects containing the resource value.
|
||||||
|
* @return string The full file path to the specified database resource.
|
||||||
|
*/
|
||||||
public static function getDatabaseResource(DatabaseObjects $object): string
|
public static function getDatabaseResource(DatabaseObjects $object): string
|
||||||
{
|
{
|
||||||
$tables_directory = __DIR__ . DIRECTORY_SEPARATOR . 'Resources' . DIRECTORY_SEPARATOR . 'database';
|
$tables_directory = __DIR__ . DIRECTORY_SEPARATOR . 'Resources' . DIRECTORY_SEPARATOR . 'database';
|
||||||
return $tables_directory . DIRECTORY_SEPARATOR . $object->value;
|
return $tables_directory . DIRECTORY_SEPARATOR . $object->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the file path of a document resource based on the provided name.
|
||||||
|
*
|
||||||
|
* @param string $name The name of the document resource to retrieve.
|
||||||
|
* @return string The file path of the specified document resource.
|
||||||
|
*/
|
||||||
|
public static function getDocumentResource(String $name): string
|
||||||
|
{
|
||||||
|
$documents_directory = __DIR__ . DIRECTORY_SEPARATOR . 'Resources' . DIRECTORY_SEPARATOR . 'documents';
|
||||||
|
return $documents_directory . DIRECTORY_SEPARATOR . $name . '.html';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the content of the privacy policy document.
|
||||||
|
*
|
||||||
|
* @return string The content of the privacy policy document. Attempts to fetch the document
|
||||||
|
* from a configured location if available and valid; otherwise, retrieves it from a default resource.
|
||||||
|
*/
|
||||||
|
public static function getPrivacyPolicy(): string
|
||||||
|
{
|
||||||
|
$configuredLocation = Configuration::getRegistrationConfiguration()->getPrivacyPolicyDocument();
|
||||||
|
if($configuredLocation !== null && file_exists($configuredLocation))
|
||||||
|
{
|
||||||
|
return file_get_contents($configuredLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return file_get_contents(self::getDocumentResource('privacy'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the content of the Terms of Service document.
|
||||||
|
*
|
||||||
|
* @return string The content of the Terms of Service file. The method checks a configured location first,
|
||||||
|
* and falls back to a default resource if the configured file is unavailable.
|
||||||
|
*/
|
||||||
|
public static function getTermsOfService(): string
|
||||||
|
{
|
||||||
|
$configuredLocation = Configuration::getRegistrationConfiguration()->getTermsOfServiceDocument();
|
||||||
|
if($configuredLocation !== null && file_exists($configuredLocation))
|
||||||
|
{
|
||||||
|
return file_get_contents($configuredLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return file_get_contents(self::getDocumentResource('tos'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the community guidelines document content.
|
||||||
|
*
|
||||||
|
* @return string The content of the community guidelines document, either from a configured location
|
||||||
|
* or a default resource if the configured location is unavailable.
|
||||||
|
*/
|
||||||
|
public static function getCommunityGuidelines(): string
|
||||||
|
{
|
||||||
|
$configuredLocation = Configuration::getRegistrationConfiguration()->getCommunityGuidelinesDocument();
|
||||||
|
if($configuredLocation !== null && file_exists($configuredLocation))
|
||||||
|
{
|
||||||
|
return file_get_contents($configuredLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return file_get_contents(self::getDocumentResource('community'));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
create table authentication_otp
|
||||||
|
(
|
||||||
|
peer_uuid varchar(36) not null comment 'The Peer UUID associated with this record'
|
||||||
|
primary key comment 'The Peer UUID unique Index',
|
||||||
|
secret mediumtext not null comment 'The encrypted secret for the OTP',
|
||||||
|
updated timestamp default current_timestamp() not null comment 'The Timestamp for when the record was last updated',
|
||||||
|
constraint authentication_otp_peer_uuid_uindex
|
||||||
|
unique (peer_uuid) comment 'The Peer UUID unique Index',
|
||||||
|
constraint authentication_otp_registered_peers_uuid_fk
|
||||||
|
foreign key (peer_uuid) references peers (uuid)
|
||||||
|
on update cascade on delete cascade
|
||||||
|
)
|
||||||
|
comment 'Table for housing encrypted OTP secrets for for verification';
|
||||||
|
|
||||||
|
create index authentication_otp_updated_index
|
||||||
|
on authentication_otp (updated)
|
||||||
|
comment 'The index for the updated column';
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
create table authentication_passwords
|
||||||
|
(
|
||||||
|
peer_uuid varchar(36) not null comment 'The primary unique index of the peer uuid'
|
||||||
|
primary key,
|
||||||
|
hash mediumtext not null comment 'The encrypted hash of the password',
|
||||||
|
updated timestamp default current_timestamp() not null comment 'The Timestamp for when this record was last updated',
|
||||||
|
constraint authentication_passwords_peer_uuid_uindex
|
||||||
|
unique (peer_uuid) comment 'The primary unique index of the peer uuid',
|
||||||
|
constraint authentication_passwords_registered_peers_uuid_fk
|
||||||
|
foreign key (peer_uuid) references peers (uuid)
|
||||||
|
on update cascade on delete cascade
|
||||||
|
)
|
||||||
|
comment 'Table for housing authentication passwords for registered peers';
|
||||||
|
|
||||||
|
create index authentication_passwords_updated_index
|
||||||
|
on authentication_passwords (updated)
|
||||||
|
comment 'The index of the of the updated column of the record';
|
||||||
|
|
24
src/Socialbox/Classes/Resources/database/captcha_images.sql
Normal file
24
src/Socialbox/Classes/Resources/database/captcha_images.sql
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
create table captcha_images
|
||||||
|
(
|
||||||
|
uuid varchar(36) default uuid() not null comment 'The Unique index for the UUID column'
|
||||||
|
primary key,
|
||||||
|
peer_uuid varchar(36) not null comment 'The UUID of the peer that is associated with this captcha challenge',
|
||||||
|
status enum ('UNSOLVED', 'SOLVED') default 'UNSOLVED' not null comment 'The status of the current captcha',
|
||||||
|
answer varchar(8) null comment 'The current answer for the captcha',
|
||||||
|
answered timestamp null comment 'The Timestamp for when this captcha was answered',
|
||||||
|
created timestamp default current_timestamp() not null comment 'The Timestamp for when this captcha record was created',
|
||||||
|
constraint captchas_peer_uuid_uindex
|
||||||
|
unique (peer_uuid) comment 'The Primary Unique Index for the peer UUID',
|
||||||
|
constraint captchas_registered_peers_uuid_fk
|
||||||
|
foreign key (peer_uuid) references peers (uuid)
|
||||||
|
on update cascade on delete cascade
|
||||||
|
);
|
||||||
|
|
||||||
|
create index captchas_status_index
|
||||||
|
on captcha_images (status)
|
||||||
|
comment 'The Index for the captcha status';
|
||||||
|
|
||||||
|
create index captchas_uuid_index
|
||||||
|
on captcha_images (uuid)
|
||||||
|
comment 'The Unique index for the UUID column';
|
||||||
|
|
32
src/Socialbox/Classes/Resources/database/channel_com.sql
Normal file
32
src/Socialbox/Classes/Resources/database/channel_com.sql
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
create table channel_com
|
||||||
|
(
|
||||||
|
uuid varchar(36) default uuid() not null comment 'The UUID of the message',
|
||||||
|
channel_uuid varchar(36) not null comment 'The UUID of the encryption channel used',
|
||||||
|
recipient enum ('SENDER', 'RECEIVER') not null comment 'The recipient of the message',
|
||||||
|
message text not null comment 'The encrypted message content',
|
||||||
|
signature varchar(64) not null comment 'The signature of the decrypted message',
|
||||||
|
received tinyint(1) default 0 not null comment 'True if the message was received by the recipient',
|
||||||
|
timestamp timestamp default current_timestamp() not null comment 'The timestamp of the mssage being sent',
|
||||||
|
primary key (uuid, channel_uuid) comment 'The Unique Pair Index for the channel UUID and message UUID',
|
||||||
|
constraint channel_com_uuid_channel_uuid_uindex
|
||||||
|
unique (uuid, channel_uuid) comment 'The Unique Pair Index for the channel UUID and message UUID',
|
||||||
|
constraint channel_com_uuid_channel_uuid_uindex_2
|
||||||
|
unique (uuid, channel_uuid) comment 'The Unique Index Pair for the channel UUID and message UUID',
|
||||||
|
constraint channel_com_encryption_channels_uuid_fk
|
||||||
|
foreign key (channel_uuid) references encryption_channels (uuid)
|
||||||
|
on update cascade on delete cascade
|
||||||
|
)
|
||||||
|
comment 'Table for housing communication messages over encryption channels';
|
||||||
|
|
||||||
|
create index channel_com_received_index
|
||||||
|
on channel_com (received)
|
||||||
|
comment 'The index for the received column';
|
||||||
|
|
||||||
|
create index channel_com_recipient_index
|
||||||
|
on channel_com (recipient)
|
||||||
|
comment 'The index for the recipient column';
|
||||||
|
|
||||||
|
create index channel_com_timestamp_index
|
||||||
|
on channel_com (timestamp)
|
||||||
|
comment 'The index for the Timestamp column';
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
create table contacts_known_keys
|
||||||
|
(
|
||||||
|
contact_uuid varchar(36) not null comment 'The Unique Universal Identifier of the personal contact that this record is associated with',
|
||||||
|
signature_uuid varchar(36) not null comment 'The Unique Universal Identifier for the signature key',
|
||||||
|
signature_name varchar(64) not null comment 'The name of the signing key',
|
||||||
|
signature_key varchar(32) not null comment 'The public signing key',
|
||||||
|
expires timestamp null comment 'The Timestamp for when this key expires, null means never',
|
||||||
|
created timestamp not null comment 'The Timestamp for when this key was created',
|
||||||
|
trusted_on timestamp default current_timestamp() not null comment 'The Timestamp for when the peer trusted this key',
|
||||||
|
primary key (contact_uuid, signature_uuid),
|
||||||
|
constraint contacts_known_keys_signature_uuid_contact_uuid_uindex
|
||||||
|
unique (signature_uuid, contact_uuid) comment 'The Unique Signature Index Pair for the contact UUID and key UUID',
|
||||||
|
constraint contacts_known_keys_contacts_uuid_fk
|
||||||
|
foreign key (contact_uuid) references contacts (uuid)
|
||||||
|
on update cascade on delete cascade
|
||||||
|
)
|
||||||
|
comment 'Table for housing known keys associated with personal contact records';
|
||||||
|
|
||||||
|
create index contacts_known_keys_contact_uuid_index
|
||||||
|
on contacts_known_keys (contact_uuid)
|
||||||
|
comment 'The Index of the contact UUID';
|
||||||
|
|
18
src/Socialbox/Classes/Resources/database/contacts.sql
Normal file
18
src/Socialbox/Classes/Resources/database/contacts.sql
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
create table contacts
|
||||||
|
(
|
||||||
|
uuid varchar(36) default uuid() not null comment 'The contact UUID for the record'
|
||||||
|
primary key comment 'The Primary Unique Universal Identifier for the contact record',
|
||||||
|
peer_uuid varchar(36) not null comment 'The Peer UUID',
|
||||||
|
contact_peer_address varchar(256) not null comment 'The contact peer address',
|
||||||
|
relationship enum ('MUTUAL', 'TRUSTED', 'BLOCKED') default 'MUTUAL' not null comment 'The relationship between the two peers, MUTUAL=The contact peer is recognized',
|
||||||
|
created timestamp default current_timestamp() not null comment 'The Timestamp for when this contact was created',
|
||||||
|
constraint contacts_uuid_uindex
|
||||||
|
unique (uuid) comment 'The Primary Unique Universal Identifier for the contact record',
|
||||||
|
constraint peer_contacts_peer_uuid_contact_peer_address_uindex
|
||||||
|
unique (peer_uuid, contact_peer_address) comment 'The Unique Peer UUID & Contact Peer Address combination pair',
|
||||||
|
constraint peer_contacts_registered_peers_uuid_fk
|
||||||
|
foreign key (peer_uuid) references peers (uuid)
|
||||||
|
on update cascade on delete cascade
|
||||||
|
)
|
||||||
|
comment 'Table for housing personal contacts for peers';
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
create table encryption_channels_com
|
||||||
|
(
|
||||||
|
uuid varchar(36) default uuid() not null comment 'The Unique Universal Identifier of the message for the encryption channel',
|
||||||
|
channel_uuid varchar(36) not null comment 'The UUID of the channel that the message belongs to',
|
||||||
|
recipient enum ('CALLER', 'RECEIVER') not null comment 'The recipient of the message',
|
||||||
|
status enum ('SENT', 'RECEIVED', 'REJECTED') default 'SENT' not null comment 'The status of the message, SENT being the default, RECEIVED is when the recipient receives the message successfully and REJECTED is when the message cannot be decrypted, or the checksum failed.',
|
||||||
|
checksum varchar(64) not null comment 'The SHA512 hash of the decrypted message contents',
|
||||||
|
data text not null comment 'The data of the message',
|
||||||
|
timestamp timestamp default current_timestamp() not null comment 'The Timestamp of the message',
|
||||||
|
primary key (uuid, channel_uuid) comment 'The Unique Primary Index Pair for the channel_uuid and uuid of the message',
|
||||||
|
constraint encryption_channels_com_uuid_channel_uuid_uindex
|
||||||
|
unique (uuid, channel_uuid) comment 'The Unique Primary Index Pair for the channel_uuid and uuid of the message',
|
||||||
|
constraint encryption_channels_com_encryption_channels_uuid_fk
|
||||||
|
foreign key (channel_uuid) references encryption_channels (uuid)
|
||||||
|
on update cascade on delete cascade
|
||||||
|
)
|
||||||
|
comment 'The table for housing communication messages sent over encryption channels';
|
||||||
|
|
||||||
|
create index encryption_channels_com_recipient_index
|
||||||
|
on encryption_channels_com (recipient)
|
||||||
|
comment 'The index of the recipient column used for indexing';
|
||||||
|
|
||||||
|
create index encryption_channels_com_timestamp_index
|
||||||
|
on encryption_channels_com (timestamp)
|
||||||
|
comment 'The index of the Timestamp column';
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
create table encryption_channels_com
|
||||||
|
(
|
||||||
|
uuid varchar(36) default uuid() not null comment 'The Unique Universal Identifier of the message for the encryption channel',
|
||||||
|
channel_uuid varchar(36) not null comment 'The UUID of the channel that the message belongs to',
|
||||||
|
recipient enum ('CALLER', 'RECEIVER') not null comment 'The recipient of the message',
|
||||||
|
status enum ('SENT', 'RECEIVED', 'REJECTED') default 'SENT' not null comment 'The status of the message, SENT being the default, RECEIVED is when the recipient receives the message successfully and REJECTED is when the message cannot be decrypted, or the checksum failed.',
|
||||||
|
checksum varchar(64) not null comment 'The SHA512 hash of the decrypted message contents',
|
||||||
|
data text not null comment 'The data of the message',
|
||||||
|
timestamp timestamp default current_timestamp() not null comment 'The Timestamp of the message',
|
||||||
|
primary key (uuid, channel_uuid) comment 'The Unique Primary Index Pair for the channel_uuid and uuid of the message',
|
||||||
|
constraint encryption_channels_com_uuid_channel_uuid_uindex
|
||||||
|
unique (uuid, channel_uuid) comment 'The Unique Primary Index Pair for the channel_uuid and uuid of the message',
|
||||||
|
constraint encryption_channels_com_encryption_channels_uuid_fk
|
||||||
|
foreign key (channel_uuid) references encryption_channels (uuid)
|
||||||
|
on update cascade on delete cascade
|
||||||
|
)
|
||||||
|
comment 'The table for housing communication messages sent over encryption channels';
|
||||||
|
|
||||||
|
create index encryption_channels_com_recipient_index
|
||||||
|
on encryption_channels_com (recipient)
|
||||||
|
comment 'The index of the recipient column used for indexing';
|
||||||
|
|
||||||
|
create index encryption_channels_com_timestamp_index
|
||||||
|
on encryption_channels_com (timestamp)
|
||||||
|
comment 'The index of the Timestamp column';
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
create table external_sessions
|
||||||
|
(
|
||||||
|
domain varchar(256) not null comment 'The unique domain name that this session belongs to'
|
||||||
|
primary key comment 'The Unique Primary index for the external session',
|
||||||
|
rpc_endpoint text not null comment 'The RPC endpoint of the external connection',
|
||||||
|
session_uuid varchar(36) not null comment 'The UUID of the session to the external server',
|
||||||
|
transport_encryption_algorithm enum ('xchacha20', 'chacha20', 'aes256gcm') default 'xchacha20' not null comment 'The transport encryption algorithm used',
|
||||||
|
server_keypair_expires int not null comment 'The Timestamp for when the server keypair expires',
|
||||||
|
server_public_signing_key varchar(64) not null comment 'The public signing key of the server resolved from DNS records',
|
||||||
|
server_public_encryption_key varchar(64) not null comment 'The public encryption key of the server for this session',
|
||||||
|
host_public_encryption_key varchar(64) not null comment 'The public encryption key for the host',
|
||||||
|
host_private_encryption_key varchar(64) not null comment 'The private encryption key for host',
|
||||||
|
private_shared_secret varchar(64) not null comment 'The private shared secret obtained from the DHE procedure',
|
||||||
|
host_transport_encryption_key varchar(64) not null comment 'The transport encryption key for the host',
|
||||||
|
server_transport_encryption_key varchar(64) not null comment 'The transport encryption key for the server',
|
||||||
|
last_accessed timestamp default current_timestamp() not null comment 'The Timestamp for when the record was last accessed',
|
||||||
|
created timestamp default current_timestamp() not null comment 'The Timestamp for when this record was created',
|
||||||
|
constraint external_sessions_domain_uindex
|
||||||
|
unique (domain) comment 'The Unique Primary index for the external session'
|
||||||
|
)
|
||||||
|
comment 'Table for housing external sessions to external servers';
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
create table password_authentication
|
|
||||||
(
|
|
||||||
peer_uuid varchar(36) not null comment 'The Primary unique Index for the peer UUID'
|
|
||||||
primary key,
|
|
||||||
value varchar(128) not null comment 'The hash value of the pasword',
|
|
||||||
updated timestamp default current_timestamp() not null comment 'The Timestamp for when this record was last updated',
|
|
||||||
constraint password_authentication_peer_uuid_uindex
|
|
||||||
unique (peer_uuid) comment 'The Primary unique Index for the peer UUID',
|
|
||||||
constraint password_authentication_registered_peers_uuid_fk
|
|
||||||
foreign key (peer_uuid) references registered_peers (uuid)
|
|
||||||
on update cascade on delete cascade
|
|
||||||
)
|
|
||||||
comment 'Table for housing password authentications associated with peers';
|
|
||||||
|
|
||||||
create index password_authentication_updated_index
|
|
||||||
on password_authentication (updated)
|
|
||||||
comment 'The Indefor the updated timestamp';
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
create table peer_information
|
||||||
|
(
|
||||||
|
peer_uuid varchar(36) not null comment 'The Unique Universal Identifier for the peer',
|
||||||
|
property_name enum ('DISPLAY_NAME', 'DISPLAY_PICTURE', 'FIRST_NAME', 'MIDDLE_NAME', 'LAST_NAME', 'EMAIL_ADDRESS', 'PHONE_NUMBER', 'BIRTHDAY', 'URL') not null comment 'The name of the property',
|
||||||
|
property_value varchar(256) not null comment 'The value of the property associated with the peer',
|
||||||
|
privacy_state enum ('PUBLIC', 'PRIVATE', 'CONTACTS', 'TRUSTED') default 'PRIVATE' not null comment 'The privacy setting for the information property',
|
||||||
|
primary key (property_name, peer_uuid),
|
||||||
|
constraint peer_information_peer_uuid_property_name_uindex
|
||||||
|
unique (peer_uuid, property_name) comment 'The Unique Index for the the peer uuid & property name combination',
|
||||||
|
constraint peer_information_registered_peers_uuid_fk
|
||||||
|
foreign key (peer_uuid) references peers (uuid)
|
||||||
|
on update cascade on delete cascade
|
||||||
|
)
|
||||||
|
comment 'Table for housing peer information';
|
||||||
|
|
||||||
|
create index peer_information_peer_uuid_index
|
||||||
|
on peer_information (peer_uuid)
|
||||||
|
comment 'The index for the peer uuid';
|
||||||
|
|
37
src/Socialbox/Classes/Resources/database/peers.sql
Normal file
37
src/Socialbox/Classes/Resources/database/peers.sql
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
create table peers
|
||||||
|
(
|
||||||
|
uuid varchar(36) default uuid() not null comment 'The Primary index for the peer uuid'
|
||||||
|
primary key,
|
||||||
|
username varchar(255) not null comment 'The Unique username associated with the peer',
|
||||||
|
server varchar(255) default 'host' not null comment 'The server that this peer is registered to',
|
||||||
|
flags text null comment 'Comma seprted flags associated with the peer',
|
||||||
|
enabled tinyint(1) default 0 not null comment 'Boolean column to indicate if this account is Enabled, by default it''s Disabled until the account is verified.',
|
||||||
|
updated timestamp default current_timestamp() not null comment 'The Timestamp for when this record was last updated',
|
||||||
|
created timestamp default current_timestamp() not null comment 'The Timestamp for when the peer was registered on the network',
|
||||||
|
constraint registered_peers_server_username_uindex
|
||||||
|
unique (server, username) comment 'The Unique Username + Server Index Pair',
|
||||||
|
constraint registered_peers_uuid_uindex
|
||||||
|
unique (uuid) comment 'The Primary index for the peer uuid'
|
||||||
|
)
|
||||||
|
comment 'Table for housing registered peers under this network';
|
||||||
|
|
||||||
|
create index registered_peers_enabled_index
|
||||||
|
on peers (enabled)
|
||||||
|
comment 'The index of the enabled column for registered peers';
|
||||||
|
|
||||||
|
create index registered_peers_registered_index
|
||||||
|
on peers (created)
|
||||||
|
comment 'The Index for the reigstered column of the peer';
|
||||||
|
|
||||||
|
create index registered_peers_server_index
|
||||||
|
on peers (server)
|
||||||
|
comment 'The Index for the peer''s server';
|
||||||
|
|
||||||
|
create index registered_peers_updated_index
|
||||||
|
on peers (updated)
|
||||||
|
comment 'The Index for the update column';
|
||||||
|
|
||||||
|
create index registered_peers_username_index
|
||||||
|
on peers (username)
|
||||||
|
comment 'The index for the registered username';
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
create table registered_peers
|
|
||||||
(
|
|
||||||
uuid varchar(36) default uuid() not null comment 'The Primary index for the peer uuid'
|
|
||||||
primary key,
|
|
||||||
username varchar(255) not null comment 'The Unique username associated with the peer',
|
|
||||||
flags text null comment 'Comma seprted flags associated with the peer',
|
|
||||||
registered timestamp default current_timestamp() not null comment 'The Timestamp for when the peer was registered on the network',
|
|
||||||
constraint registered_peers_pk_2
|
|
||||||
unique (username) comment 'The unique username for the peer',
|
|
||||||
constraint registered_peers_username_uindex
|
|
||||||
unique (username) comment 'The unique username for the peer',
|
|
||||||
constraint registered_peers_uuid_uindex
|
|
||||||
unique (uuid) comment 'The Primary index for the peer uuid'
|
|
||||||
)
|
|
||||||
comment 'Table for housing registered peers under this network';
|
|
||||||
|
|
||||||
create index registered_peers_registered_index
|
|
||||||
on registered_peers (registered)
|
|
||||||
comment 'The Index for the reigstered column of the peer';
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
create table resolved_dns_records
|
||||||
|
(
|
||||||
|
domain varchar(512) not null comment 'Unique Index for the server domain'
|
||||||
|
primary key,
|
||||||
|
rpc_endpoint text not null comment 'The endpoint of the RPC server',
|
||||||
|
public_key text not null comment 'The Public Key of the server',
|
||||||
|
expires bigint not null comment 'The Unix Timestamp for when the server''s keypair expires',
|
||||||
|
updated timestamp default current_timestamp() not null comment 'The Timestamp for when this record was last updated',
|
||||||
|
constraint resolved_dns_records_domain_uindex
|
||||||
|
unique (domain) comment 'Unique Index for the server domain',
|
||||||
|
constraint resolved_dns_records_pk
|
||||||
|
unique (domain) comment 'Unique Index for the server domain'
|
||||||
|
)
|
||||||
|
comment 'A table for housing DNS resolutions';
|
||||||
|
|
||||||
|
create index resolved_dns_records_updated_index
|
||||||
|
on resolved_dns_records (updated)
|
||||||
|
comment 'The index for the updated column';
|
||||||
|
|
|
@ -2,20 +2,30 @@ create table sessions
|
||||||
(
|
(
|
||||||
uuid varchar(36) default uuid() not null comment 'The Unique Primary index for the session UUID'
|
uuid varchar(36) default uuid() not null comment 'The Unique Primary index for the session UUID'
|
||||||
primary key,
|
primary key,
|
||||||
authenticated_peer_uuid varchar(36) null comment 'The peer the session is authenticated as, null if the session isn''t authenticated',
|
peer_uuid varchar(36) not null comment 'The peer the session is identified as, null if the session isn''t identified as a peer',
|
||||||
public_key blob not null comment 'The client''s public key provided when creating the session',
|
client_name varchar(256) not null comment 'The name of the client that is using this session',
|
||||||
state enum ('ACTIVE', 'EXPIRED', 'CLOSED') default 'ACTIVE' not null comment 'The status of the session',
|
client_version varchar(16) not null comment 'The version of the client',
|
||||||
|
authenticated tinyint(1) default 0 not null comment 'Indicates if the session is currently authenticated as the peer',
|
||||||
|
client_public_signing_key varchar(64) not null comment 'The client''s public signing key used for signing decrypted messages',
|
||||||
|
client_public_encryption_key varchar(64) not null comment 'The Public Key of the client''s encryption key',
|
||||||
|
server_public_encryption_key varchar(64) not null comment 'The server''s public encryption key for this session',
|
||||||
|
server_private_encryption_key varchar(64) not null comment 'The server''s private encryption key for this session',
|
||||||
|
private_shared_secret varchar(64) null comment 'The shared secret encryption key between the Client & Server',
|
||||||
|
client_transport_encryption_key varchar(64) null comment 'The encryption key for sending messages to the client',
|
||||||
|
server_transport_encryption_key varchar(64) null comment 'The encryption key for sending messages to the server',
|
||||||
|
state enum ('AWAITING_DHE', 'ACTIVE', 'CLOSED', 'EXPIRED') default 'AWAITING_DHE' not null comment 'The status of the session',
|
||||||
|
flags text null comment 'The current flags that is set to the session',
|
||||||
created timestamp default current_timestamp() not null comment 'The Timestamp for when the session was last created',
|
created timestamp default current_timestamp() not null comment 'The Timestamp for when the session was last created',
|
||||||
last_request timestamp null comment 'The Timestamp for when the last request was made using this session',
|
last_request timestamp null comment 'The Timestamp for when the last request was made using this session',
|
||||||
constraint sessions_uuid_uindex
|
constraint sessions_uuid_uindex
|
||||||
unique (uuid) comment 'The Unique Primary index for the session UUID',
|
unique (uuid) comment 'The Unique Primary index for the session UUID',
|
||||||
constraint sessions_registered_peers_uuid_fk
|
constraint sessions_registered_peers_uuid_fk
|
||||||
foreign key (authenticated_peer_uuid) references registered_peers (uuid)
|
foreign key (peer_uuid) references peers (uuid)
|
||||||
on update cascade on delete cascade
|
on update cascade on delete cascade
|
||||||
);
|
);
|
||||||
|
|
||||||
create index sessions_authenticated_peer_index
|
create index sessions_authenticated_peer_index
|
||||||
on sessions (authenticated_peer_uuid)
|
on sessions (peer_uuid)
|
||||||
comment 'The Index for the authenticated peer column';
|
comment 'The Index for the authenticated peer column';
|
||||||
|
|
||||||
create index sessions_created_index
|
create index sessions_created_index
|
||||||
|
|
32
src/Socialbox/Classes/Resources/database/signing_keys.sql
Normal file
32
src/Socialbox/Classes/Resources/database/signing_keys.sql
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
create table signing_keys
|
||||||
|
(
|
||||||
|
peer_uuid varchar(36) not null comment 'The UUID of the peer',
|
||||||
|
uuid varchar(36) default uuid() not null comment 'The UUID of the key record',
|
||||||
|
name varchar(64) not null comment 'Optional. User provided name for the key',
|
||||||
|
public_key varchar(64) not null comment 'The Public Signature Key',
|
||||||
|
state enum ('ACTIVE', 'EXPIRED') default 'ACTIVE' not null comment 'The state of the public key',
|
||||||
|
expires timestamp null comment 'The Timestamp for when this key expires, null = Never expires',
|
||||||
|
created timestamp default current_timestamp() not null comment 'The Timestamp for when the signing key record was created',
|
||||||
|
primary key (peer_uuid, uuid) comment 'The Unique Index pair for the signing key name and the UUID of the peer',
|
||||||
|
constraint signing_keys_peer_uuid_uuid_uindex
|
||||||
|
unique (peer_uuid, uuid) comment 'The Unique Index pair for the signing key name and the UUID of the peer',
|
||||||
|
constraint signing_keys_pk
|
||||||
|
unique (peer_uuid, uuid) comment 'The Unique Index pair for the signing key name and the UUID of the peer',
|
||||||
|
constraint signing_keys_registered_peers_uuid_fk
|
||||||
|
foreign key (peer_uuid) references peers (uuid)
|
||||||
|
on update cascade on delete cascade
|
||||||
|
)
|
||||||
|
comment 'Table for housing public signing keys for peers on the network';
|
||||||
|
|
||||||
|
create index signing_keys_peer_uuid_index
|
||||||
|
on signing_keys (peer_uuid)
|
||||||
|
comment 'The primary index for the peer UUID column';
|
||||||
|
|
||||||
|
create index signing_keys_state_index
|
||||||
|
on signing_keys (state)
|
||||||
|
comment 'Signing key state index';
|
||||||
|
|
||||||
|
create index signing_keys_uuid_index
|
||||||
|
on signing_keys (uuid)
|
||||||
|
comment 'The index for the signing key namee';
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
create table variables
|
create table variables
|
||||||
(
|
(
|
||||||
name varchar(255) not null comment 'The name of the variable'
|
name varchar(255) not null comment 'The unique index for the variable name'
|
||||||
primary key comment 'The unique index for the variable name',
|
primary key,
|
||||||
value text null comment 'The value of the variable',
|
value text null comment 'The value of the variable',
|
||||||
`read_only` tinyint(1) default 0 not null comment 'Boolean indicator if the variable is read only',
|
read_only tinyint(1) default 0 not null comment 'Boolean indicator if the variable is read only',
|
||||||
created timestamp default current_timestamp() not null comment 'The Timestamp for when this record was created',
|
created timestamp default current_timestamp() not null comment 'The Timestamp for when this record was created',
|
||||||
updated timestamp null comment 'The Timestamp for when this record was last updated',
|
updated timestamp null comment 'The Timestamp for when this record was last updated',
|
||||||
constraint variables_name_uindex
|
constraint variables_name_uindex
|
||||||
unique (name) comment 'The unique index for the variable name'
|
unique (name) comment 'The unique index for the variable name'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<h1>Community Guidelines</h1>
|
2
src/Socialbox/Classes/Resources/documents/privacy.html
Normal file
2
src/Socialbox/Classes/Resources/documents/privacy.html
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<h1>Privacy Policy Document</h1>
|
||||||
|
<p>This is where the privacy policy document would reside in</p>
|
2
src/Socialbox/Classes/Resources/documents/tos.html
Normal file
2
src/Socialbox/Classes/Resources/documents/tos.html
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<h1>Terms of Service Document</h1>
|
||||||
|
<p>This is where the Terms of Service document would reside</p>
|
|
@ -2,87 +2,915 @@
|
||||||
|
|
||||||
namespace Socialbox\Classes;
|
namespace Socialbox\Classes;
|
||||||
|
|
||||||
use Socialbox\Classes\ServerResolver;
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
use Socialbox\Enums\StandardHeaders;
|
use Socialbox\Enums\StandardHeaders;
|
||||||
|
use Socialbox\Enums\Types\RequestType;
|
||||||
|
use Socialbox\Exceptions\CryptographyException;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
use Socialbox\Exceptions\ResolutionException;
|
use Socialbox\Exceptions\ResolutionException;
|
||||||
use Socialclient\Exceptions\RpcRequestException;
|
use Socialbox\Exceptions\RpcException;
|
||||||
|
use Socialbox\Objects\Client\EncryptionChannelSecret;
|
||||||
|
use Socialbox\Objects\Client\ExportedSession;
|
||||||
|
use Socialbox\Objects\Client\SignatureKeyPair;
|
||||||
|
use Socialbox\Objects\KeyPair;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Socialbox\Objects\RpcResult;
|
||||||
|
use Socialbox\Objects\Standard\ServerInformation;
|
||||||
|
|
||||||
class RpcClient
|
class RpcClient
|
||||||
{
|
{
|
||||||
|
private \LogLib2\Logger $logger;
|
||||||
|
|
||||||
private const string CLIENT_NAME = 'Socialbox PHP';
|
private const string CLIENT_NAME = 'Socialbox PHP';
|
||||||
private const string CLIENT_VERSION = '1.0';
|
private const string CLIENT_VERSION = '1.0';
|
||||||
private const string CONTENT_TYPE = 'application/json';
|
|
||||||
|
|
||||||
private string $domain;
|
|
||||||
private string $endpoint;
|
|
||||||
private string $serverPublicKey;
|
|
||||||
|
|
||||||
|
private PeerAddress $identifiedAs;
|
||||||
|
private string $serverPublicSigningKey;
|
||||||
|
private string $serverPublicEncryptionKey;
|
||||||
|
private KeyPair $clientSigningKeyPair;
|
||||||
|
private KeyPair $clientEncryptionKeyPair;
|
||||||
|
private string $privateSharedSecret;
|
||||||
|
private string $clientTransportEncryptionKey;
|
||||||
|
private string $serverTransportEncryptionKey;
|
||||||
|
private ServerInformation $serverInformation;
|
||||||
|
private string $rpcEndpoint;
|
||||||
|
private string $remoteServer;
|
||||||
|
private string $sessionUuid;
|
||||||
|
private ?string $defaultSigningKey;
|
||||||
|
private array $signingKeys;
|
||||||
|
/**
|
||||||
|
* @var EncryptionChannelSecret[]
|
||||||
|
*/
|
||||||
|
private array $encryptionChannelSecrets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws ResolutionException
|
* Constructs a new instance with the specified peer address.
|
||||||
|
*
|
||||||
|
* @param string|PeerAddress $identifiedAs The peer address to be used for the instance (eg; johndoe@example.com)
|
||||||
|
* @param string|null $server Optional. The domain of the server to connect to if different from the identified
|
||||||
|
* @param ExportedSession|null $exportedSession Optional. An exported session to be used to re-connect.
|
||||||
|
* @throws CryptographyException If there is an error in the cryptographic operations.
|
||||||
|
* @throws ResolutionException If there is an error in the resolution process.
|
||||||
|
* @throws RpcException If there is an error in the RPC request or if no response is received.
|
||||||
|
* @throws DatabaseOperationException If there is an error in the database operation.
|
||||||
*/
|
*/
|
||||||
public function __construct(string $domain)
|
public function __construct(string|PeerAddress $identifiedAs, ?string $server=null, ?ExportedSession $exportedSession=null)
|
||||||
{
|
{
|
||||||
$resolved = ServerResolver::resolveDomain($domain);
|
$this->logger = new \LogLib2\Logger('net.nosial.socialbox');
|
||||||
|
|
||||||
$this->domain = $domain;
|
// If an exported session is provided, no need to re-connect.
|
||||||
$this->endpoint = $resolved->getEndpoint();
|
// Just use the session details provided.
|
||||||
$this->serverPublicKey = $resolved->getPublicKey();
|
if($exportedSession !== null)
|
||||||
$this->clientPrivateKey = null;
|
{
|
||||||
|
// Check if the server keypair has expired from the exported session
|
||||||
|
if($exportedSession->getServerKeypairExpires() > 0 && time() > $exportedSession->getServerKeypairExpires())
|
||||||
|
{
|
||||||
|
throw new RpcException('The server keypair has expired, a new session must be created');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDomain(): string
|
$this->identifiedAs = PeerAddress::fromAddress($exportedSession->getPeerAddress());
|
||||||
|
$this->rpcEndpoint = $exportedSession->getRpcEndpoint();
|
||||||
|
$this->remoteServer = $exportedSession->getRemoteServer();
|
||||||
|
$this->sessionUuid = $exportedSession->getSessionUuid();
|
||||||
|
$this->serverPublicSigningKey = $exportedSession->getServerPublicSigningKey();
|
||||||
|
$this->serverPublicEncryptionKey = $exportedSession->getServerPublicEncryptionKey();
|
||||||
|
$this->clientSigningKeyPair = new KeyPair($exportedSession->getClientPublicSigningKey(), $exportedSession->getClientPrivateSigningKey());
|
||||||
|
$this->clientEncryptionKeyPair = new KeyPair($exportedSession->getClientPublicEncryptionKey(), $exportedSession->getClientPrivateEncryptionKey());
|
||||||
|
$this->privateSharedSecret = $exportedSession->getPrivateSharedSecret();
|
||||||
|
$this->clientTransportEncryptionKey = $exportedSession->getClientTransportEncryptionKey();
|
||||||
|
$this->serverTransportEncryptionKey = $exportedSession->getServerTransportEncryptionKey();
|
||||||
|
$this->signingKeys = $exportedSession->getSigningKeys();
|
||||||
|
$this->defaultSigningKey = $exportedSession->getDefaultSigningKey();
|
||||||
|
$this->encryptionChannelSecrets = $exportedSession->getEncryptionChannelSecrets();
|
||||||
|
|
||||||
|
// Still solve the server information
|
||||||
|
$this->serverInformation = self::getServerInformation();
|
||||||
|
|
||||||
|
// Check if the active keypair has expired
|
||||||
|
if($this->serverInformation->getServerKeypairExpires() > 0 && time() > $this->serverInformation->getServerKeypairExpires())
|
||||||
{
|
{
|
||||||
return $this->domain;
|
// TODO: Could be auto-resolved
|
||||||
|
throw new RpcException('The server keypair has expired but the server has not provided a new keypair, contact the server administrator');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getEndpoint(): string
|
// Check if the transport encryption algorithm has changed
|
||||||
|
if($this->serverInformation->getTransportEncryptionAlgorithm() !== $exportedSession->getTransportEncryptionAlgorithm())
|
||||||
{
|
{
|
||||||
return $this->endpoint;
|
// TODO: Could be auto-resolved
|
||||||
|
throw new RpcException('The server has changed its transport encryption algorithm, a new session must be created, old algorithm: ' . $exportedSession->getTransportEncryptionAlgorithm() . ', new algorithm: ' . $this->serverInformation->getTransportEncryptionAlgorithm());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getServerPublicKey(): string
|
return;
|
||||||
{
|
|
||||||
return $this->serverPublicKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendRequest(array $data)
|
// If the peer address is a string, we need to convert it to a PeerAddress object
|
||||||
|
if(is_string($identifiedAs))
|
||||||
{
|
{
|
||||||
$ch = curl_init($this->endpoint);
|
$identifiedAs = PeerAddress::fromAddress($identifiedAs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the initial properties
|
||||||
|
$this->signingKeys = [];
|
||||||
|
$this->encryptionChannelSecrets = [];
|
||||||
|
$this->defaultSigningKey = null;
|
||||||
|
$this->identifiedAs = $identifiedAs;
|
||||||
|
$this->remoteServer = $server ?? $identifiedAs->getDomain();
|
||||||
|
|
||||||
|
// Resolve the domain and get the server's Public Key & RPC Endpoint
|
||||||
|
$resolvedServer = ServerResolver::resolveDomain($this->remoteServer, false);
|
||||||
|
|
||||||
|
// Import the RPC Endpoint & the server's public key.
|
||||||
|
$this->serverPublicSigningKey = $resolvedServer->getPublicSigningKey();
|
||||||
|
$this->rpcEndpoint = $resolvedServer->getRpcEndpoint();
|
||||||
|
|
||||||
|
if(empty($this->serverPublicSigningKey))
|
||||||
|
{
|
||||||
|
throw new ResolutionException('Failed to resolve domain: No public key found for the server');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve basic server information
|
||||||
|
$this->serverInformation = self::getServerInformation();
|
||||||
|
|
||||||
|
// Check if the server keypair has expired
|
||||||
|
if($this->serverInformation->getServerKeypairExpires() > 0 && time() > $this->serverInformation->getServerKeypairExpires())
|
||||||
|
{
|
||||||
|
throw new RpcException('The server keypair has expired but the server has not provided a new keypair, contact the server administrator');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the username is `host` and the domain is the same as this server's domain, we use our signing keypair
|
||||||
|
// Essentially this is a special case for the server to contact another server
|
||||||
|
if($this->identifiedAs->isHost())
|
||||||
|
{
|
||||||
|
$this->clientSigningKeyPair = new KeyPair(Configuration::getCryptographyConfiguration()->getHostPublicKey(), Configuration::getCryptographyConfiguration()->getHostPrivateKey());
|
||||||
|
}
|
||||||
|
// Otherwise we generate a random signing keypair
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->clientSigningKeyPair = Cryptography::generateSigningKeyPair();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always use a random encryption keypair
|
||||||
|
$this->clientEncryptionKeyPair = Cryptography::generateEncryptionKeyPair();
|
||||||
|
|
||||||
|
// Create a session with the server, with the method we obtain the Session UUID
|
||||||
|
// And the server's public encryption key.
|
||||||
|
$this->createSession();
|
||||||
|
|
||||||
|
// Generate a transport encryption key on our end using the server's preferred algorithm
|
||||||
|
$this->clientTransportEncryptionKey = Cryptography::generateEncryptionKey($this->serverInformation->getTransportEncryptionAlgorithm());
|
||||||
|
|
||||||
|
// Preform the DHE so that transport encryption keys can be exchanged
|
||||||
|
$this->sendDheExchange();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a new session with the server and retrieves the session UUID.
|
||||||
|
*
|
||||||
|
* @throws RpcException If the session cannot be created, if the server does not provide a valid response,
|
||||||
|
* or critical headers like encryption public key are missing in the server's response.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function createSession(): void
|
||||||
|
{
|
||||||
|
$ch = curl_init();
|
||||||
|
$headers = [
|
||||||
|
StandardHeaders::REQUEST_TYPE->value . ': ' . RequestType::INITIATE_SESSION->value,
|
||||||
|
StandardHeaders::CLIENT_NAME->value . ': ' . self::CLIENT_NAME,
|
||||||
|
StandardHeaders::CLIENT_VERSION->value . ': ' . self::CLIENT_VERSION,
|
||||||
|
StandardHeaders::IDENTIFY_AS->value . ': ' . $this->identifiedAs->getAddress(),
|
||||||
|
// Always provide our generated encrypted public key
|
||||||
|
StandardHeaders::ENCRYPTION_PUBLIC_KEY->value . ': ' . $this->clientEncryptionKeyPair->getPublicKey()
|
||||||
|
];
|
||||||
|
|
||||||
|
// If we're not connecting as the host, we need to provide our public key
|
||||||
|
// Otherwise, the server will obtain the public key itself from DNS records rather than trusting the client
|
||||||
|
if(!$this->identifiedAs->isHost())
|
||||||
|
{
|
||||||
|
$headers[] = StandardHeaders::SIGNING_PUBLIC_KEY->value . ': ' . $this->clientSigningKeyPair->getPublicKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseHeaders = [];
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $this->rpcEndpoint);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
curl_setopt($ch, CURLOPT_POST, true);
|
// Capture the response headers to get the encryption public key
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, Utilities::jsonEncode($data));
|
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use (&$responseHeaders)
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
{
|
||||||
Utilities::generateHeader(StandardHeaders::CLIENT_NAME, self::CLIENT_NAME),
|
$len = strlen($header);
|
||||||
Utilities::generateHeader(StandardHeaders::CLIENT_VERSION, self::CLIENT_VERSION),
|
$header = explode(':', $header, 2);
|
||||||
Utilities::generateHeader(StandardHeaders::CONTENT_TYPE, self::CONTENT_TYPE)
|
if (count($header) < 2) // ignore invalid headers
|
||||||
]);
|
{
|
||||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
return $len;
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseHeaders[strtolower(trim($header[0]))][] = trim($header[1]);
|
||||||
|
return $len;
|
||||||
|
});
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||||
|
|
||||||
|
$this->logger->debug(sprintf('Creating session with %s', $this->rpcEndpoint));
|
||||||
|
$this->logger->debug(sprintf('Headers: %s', json_encode($headers)));
|
||||||
|
$this->logger->debug(sprintf('Client Encryption Public Key: %s', $this->clientEncryptionKeyPair->getPublicKey()));
|
||||||
|
$this->logger->debug(sprintf('Client Signing Public Key: %s', $this->clientSigningKeyPair->getPublicKey()));
|
||||||
|
$this->logger->debug(sprintf('Identified As: %s', $this->identifiedAs->getAddress()));
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
if (curl_errno($ch))
|
// If the response is false, the request failed
|
||||||
|
if($response === false)
|
||||||
{
|
{
|
||||||
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
|
|
||||||
// Separate headers and body
|
|
||||||
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
|
||||||
$response_body = substr($response, $header_size);
|
|
||||||
|
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
|
throw new RpcException(sprintf('Failed to create the session at %s, no response received', $this->rpcEndpoint));
|
||||||
// Throw exception with response body as message and status code as code
|
|
||||||
throw new RpcRequestException($response_body, $statusCode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
// If the response code is not 201, the request failed
|
||||||
|
$responseCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
||||||
|
if($responseCode !== 201)
|
||||||
|
{
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
// Separate headers and body
|
if(empty($response))
|
||||||
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
{
|
||||||
$response_headers = substr($response, 0, $header_size);
|
throw new RpcException(sprintf('Failed to create the session at %s, server responded with ' . $responseCode, $this->rpcEndpoint));
|
||||||
$response_body = substr($response, $header_size);
|
}
|
||||||
|
|
||||||
|
throw new RpcException(sprintf('Failed to create the session at %s, server responded with ' . $responseCode . ': ' . $response, $this->rpcEndpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the response is empty, the server did not provide a session UUID
|
||||||
|
if(empty($response))
|
||||||
|
{
|
||||||
|
curl_close($ch);
|
||||||
|
throw new RpcException(sprintf('Failed to create the session at %s, server did not return a session UUID', $this->rpcEndpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the Encryption Public Key from the server's response headers
|
||||||
|
$serverPublicEncryptionKey = $responseHeaders[strtolower(StandardHeaders::ENCRYPTION_PUBLIC_KEY->value)][0] ?? null;
|
||||||
|
|
||||||
|
// null check
|
||||||
|
if($serverPublicEncryptionKey === null)
|
||||||
|
{
|
||||||
|
curl_close($ch);
|
||||||
|
throw new RpcException('Failed to create session at %s, the server did not return a public encryption key');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->debug(sprintf('Server Encryption Public Key: %s', $serverPublicEncryptionKey));
|
||||||
|
|
||||||
|
// Validate the server's encryption public key
|
||||||
|
if(!Cryptography::validatePublicEncryptionKey($serverPublicEncryptionKey))
|
||||||
|
{
|
||||||
|
curl_close($ch);
|
||||||
|
throw new RpcException('The server did not provide a valid encryption public key');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the server did not provide an encryption public key, the response is invalid
|
||||||
|
// We can't preform the DHE without the server's encryption key.
|
||||||
|
if ($serverPublicEncryptionKey === null)
|
||||||
|
{
|
||||||
|
curl_close($ch);
|
||||||
|
throw new RpcException('The server did not provide a signature for the response');
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_close($ch);
|
||||||
|
$this->logger->debug(sprintf('Session UUID: %s', $response));
|
||||||
|
|
||||||
|
// Set the server's encryption key
|
||||||
|
$this->serverPublicEncryptionKey = $serverPublicEncryptionKey;
|
||||||
|
// Set the session UUID
|
||||||
|
$this->sessionUuid = $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a Diffie-Hellman Ephemeral (DHE) exchange request to the server.
|
||||||
|
*
|
||||||
|
* @throws RpcException If the encryption or the request fails.
|
||||||
|
*/
|
||||||
|
private function sendDheExchange(): void
|
||||||
|
{
|
||||||
|
// First preform the DHE
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$this->privateSharedSecret = Cryptography::performDHE($this->serverPublicEncryptionKey, $this->clientEncryptionKeyPair->getPrivateKey());
|
||||||
|
}
|
||||||
|
catch(CryptographyException $e)
|
||||||
|
{
|
||||||
|
throw new RpcException('Failed to preform DHE: ' . $e->getMessage(), StandardError::CRYPTOGRAPHIC_ERROR->value, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request body should contain the encrypted key, the client's public key, and the session UUID
|
||||||
|
// Upon success the server should return 204 without a body
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$encryptedKey = Cryptography::encryptShared($this->clientTransportEncryptionKey, $this->privateSharedSecret);
|
||||||
|
$signature = Cryptography::signMessage($this->clientTransportEncryptionKey, $this->clientSigningKeyPair->getPrivateKey());
|
||||||
|
}
|
||||||
|
catch (CryptographyException $e)
|
||||||
|
{
|
||||||
|
throw new RpcException('Failed to encrypt DHE exchange data', StandardError::CRYPTOGRAPHIC_ERROR->value, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $this->rpcEndpoint);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
StandardHeaders::REQUEST_TYPE->value . ': ' . RequestType::DHE_EXCHANGE->value,
|
||||||
|
StandardHeaders::SESSION_UUID->value . ': ' . $this->sessionUuid,
|
||||||
|
StandardHeaders::SIGNATURE->value . ': ' . $signature
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $encryptedKey);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
if($response === false)
|
||||||
|
{
|
||||||
|
curl_close($ch);
|
||||||
|
throw new RpcException('Failed to send DHE exchange, no response received', StandardError::CRYPTOGRAPHIC_ERROR->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
||||||
|
if($responseCode !== 200)
|
||||||
|
{
|
||||||
|
curl_close($ch);
|
||||||
|
throw new RpcException('Failed to send DHE exchange, server responded with ' . $responseCode . ': ' . $response, StandardError::CRYPTOGRAPHIC_ERROR->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$this->serverTransportEncryptionKey = Cryptography::decryptShared($response, $this->privateSharedSecret);
|
||||||
|
}
|
||||||
|
catch(CryptographyException $e)
|
||||||
|
{
|
||||||
|
throw new RpcException('Failed to decrypt DHE exchange data', 0, $e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends an RPC request with the given JSON data.
|
||||||
|
*
|
||||||
|
* @param string $jsonData The JSON data to be sent in the request.
|
||||||
|
* @param string|null $identifiedAs Optional. The username to identify as, usually the requesting peer. Required for server-to-server communication.
|
||||||
|
* @return RpcResult[] An array of RpcResult objects.
|
||||||
|
* @throws RpcException If the request fails, the response is invalid, or the decryption/signature verification fails.
|
||||||
|
*/
|
||||||
|
public function sendRawRequest(string $jsonData, ?string $identifiedAs=null): array
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$encryptedData = Cryptography::encryptMessage(
|
||||||
|
message: $jsonData,
|
||||||
|
encryptionKey: $this->serverTransportEncryptionKey,
|
||||||
|
algorithm: $this->serverInformation->getTransportEncryptionAlgorithm()
|
||||||
|
);
|
||||||
|
|
||||||
|
$signature = Cryptography::signMessage(
|
||||||
|
message: $jsonData,
|
||||||
|
privateKey: $this->clientSigningKeyPair->getPrivateKey(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (CryptographyException $e)
|
||||||
|
{
|
||||||
|
throw new RpcException('Failed to encrypt request data: ' . $e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
$returnHeaders = [];
|
||||||
|
$headers = [
|
||||||
|
StandardHeaders::REQUEST_TYPE->value . ': ' . RequestType::RPC->value,
|
||||||
|
StandardHeaders::SESSION_UUID->value . ': ' . $this->sessionUuid,
|
||||||
|
StandardHeaders::SIGNATURE->value . ': ' . $signature
|
||||||
|
];
|
||||||
|
|
||||||
|
if($identifiedAs)
|
||||||
|
{
|
||||||
|
$headers[] = StandardHeaders::IDENTIFY_AS->value . ': ' . $identifiedAs;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $this->rpcEndpoint);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use (&$returnHeaders)
|
||||||
|
{
|
||||||
|
$len = strlen($header);
|
||||||
|
$header = explode(':', $header, 2);
|
||||||
|
if (count($header) < 2) // ignore invalid returnHeaders
|
||||||
|
{
|
||||||
|
return $len;
|
||||||
|
}
|
||||||
|
|
||||||
|
$returnHeaders[strtolower(trim($header[0]))][] = trim($header[1]);
|
||||||
|
return $len;
|
||||||
|
});
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $encryptedData);
|
||||||
|
|
||||||
|
$this->logger->debug(sprintf('Sending RPC request to %s', $this->rpcEndpoint));
|
||||||
|
$this->logger->debug(sprintf('Headers: %s', json_encode($headers)));
|
||||||
|
$this->logger->debug(sprintf('Encrypted Data Size: %d', strlen($encryptedData)));
|
||||||
|
$this->logger->debug(sprintf('Request Signature: %s', $signature));
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
if ($response === false)
|
||||||
|
{
|
||||||
|
curl_close($ch);
|
||||||
|
throw new RpcException('Failed to send request, no response received');
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
||||||
|
$responseString = $response;
|
||||||
|
|
||||||
|
if (!Utilities::isSuccessCodes($responseCode))
|
||||||
|
{
|
||||||
|
curl_close($ch);
|
||||||
|
if (!empty($responseString))
|
||||||
|
{
|
||||||
|
throw new RpcException($responseString);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RpcException('Failed to send request (Empty Response): ' . $responseCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($responseCode == 204)
|
||||||
|
{
|
||||||
|
curl_close($ch);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($responseString))
|
||||||
|
{
|
||||||
|
curl_close($ch);
|
||||||
|
throw new RpcException('The request was successful but the server did not indicate an empty response');
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_close($ch);
|
||||||
|
$this->logger->debug(sprintf('Encrypted Response Size: %d', strlen($responseString)));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$decryptedResponse = Cryptography::decryptMessage(
|
||||||
|
encryptedMessage: $responseString,
|
||||||
|
encryptionKey: $this->clientTransportEncryptionKey,
|
||||||
|
algorithm: $this->serverInformation->getTransportEncryptionAlgorithm()
|
||||||
|
);
|
||||||
|
$this->logger->debug(sprintf('Decrypted Response: %s', $decryptedResponse));
|
||||||
|
}
|
||||||
|
catch (CryptographyException $e)
|
||||||
|
{
|
||||||
|
throw new RpcException('Failed to decrypt response: ' . $e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$signature = $returnHeaders[strtolower(StandardHeaders::SIGNATURE->value)][0] ?? null;
|
||||||
|
$this->logger->debug(sprintf('Response Signature: %s', $signature));
|
||||||
|
if ($signature === null)
|
||||||
|
{
|
||||||
|
throw new RpcException('The server did not provide a signature for the response');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(!Cryptography::verifyMessage(
|
||||||
|
message: $decryptedResponse,
|
||||||
|
signature: $signature,
|
||||||
|
publicKey: $this->serverPublicSigningKey,
|
||||||
|
))
|
||||||
|
{
|
||||||
|
throw new RpcException('Failed to verify the response signature');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CryptographyException $e)
|
||||||
|
{
|
||||||
|
throw new RpcException('Failed to verify the response signature: ' . $e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$decoded = json_decode($decryptedResponse, true);
|
||||||
|
if(isset($decoded['id']))
|
||||||
|
{
|
||||||
|
return [new RpcResult($decoded)];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$results = [];
|
||||||
|
foreach ($decoded as $responseMap)
|
||||||
|
{
|
||||||
|
$results[] = new RpcResult($responseMap);
|
||||||
|
}
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves server information by making an RPC request.
|
||||||
|
*
|
||||||
|
* @return ServerInformation The parsed server information received in the response.
|
||||||
|
* @throws RpcException If the request fails, no response is received, or the server returns an error status code or invalid data.
|
||||||
|
*/
|
||||||
|
public function getServerInformation(): ServerInformation
|
||||||
|
{
|
||||||
|
$ch = curl_init();
|
||||||
|
$headers = [
|
||||||
|
StandardHeaders::REQUEST_TYPE->value . ': ' . RequestType::INFO->value,
|
||||||
|
StandardHeaders::CLIENT_NAME->value . ': ' . self::CLIENT_NAME,
|
||||||
|
StandardHeaders::CLIENT_VERSION->value . ': ' . self::CLIENT_VERSION,
|
||||||
|
];
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $this->rpcEndpoint);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||||
|
|
||||||
|
$this->logger->debug(sprintf('Getting server information from %s', $this->rpcEndpoint));
|
||||||
|
$this->logger->debug(sprintf('Headers: %s', json_encode($headers)));
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
if($response === false)
|
||||||
|
{
|
||||||
|
curl_close($ch);
|
||||||
|
throw new RpcException(sprintf('Failed to get server information at %s, no response received', $this->rpcEndpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
||||||
|
if($responseCode !== 200)
|
||||||
|
{
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if(empty($response))
|
||||||
|
{
|
||||||
|
throw new RpcException(sprintf('Failed to get server information at %s, server responded with ' . $responseCode, $this->rpcEndpoint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_close($ch);
|
||||||
|
return ServerInformation::fromArray(json_decode($response, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends an RPC request and retrieves the corresponding RPC response.
|
||||||
|
*
|
||||||
|
* @param RpcRequest $request The RPC request to be sent.
|
||||||
|
* @param bool $throwException Optional. Whether to throw an exception if the response contains an error.
|
||||||
|
* @param string|null $identifiedAs Optional. The username to identify as, usually the requesting peer. Required for server-to-server communication.
|
||||||
|
* @return RpcResult The received RPC response.
|
||||||
|
* @throws RpcException If no response is received from the request.
|
||||||
|
*/
|
||||||
|
public function sendRequest(RpcRequest $request, bool $throwException=true, ?string $identifiedAs=null): RpcResult
|
||||||
|
{
|
||||||
|
$response = $this->sendRawRequest(json_encode($request->toArray()), $identifiedAs);
|
||||||
|
|
||||||
|
if (count($response) === 0)
|
||||||
|
{
|
||||||
|
throw new RpcException('Failed to send request, no response received');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($throwException)
|
||||||
|
{
|
||||||
|
if($response[0]->getError() !== null)
|
||||||
|
{
|
||||||
|
throw $response[0]->getError()->toRpcException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a batch of requests to the server, processes them into an appropriate format,
|
||||||
|
* and handles the response.
|
||||||
|
*
|
||||||
|
* @param RpcRequest[] $requests An array of RpcRequest objects to be sent to the server.
|
||||||
|
* @param string|null $identifiedAs Optional. The username to identify as, usually the requesting peer. Required for server-to-server communication.
|
||||||
|
* @return RpcResult[] An array of RpcResult objects received from the server.
|
||||||
|
* @throws RpcException If no response is received from the server.
|
||||||
|
*/
|
||||||
|
public function sendRequests(array $requests, ?string $identifiedAs=null): array
|
||||||
|
{
|
||||||
|
$parsedRequests = [];
|
||||||
|
foreach ($requests as $request)
|
||||||
|
{
|
||||||
|
$parsedRequests[] = $request->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
$responses = $this->sendRawRequest(json_encode($parsedRequests), $identifiedAs);
|
||||||
|
|
||||||
|
if (count($responses) === 0)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $responses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the peer address identified for the instance.
|
||||||
|
*
|
||||||
|
* @return PeerAddress The identified peer address.
|
||||||
|
*/
|
||||||
|
public function getIdentifiedAs(): PeerAddress
|
||||||
|
{
|
||||||
|
return $this->identifiedAs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the server's public signing key.
|
||||||
|
*
|
||||||
|
* @return string The server's public signing key.
|
||||||
|
*/
|
||||||
|
public function getServerPublicSigningKey(): string
|
||||||
|
{
|
||||||
|
return $this->serverPublicSigningKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the server's public encryption key.
|
||||||
|
*
|
||||||
|
* @return string The public encryption key of the server.
|
||||||
|
*/
|
||||||
|
public function getServerPublicEncryptionKey(): string
|
||||||
|
{
|
||||||
|
return $this->serverPublicEncryptionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the client's signing key pair.
|
||||||
|
*
|
||||||
|
* @return KeyPair The client's signing key pair.
|
||||||
|
*/
|
||||||
|
public function getClientSigningKeyPair(): KeyPair
|
||||||
|
{
|
||||||
|
return $this->clientSigningKeyPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the client encryption key pair configured for the instance.
|
||||||
|
*
|
||||||
|
* @return KeyPair The client encryption key pair.
|
||||||
|
*/
|
||||||
|
public function getClientEncryptionKeyPair(): KeyPair
|
||||||
|
{
|
||||||
|
return $this->clientEncryptionKeyPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the private shared secret configured for the instance.
|
||||||
|
*
|
||||||
|
* @return string The private shared secret value.
|
||||||
|
*/
|
||||||
|
public function getPrivateSharedSecret(): string
|
||||||
|
{
|
||||||
|
return $this->privateSharedSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the client transport encryption key configured for the instance.
|
||||||
|
*
|
||||||
|
* @return string The client transport encryption key value.
|
||||||
|
*/
|
||||||
|
public function getClientTransportEncryptionKey(): string
|
||||||
|
{
|
||||||
|
return $this->clientTransportEncryptionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the server transport encryption key.
|
||||||
|
*
|
||||||
|
* @return string The server transport encryption key.
|
||||||
|
*/
|
||||||
|
public function getServerTransportEncryptionKey(): string
|
||||||
|
{
|
||||||
|
return $this->serverTransportEncryptionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the RPC endpoint configured for the instance.
|
||||||
|
*
|
||||||
|
* @return string The RPC endpoint value.
|
||||||
|
*/
|
||||||
|
public function getRpcEndpoint(): string
|
||||||
|
{
|
||||||
|
return $this->rpcEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the remote server address configured for the instance.
|
||||||
|
*
|
||||||
|
* @return string The remote server address.
|
||||||
|
*/
|
||||||
|
public function getRemoteServer(): string
|
||||||
|
{
|
||||||
|
return $this->remoteServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the session UUID associated with the instance.
|
||||||
|
*
|
||||||
|
* @return string The session UUID value.
|
||||||
|
*/
|
||||||
|
public function getSessionUuid(): string
|
||||||
|
{
|
||||||
|
return $this->sessionUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the signing keys associated with the current instance.
|
||||||
|
*
|
||||||
|
* @return SignatureKeyPair[] The signing keys.
|
||||||
|
*/
|
||||||
|
public function getSigningKeys(): array
|
||||||
|
{
|
||||||
|
return $this->signingKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new signing key to the current instance.
|
||||||
|
*
|
||||||
|
* @param SignatureKeyPair $key The signing key to be added.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function addSigningKey(SignatureKeyPair $key): void
|
||||||
|
{
|
||||||
|
$this->signingKeys[$key->getUuid()] = $key;
|
||||||
|
|
||||||
|
if($this->defaultSigningKey === null)
|
||||||
|
{
|
||||||
|
$this->defaultSigningKey = $key->getUuid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $uuid
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function signingKeyExists(string $uuid): bool
|
||||||
|
{
|
||||||
|
return isset($this->signingKeys[$uuid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a signing key from the current instance.
|
||||||
|
*
|
||||||
|
* @param string $uuid The UUID of the signing key to be removed.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function removeSigningKey(string $uuid): void
|
||||||
|
{
|
||||||
|
unset($this->signingKeys[$uuid]);
|
||||||
|
|
||||||
|
if($this->defaultSigningKey === $uuid)
|
||||||
|
{
|
||||||
|
$this->defaultSigningKey = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the signing key associated with the specified UUID.
|
||||||
|
*
|
||||||
|
* @param string $uuid The UUID of the signing key to be retrieved.
|
||||||
|
* @return SignatureKeyPair|null The signing key associated with the UUID, or null if not found.
|
||||||
|
*/
|
||||||
|
public function getSigningKey(string $uuid): ?SignatureKeyPair
|
||||||
|
{
|
||||||
|
return $this->signingKeys[$uuid] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a signing key from the current instance.
|
||||||
|
*
|
||||||
|
* @param string $uuid The UUID of the signing key to be deleted.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function deleteSigningKey(string $uuid): void
|
||||||
|
{
|
||||||
|
unset($this->signingKeys[$uuid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the default signing key associated with the current instance.
|
||||||
|
*
|
||||||
|
* @return SignatureKeyPair|null The default signing key.
|
||||||
|
*/
|
||||||
|
public function getDefaultSigningKey(): ?SignatureKeyPair
|
||||||
|
{
|
||||||
|
return $this->signingKeys[$this->defaultSigningKey] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the default signing key for the current instance.
|
||||||
|
*
|
||||||
|
* @param string $uuid The UUID of the signing key to be set as default.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setDefaultSigningKey(string $uuid): void
|
||||||
|
{
|
||||||
|
if(!isset($this->signingKeys[$uuid]))
|
||||||
|
{
|
||||||
|
throw new InvalidArgumentException('The specified signing key does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->defaultSigningKey = $uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the encryption channel keys associated with the current instance.
|
||||||
|
*
|
||||||
|
* @return EncryptionChannelSecret[] The encryption channel keys.
|
||||||
|
*/
|
||||||
|
public function getEncryptionChannelSecrets(): array
|
||||||
|
{
|
||||||
|
return $this->encryptionChannelSecrets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEncryptionChannelSecret(string $channelUuid): ?EncryptionChannelSecret
|
||||||
|
{
|
||||||
|
return $this->encryptionChannelSecrets[$channelUuid] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new encryption channel key to the current instance.
|
||||||
|
*
|
||||||
|
* @param EncryptionChannelSecret $key The encryption channel key to be added.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addEncryptionChannelSecret(EncryptionChannelSecret $key): void
|
||||||
|
{
|
||||||
|
$this->encryptionChannelSecrets[$key->getChannelUuid()] = $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an encryption channel key from the current instance.
|
||||||
|
*
|
||||||
|
* @param string $channelUuid The UUID of the encryption channel key to be removed.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function removeEncryptionChannelKey(string $channelUuid): void
|
||||||
|
{
|
||||||
|
unset($this->encryptionChannelSecrets[$channelUuid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the encryption channel key associated with the specified UUID.
|
||||||
|
*
|
||||||
|
* @param string $channelUuid The UUID of the encryption channel key to be retrieved.
|
||||||
|
* @return EncryptionChannelSecret|null The encryption channel key associated with the UUID, or null if not found.
|
||||||
|
*/
|
||||||
|
public function getEncryptionChannelKey(string $channelUuid): ?EncryptionChannelSecret
|
||||||
|
{
|
||||||
|
return $this->encryptionChannelSecrets[$channelUuid] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if an encryption channel key exists with the specified UUID.
|
||||||
|
*
|
||||||
|
* @param string $channelUuid The UUID of the encryption channel key to check.
|
||||||
|
* @return bool True if the encryption channel key exists, false otherwise.
|
||||||
|
*/
|
||||||
|
public function encryptionChannelKeyExists(string $channelUuid): bool
|
||||||
|
{
|
||||||
|
return isset($this->encryptionChannelSecrets[$channelUuid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes an encryption channel key from the current instance.
|
||||||
|
*
|
||||||
|
* @param string $channelUuid The UUID of the encryption channel key to be deleted.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function deleteEncryptionChannelKey(string $channelUuid): void
|
||||||
|
{
|
||||||
|
unset($this->encryptionChannelSecrets[$channelUuid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the current session details into an ExportedSession object.
|
||||||
|
*
|
||||||
|
* @return ExportedSession The exported session containing session-specific details.
|
||||||
|
*/
|
||||||
|
public function exportSession(): ExportedSession
|
||||||
|
{
|
||||||
|
return new ExportedSession([
|
||||||
|
'peer_address' => $this->identifiedAs->getAddress(),
|
||||||
|
'rpc_endpoint' => $this->rpcEndpoint,
|
||||||
|
'remote_server' => $this->remoteServer,
|
||||||
|
'session_uuid' => $this->sessionUuid,
|
||||||
|
'transport_encryption_algorithm' => $this->serverInformation->getTransportEncryptionAlgorithm(),
|
||||||
|
'server_keypair_expires' => $this->serverInformation->getServerKeypairExpires(),
|
||||||
|
'server_public_signing_key' => $this->serverPublicSigningKey,
|
||||||
|
'server_public_encryption_key' => $this->serverPublicEncryptionKey,
|
||||||
|
'client_public_signing_key' => $this->clientSigningKeyPair->getPublicKey(),
|
||||||
|
'client_private_signing_key' => $this->clientSigningKeyPair->getPrivateKey(),
|
||||||
|
'client_public_encryption_key' => $this->clientEncryptionKeyPair->getPublicKey(),
|
||||||
|
'client_private_encryption_key' => $this->clientEncryptionKeyPair->getPrivateKey(),
|
||||||
|
'private_shared_secret' => $this->privateSharedSecret,
|
||||||
|
'client_transport_encryption_key' => $this->clientTransportEncryptionKey,
|
||||||
|
'server_transport_encryption_key' => $this->serverTransportEncryptionKey,
|
||||||
|
'default_signing_key' => $this->defaultSigningKey,
|
||||||
|
'signing_keys' => array_map(fn(SignatureKeyPair $key) => $key->toArray(), $this->signingKeys),
|
||||||
|
'encryption_channel_secrets' => array_map(fn(EncryptionChannelSecret $key) => $key->toArray(), $this->encryptionChannelSecrets)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,195 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Socialbox\Classes;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use RuntimeException;
|
|
||||||
use Socialbox\Enums\StandardHeaders;
|
|
||||||
use Socialbox\Exceptions\CryptographyException;
|
|
||||||
use Socialbox\Exceptions\DatabaseOperationException;
|
|
||||||
use Socialbox\Exceptions\RpcException;
|
|
||||||
use Socialbox\Exceptions\StandardException;
|
|
||||||
use Socialbox\Managers\SessionManager;
|
|
||||||
use Socialbox\Objects\ClientRequest;
|
|
||||||
use Socialbox\Objects\RpcRequest;
|
|
||||||
|
|
||||||
class RpcHandler
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Gets the incoming ClientRequest object, validates if the request is valid & if a session UUID is provided
|
|
||||||
* checks if the request signature matches the client's provided public key.
|
|
||||||
*
|
|
||||||
* @return ClientRequest The parsed ClientRequest object
|
|
||||||
* @throws RpcException Thrown if the request is invalid
|
|
||||||
*/
|
|
||||||
public static function getClientRequest(): ClientRequest
|
|
||||||
{
|
|
||||||
if($_SERVER['REQUEST_METHOD'] !== 'POST')
|
|
||||||
{
|
|
||||||
throw new RpcException('Invalid Request Method, expected POST', 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$headers = Utilities::getRequestHeaders();
|
|
||||||
foreach(StandardHeaders::getRequiredHeaders() as $header)
|
|
||||||
{
|
|
||||||
if (!isset($headers[$header]))
|
|
||||||
{
|
|
||||||
throw new RpcException("Missing required header: $header", 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the headers
|
|
||||||
switch(StandardHeaders::tryFrom($header))
|
|
||||||
{
|
|
||||||
case StandardHeaders::CLIENT_VERSION:
|
|
||||||
if($headers[$header] !== '1.0')
|
|
||||||
{
|
|
||||||
throw new RpcException(sprintf("Unsupported Client Version: %s", $headers[$header]));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case StandardHeaders::CONTENT_TYPE:
|
|
||||||
if(!str_contains($headers[$header], 'application/json'))
|
|
||||||
{
|
|
||||||
throw new RpcException(sprintf("Invalid Content-Type header: Expected application/json, got %s", $headers[$header]), 400);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case StandardHeaders::FROM_PEER:
|
|
||||||
if(!Validator::validatePeerAddress($headers[$header]))
|
|
||||||
{
|
|
||||||
throw new RpcException("Invalid From-Peer header: " . $headers[$header], 400);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(RuntimeException $e)
|
|
||||||
{
|
|
||||||
throw new RpcException("Failed to parse request: " . $e->getMessage(), 400, $e);
|
|
||||||
}
|
|
||||||
|
|
||||||
$clientRequest = new ClientRequest($headers, self::getRpcRequests(), self::getRequestHash());
|
|
||||||
|
|
||||||
// Verify the session & request signature
|
|
||||||
if($clientRequest->getSessionUuid() !== null)
|
|
||||||
{
|
|
||||||
// If no signature is provided, it must be required if the client is providing a Session UUID
|
|
||||||
if($clientRequest->getSignature() === null)
|
|
||||||
{
|
|
||||||
throw new RpcException(sprintf('Unauthorized request, signature required for session based requests'), 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$session = SessionManager::getSession($clientRequest->getSessionUuid());
|
|
||||||
|
|
||||||
// Verify the signature of the request
|
|
||||||
if(!Cryptography::verifyContent($clientRequest->getHash(), $clientRequest->getSignature(), $session->getPublicKey()))
|
|
||||||
{
|
|
||||||
throw new RpcException('Request signature check failed', 400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(StandardException $e)
|
|
||||||
{
|
|
||||||
throw new RpcException($e->getMessage(), 400);
|
|
||||||
}
|
|
||||||
catch(CryptographyException $e)
|
|
||||||
{
|
|
||||||
throw new RpcException('Request signature check failed (Cryptography Error)', 400, $e);
|
|
||||||
}
|
|
||||||
catch(DatabaseOperationException $e)
|
|
||||||
{
|
|
||||||
throw new RpcException('Failed to verify session', 500, $e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $clientRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the request hash by hashing the request body using SHA256
|
|
||||||
*
|
|
||||||
* @return string Returns the request hash in SHA256 representation
|
|
||||||
*/
|
|
||||||
private static function getRequestHash(): string
|
|
||||||
{
|
|
||||||
return hash('sha1', file_get_contents('php://input'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a POST request, returning an array of RpcRequest objects
|
|
||||||
* expects a JSON encoded body with either a single RpcRequest object or an array of RpcRequest objects
|
|
||||||
*
|
|
||||||
* @return RpcRequest[] The parsed RpcRequest objects
|
|
||||||
* @throws RpcException Thrown if the request is invalid
|
|
||||||
*/
|
|
||||||
private static function getRpcRequests(): array
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Decode the request body
|
|
||||||
$body = Utilities::jsonDecode(file_get_contents('php://input'));
|
|
||||||
}
|
|
||||||
catch(InvalidArgumentException $e)
|
|
||||||
{
|
|
||||||
throw new RpcException("Invalid JSON in request body: " . $e->getMessage(), 400, $e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isset($body['method']))
|
|
||||||
{
|
|
||||||
// If it only contains a method, we assume it's a single request
|
|
||||||
return [self::parseRequest($body)];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we assume it's an array of requests
|
|
||||||
return array_map(fn($request) => self::parseRequest($request), $body);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the raw request data into an RpcRequest object
|
|
||||||
*
|
|
||||||
* @param array $data The raw request data
|
|
||||||
* @return RpcRequest The parsed RpcRequest object
|
|
||||||
* @throws RpcException If the request is invalid
|
|
||||||
*/
|
|
||||||
private static function parseRequest(array $data): RpcRequest
|
|
||||||
{
|
|
||||||
if(!isset($data['method']))
|
|
||||||
{
|
|
||||||
throw new RpcException("Missing 'method' key in request", 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isset($data['id']))
|
|
||||||
{
|
|
||||||
if(!is_string($data['id']))
|
|
||||||
{
|
|
||||||
throw new RpcException("Invalid 'id' key in request: Expected string", 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(strlen($data['id']) === 0)
|
|
||||||
{
|
|
||||||
throw new RpcException("Invalid 'id' key in request: Expected non-empty string", 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(strlen($data['id']) > 8)
|
|
||||||
{
|
|
||||||
throw new RpcException("Invalid 'id' key in request: Expected string of length <= 8", 400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isset($data['parameters']))
|
|
||||||
{
|
|
||||||
if(!is_array($data['parameters']))
|
|
||||||
{
|
|
||||||
throw new RpcException("Invalid 'parameters' key in request: Expected array", 400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RpcRequest($data['method'], $data['id'] ?? null, $data['parameters'] ?? null);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,51 +2,153 @@
|
||||||
|
|
||||||
namespace Socialbox\Classes;
|
namespace Socialbox\Classes;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
use Socialbox\Exceptions\ResolutionException;
|
use Socialbox\Exceptions\ResolutionException;
|
||||||
use Socialbox\Objects\ResolvedServer;
|
use Socialbox\Managers\ResolvedDnsRecordsManager;
|
||||||
|
use Socialbox\Objects\DnsRecord;
|
||||||
|
|
||||||
class ServerResolver
|
class ServerResolver
|
||||||
{
|
{
|
||||||
/**
|
private static array $mockedRecords = [];
|
||||||
* Resolves a given domain to fetch the RPC endpoint and public key from its DNS TXT records.
|
|
||||||
*
|
|
||||||
* @param string $domain The domain to be resolved.
|
|
||||||
* @return ResolvedServer An instance of ResolvedServer containing the endpoint and public key.
|
|
||||||
* @throws ResolutionException If the DNS TXT records cannot be resolved or if required information is missing.
|
|
||||||
*/
|
|
||||||
public static function resolveDomain(string $domain): ResolvedServer
|
|
||||||
{
|
|
||||||
$txtRecords = dns_get_record($domain, DNS_TXT);
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a domain by retrieving and parsing its DNS TXT records.
|
||||||
|
* Optionally checks a database for cached resolution data before performing a DNS query.
|
||||||
|
*
|
||||||
|
* @param string $domain The domain name to resolve.
|
||||||
|
* @param bool $useDatabase Whether to check the database for cached resolution data; defaults to true.
|
||||||
|
* @return DnsRecord The parsed DNS record for the given domain.
|
||||||
|
* @throws ResolutionException If the DNS TXT records cannot be retrieved or parsed.
|
||||||
|
* @throws DatabaseOperationException If an error occurs while interacting with the database. (Only if $useDatabase is true)
|
||||||
|
*/
|
||||||
|
public static function resolveDomain(string $domain, bool $useDatabase=true): DnsRecord
|
||||||
|
{
|
||||||
|
// Return the mocked record if the mocking record is set
|
||||||
|
if(isset(self::$mockedRecords[$domain]))
|
||||||
|
{
|
||||||
|
return self::$mockedRecords[$domain];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the mocked record from the configuration if one is set
|
||||||
|
if(isset(Configuration::getInstanceConfiguration()->getDnsMocks()[$domain]))
|
||||||
|
{
|
||||||
|
return DnsHelper::parseTxt(Configuration::getInstanceConfiguration()->getDnsMocks()[$domain]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the database if enabled
|
||||||
|
if ($useDatabase)
|
||||||
|
{
|
||||||
|
// Return from the database cache if one exists
|
||||||
|
// TODO: Implement renewal here
|
||||||
|
$resolvedServer = ResolvedDnsRecordsManager::getDnsRecord($domain);
|
||||||
|
if ($resolvedServer !== null)
|
||||||
|
{
|
||||||
|
return $resolvedServer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve DNS & Records
|
||||||
|
$txtRecords = self::dnsGetTxtRecords($domain);
|
||||||
if ($txtRecords === false)
|
if ($txtRecords === false)
|
||||||
{
|
{
|
||||||
throw new ResolutionException(sprintf("Failed to resolve DNS TXT records for %s", $domain));
|
throw new ResolutionException(sprintf("Failed to resolve DNS TXT records for %s", $domain));
|
||||||
}
|
}
|
||||||
|
$fullRecord = self::concatenateTxtRecords($txtRecords);
|
||||||
|
|
||||||
$endpoint = null;
|
try
|
||||||
$publicKey = null;
|
{
|
||||||
|
// Parse the TXT record using DnsHelper
|
||||||
|
$record = DnsHelper::parseTxt($fullRecord);
|
||||||
|
|
||||||
|
// Cache the resolved server record in the database
|
||||||
|
if($useDatabase)
|
||||||
|
{
|
||||||
|
ResolvedDnsRecordsManager::addResolvedServer($domain, $record);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $record;
|
||||||
|
}
|
||||||
|
catch (InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
throw new ResolutionException(sprintf("Failed to find valid SocialBox record for %s: %s", $domain, $e->getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the TXT records for a given domain using the dns_get_record function.
|
||||||
|
*
|
||||||
|
* @param string $domain The domain name to fetch TXT records for.
|
||||||
|
* @return array|false An array of DNS TXT records on success, or false on failure.
|
||||||
|
*/
|
||||||
|
private static function dnsGetTxtRecords(string $domain): array|false
|
||||||
|
{
|
||||||
|
return @dns_get_record($domain, DNS_TXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenates an array of TXT records into a single string, filtering for SocialBox records.
|
||||||
|
*
|
||||||
|
* @param array $txtRecords An array of TXT records, where each record is expected to have a 'txt' key.
|
||||||
|
* @return string A concatenated string of all relevant TXT records.
|
||||||
|
*/
|
||||||
|
private static function concatenateTxtRecords(array $txtRecords): string
|
||||||
|
{
|
||||||
|
$fullRecordBuilder = '';
|
||||||
foreach ($txtRecords as $txt)
|
foreach ($txtRecords as $txt)
|
||||||
{
|
{
|
||||||
if (isset($txt['txt']) && str_starts_with($txt['txt'], 'socialbox='))
|
if (isset($txt['txt']))
|
||||||
{
|
{
|
||||||
$endpoint = substr($txt['txt'], strlen('socialbox='));
|
$record = trim($txt['txt'], '" ');
|
||||||
}
|
// Only include records that start with v=socialbox
|
||||||
elseif (isset($txt['txt']) && str_starts_with($txt['txt'], 'socialbox-key='))
|
if (stripos($record, 'v=socialbox') === 0)
|
||||||
{
|
{
|
||||||
$publicKey = substr($txt['txt'], strlen('socialbox-key='));
|
$fullRecordBuilder .= $record;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return $fullRecordBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
if ($endpoint === null)
|
/**
|
||||||
|
* Retrieves the mocked records.
|
||||||
|
*
|
||||||
|
* @return array The list of mocked records.
|
||||||
|
*/
|
||||||
|
public static function getMockedRecords(): array
|
||||||
{
|
{
|
||||||
throw new ResolutionException(sprintf("Failed to resolve RPC endpoint for %s", $domain));
|
return self::$mockedRecords;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($publicKey === null)
|
/**
|
||||||
|
* Adds a mock DNS record for a specific domain.
|
||||||
|
*
|
||||||
|
* @param string $domain The domain name for which the DNS record is being mocked.
|
||||||
|
* @param DnsRecord $record The DNS record to be associated with the specified domain.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function addMock(string $domain, DnsRecord|string $record): void
|
||||||
{
|
{
|
||||||
throw new ResolutionException(sprintf("Failed to resolve public key for %s", $domain));
|
if(isset(self::$mockedRecords[$domain]))
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ResolvedServer($endpoint, $publicKey);
|
if(is_string($record))
|
||||||
|
{
|
||||||
|
$record = DnsHelper::parseTxt($record);
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$mockedRecords[$domain] = $record;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all mocked records by resetting the mocked records array.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function clearMockedRecords(): void
|
||||||
|
{
|
||||||
|
self::$mockedRecords = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\AddressBook;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Enums\Types\ContactRelationshipType;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\ContactManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
|
||||||
|
class AddressBookAddContact extends Method
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Adds a contact to the authenticated peer's address book, returns True if the contact was added
|
||||||
|
* false if the contact already exists.
|
||||||
|
*
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('peer'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$address = PeerAddress::fromAddress($rpcRequest->getParameter('peer'));
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('peer', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($rpcRequest->containsParameter('relationship') && $rpcRequest->getParameter('relationship') !== null)
|
||||||
|
{
|
||||||
|
$relationship = ContactRelationshipType::tryFrom(strtoupper($rpcRequest->getParameter('relationship')));
|
||||||
|
if($relationship === null)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('relationship');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$relationship = ContactRelationshipType::MUTUAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$peer = $request->getPeer();
|
||||||
|
if($peer->getAddress() == $address)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'Cannot add self as contact');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the peer, this would throw a StandardException if something goes wrong
|
||||||
|
Socialbox::resolvePeer($address);
|
||||||
|
|
||||||
|
// Check if the contact already exists
|
||||||
|
if(ContactManager::isContact($peer, $address))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceResponse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the contact
|
||||||
|
ContactManager::createContact($peer, $address, $relationship);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to add contact', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return success
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\AddressBook;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\ContactManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
|
class AddressBookContactExists extends Method
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns True if the contact exists in the address book, False otherwise.
|
||||||
|
*
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('peer'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$address = PeerAddress::fromAddress($rpcRequest->getParameter('peer'));
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('peer', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$peer = $request->getPeer();
|
||||||
|
return $rpcRequest->produceResponse(ContactManager::isContact($peer, $address));
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to check if the contact exists', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\AddressBook;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\ContactManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
|
class AddressBookDeleteContact extends Method
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Deletes a contact from the authenticated peer's address book, returns True if the contact was deleted
|
||||||
|
* false if the contact does not exist.
|
||||||
|
*
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('peer'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$address = PeerAddress::fromAddress($rpcRequest->getParameter('peer'));
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('peer', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Check if the contact already exists
|
||||||
|
$peer = $request->getPeer();
|
||||||
|
if(!ContactManager::isContact($peer, $address))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceResponse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the contact
|
||||||
|
ContactManager::deleteContact($peer, $address);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to remove contact', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return success
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\AddressBook;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\ContactManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
|
class AddressBookGetContact extends Method
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns the contact information for the given peer address.
|
||||||
|
*
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('peer'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$address = PeerAddress::fromAddress($rpcRequest->getParameter('peer'));
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('peer', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(!ContactManager::isContact($request->getPeer(), $address))
|
||||||
|
{
|
||||||
|
// Return empty response if the contact does not exist
|
||||||
|
return $rpcRequest->produceResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
$rpcRequest->produceResponse(ContactManager::getStandardContact($request->getPeer(), $address));
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to get contact', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\AddressBook;
|
||||||
|
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Classes\Configuration;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\ContactManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
|
class AddressBookGetContacts extends Method
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns the contacts in the address book.
|
||||||
|
*
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
$limit = Configuration::getPoliciesConfiguration()->getGetContactsLimit();
|
||||||
|
if($rpcRequest->containsParameter('limit', true))
|
||||||
|
{
|
||||||
|
$limit = (int)$rpcRequest->getParameter('limit');
|
||||||
|
if($limit <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('limit', 'Invalid limit, must be greater than 0');
|
||||||
|
}
|
||||||
|
|
||||||
|
$limit = min($limit, Configuration::getPoliciesConfiguration()->getGetContactsLimit());
|
||||||
|
}
|
||||||
|
|
||||||
|
$page = 0;
|
||||||
|
if($rpcRequest->containsParameter('page', true))
|
||||||
|
{
|
||||||
|
$page = (int)$rpcRequest->getParameter('page');
|
||||||
|
if($page < 0)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('page', 'Invalid page, must be greater than or equal to 0');
|
||||||
|
}
|
||||||
|
|
||||||
|
$page = max($page, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceResponse(ContactManager::getStandardContacts($request->getPeer(), $limit, $page));
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to get contacts', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\AddressBook;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\ContactManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Symfony\Component\Uid\Uuid;
|
||||||
|
|
||||||
|
class AddressBookRevokeSignature extends Method
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @noinspection DuplicatedCode
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('peer'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$address = PeerAddress::fromAddress($rpcRequest->getParameter('peer'));
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('peer', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('signature_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('signature_uuid');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$signatureUuid = Uuid::fromString($rpcRequest->getParameter('signature_uuid'));
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('signature_uuid', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Check if the contact already exists
|
||||||
|
$peer = $request->getPeer();
|
||||||
|
$contact = ContactManager::getContact($peer, $address);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to check contact state with calling peer', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($contact === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceResponse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(!ContactManager::contactSigningKeyUuidExists($contact, $signatureUuid))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceResponse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactManager::removeContactSigningKey($contact, $signatureUuid);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to remove contact signature', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return success
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\AddressBook;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Classes\Configuration;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\ContactManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
use Symfony\Component\Uid\Uuid;
|
||||||
|
|
||||||
|
class AddressBookTrustSignature extends Method
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('peer'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$address = PeerAddress::fromAddress($rpcRequest->getParameter('peer'));
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('peer', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('signature_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('signature_uuid');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$signatureUuid = Uuid::fromString($rpcRequest->getParameter('signature_uuid'));
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('signature_uuid', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$signingKey = Socialbox::resolvePeerSignature($address, $signatureUuid);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Check if the contact already exists
|
||||||
|
$peer = $request->getPeer();
|
||||||
|
if(!ContactManager::isContact($peer, $address))
|
||||||
|
{
|
||||||
|
ContactManager::createContact($peer, $address);
|
||||||
|
}
|
||||||
|
|
||||||
|
$contact = ContactManager::getContact($peer, $address);
|
||||||
|
if(ContactManager::contactGetSigningKeysCount($contact) > Configuration::getPoliciesConfiguration()->getMaxContactSigningKeys())
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The contact has exceeded the maximum amount of trusted signatures');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to check contact state with calling peer', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($signingKey === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The requested signature key was not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(ContactManager::contactSigningKeyUuidExists($contact, $signingKey->getUuid()))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceResponse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ContactManager::contactSigningKeyExists($contact, $signingKey->getPublicKey()))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceResponse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactManager::addContactSigningKey($contact, $signingKey);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to trust contact signature', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return success
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\AddressBook;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Enums\Types\ContactRelationshipType;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\ContactManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
|
class AddressBookUpdateRelationship extends Method
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('peer'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$address = PeerAddress::fromAddress($rpcRequest->getParameter('peer'));
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('peer', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('relationship'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('relationship');
|
||||||
|
}
|
||||||
|
$relationship = ContactRelationshipType::tryFrom(strtoupper($rpcRequest->getParameter('relationship')));
|
||||||
|
if($relationship === null)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('relationship');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Check if the contact already exists
|
||||||
|
$peer = $request->getPeer();
|
||||||
|
if(!ContactManager::isContact($peer, $address))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'Contact does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the contact
|
||||||
|
ContactManager::updateContactRelationship($peer, $address, $relationship);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to update contact relationship', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return success
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,50 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Socialbox\Classes\StandardMethods;
|
|
||||||
|
|
||||||
use Socialbox\Abstracts\Method;
|
|
||||||
use Socialbox\Enums\FirstLevelAuthentication;
|
|
||||||
use Socialbox\Enums\StandardError;
|
|
||||||
use Socialbox\Interfaces\SerializableInterface;
|
|
||||||
use Socialbox\Objects\ClientRequest;
|
|
||||||
use Socialbox\Objects\RpcRequest;
|
|
||||||
use Socialbox\Objects\RpcResponse;
|
|
||||||
|
|
||||||
class Authenticate extends Method
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
|
||||||
{
|
|
||||||
if(!isset($rpcRequest->getParameters()['type']))
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'Missing required parameter \'type\'');
|
|
||||||
}
|
|
||||||
|
|
||||||
if(strlen($rpcRequest->getParameters()['type']) == 0)
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'Parameter \'type\' cannot be empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
return match (FirstLevelAuthentication::tryFrom($rpcRequest->getParameters()['type']))
|
|
||||||
{
|
|
||||||
FirstLevelAuthentication::PASSWORD => self::handlePassword($request),
|
|
||||||
|
|
||||||
default => $rpcRequest->produceError(StandardError::UNSUPPORTED_AUTHENTICATION_TYPE,
|
|
||||||
sprintf('Unsupported authentication type: %s', $rpcRequest->getParameters()['type'])
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the password authentication phase for the peer
|
|
||||||
*
|
|
||||||
* @param ClientRequest $request
|
|
||||||
* @return SerializableInterface
|
|
||||||
*/
|
|
||||||
private static function handlePassword(ClientRequest $request): SerializableInterface
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\Core;
|
||||||
|
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Enums\StandardMethods;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
|
class GetAllowedMethods extends Method
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns a list of allowed methods for the current session.
|
||||||
|
*
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
$allowedMethods = [];
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach(StandardMethods::getAllowedMethods($request) as $method)
|
||||||
|
{
|
||||||
|
$allowedMethods[] = $method->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to retrieve allowed methods due to an internal exception', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse($allowedMethods);
|
||||||
|
}
|
||||||
|
}
|
32
src/Socialbox/Classes/StandardMethods/Core/GetSelf.php
Normal file
32
src/Socialbox/Classes/StandardMethods/Core/GetSelf.php
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\Core;
|
||||||
|
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\PeerInformationManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
|
class GetSelf extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$selfPeer = $request->getPeer();
|
||||||
|
return $rpcRequest->produceResponse($selfPeer->toStandardPeer(PeerInformationManager::getFields($selfPeer)));
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Unable to resolve self peer', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\Core;
|
||||||
|
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
|
class GetSessionState extends Method
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceResponse($request->getSession()->toStandardSessionState());
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to retrieve session state due to an internal exception', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
src/Socialbox/Classes/StandardMethods/Core/Ping.php
Normal file
19
src/Socialbox/Classes/StandardMethods/Core/Ping.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\Core;
|
||||||
|
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
|
class Ping extends Method
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
}
|
59
src/Socialbox/Classes/StandardMethods/Core/ResolvePeer.php
Normal file
59
src/Socialbox/Classes/StandardMethods/Core/ResolvePeer.php
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\Core;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Enums\ReservedUsernames;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
|
||||||
|
class ResolvePeer extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
// Check if the required 'peer' parameter is set.
|
||||||
|
if(!$rpcRequest->containsParameter('peer'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the peer address
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$peerAddress = PeerAddress::fromAddress($rpcRequest->getParameter('peer'));
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Peer Address Error: ' . $e->getMessage(), StandardError::RPC_INVALID_ARGUMENTS, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if host is making the request & the identifier is not empty
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$identifyAs = null;
|
||||||
|
if ($request->getPeer()->getUsername() == ReservedUsernames::HOST && $request->getIdentifyAs() != null)
|
||||||
|
{
|
||||||
|
$identifyAs = $request->getIdentifyAs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to retrieve peer information', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the peer using the server's peer resolver, this will resolve both internal peers and external peers
|
||||||
|
return $rpcRequest->produceResponse(Socialbox::resolvePeer($peerAddress, $identifyAs));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\Core;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Classes\Validator;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
use Symfony\Component\Uid\Uuid;
|
||||||
|
|
||||||
|
class ResolveSignature extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
// Check if the required 'peer' parameter is set.
|
||||||
|
if(!$rpcRequest->containsParameter('peer'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('signature_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('signature_uuid');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('signature_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('signature_uuid', 'Invalid UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(Socialbox::resolvePeerSignature(
|
||||||
|
$rpcRequest->getParameter('peer'), $rpcRequest->getParameter('signature_uuid')
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\Core;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Classes\Cryptography;
|
||||||
|
use Socialbox\Classes\Validator;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
|
||||||
|
class VerifySignature extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
// Check if the required 'peer' parameter is set.
|
||||||
|
if(!$rpcRequest->containsParameter('peer'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('signature_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('signature_uuid');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('signature_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('signature_uuid', 'Invalid UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('signature'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('signature');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('sha512'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('sha512');
|
||||||
|
}
|
||||||
|
elseif(!Cryptography::validateSha512($rpcRequest->getParameter('sha512')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('sha512', 'Invalid SHA512');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the peer address
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$peerAddress = PeerAddress::fromAddress($rpcRequest->getParameter('peer'));
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('peer', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($rpcRequest->containsParameter('time'))
|
||||||
|
{
|
||||||
|
if(!is_numeric($rpcRequest->getParameter('time')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('time', 'Invalid timestamp, must be a Unix Timestamp');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(Socialbox::verifyTimedSignature(
|
||||||
|
signingPeer: $peerAddress,
|
||||||
|
signatureUuid: $rpcRequest->getParameter('signature_uuid'),
|
||||||
|
signature: $rpcRequest->getParameter('signature'),
|
||||||
|
messageHash: $rpcRequest->getParameter('sha512'),
|
||||||
|
signatureTime: (int)$rpcRequest->getParameter('time')
|
||||||
|
)->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(Socialbox::verifySignature(
|
||||||
|
signingPeer: $peerAddress,
|
||||||
|
signatureUuid: $rpcRequest->getParameter('signature_uuid'),
|
||||||
|
signature: $rpcRequest->getParameter('signature'),
|
||||||
|
messageHash: $rpcRequest->getParameter('sha512'),
|
||||||
|
)->value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,47 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Socialbox\Classes\StandardMethods;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use Socialbox\Abstracts\Method;
|
|
||||||
use Socialbox\Enums\StandardError;
|
|
||||||
use Socialbox\Exceptions\DatabaseOperationException;
|
|
||||||
use Socialbox\Interfaces\SerializableInterface;
|
|
||||||
use Socialbox\Managers\SessionManager;
|
|
||||||
use Socialbox\Objects\ClientRequest;
|
|
||||||
use Socialbox\Objects\RpcRequest;
|
|
||||||
|
|
||||||
class CreateSession extends Method
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Executes the session creation process based on the provided public key.
|
|
||||||
*
|
|
||||||
* @param ClientRequest $request The client request object.
|
|
||||||
* @param RpcRequest $rpcRequest The RPC request containing parameters for execution.
|
|
||||||
* @return SerializableInterface|null Returns a response with the session UUID or an error.
|
|
||||||
*/
|
|
||||||
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
|
||||||
{
|
|
||||||
if(!$rpcRequest->containsParameter('public_key'))
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, 'Missing parameter \'public_key\'');
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$uuid = SessionManager::createSession($rpcRequest->getParameter('public_key'));
|
|
||||||
}
|
|
||||||
catch(DatabaseOperationException $e)
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::INTERNAL_SERVER_ERROR, 'There was an error while trying to create a new session: ' . $e->getMessage());
|
|
||||||
}
|
|
||||||
catch(InvalidArgumentException $e)
|
|
||||||
{
|
|
||||||
return $rpcRequest->produceError(StandardError::RPC_INVALID_ARGUMENTS, $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $rpcRequest->produceResponse([
|
|
||||||
'uuid' => $uuid
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\EncryptionChannel;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Classes\Cryptography;
|
||||||
|
use Socialbox\Classes\Logger;
|
||||||
|
use Socialbox\Classes\Validator;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Enums\Status\EncryptionChannelStatus;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\RpcException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\EncryptionChannelManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
|
||||||
|
class EncryptionAcceptChannel extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ($request->isExternal())
|
||||||
|
{
|
||||||
|
return self::handleExternal($request, $rpcRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::handleInternal($request, $rpcRequest);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('An error occurred while checking the request type', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClientRequest $request
|
||||||
|
* @param RpcRequest $rpcRequest
|
||||||
|
* @return SerializableInterface|null
|
||||||
|
* @throws StandardRpcException
|
||||||
|
*/
|
||||||
|
private static function handleInternal(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('channel_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('channel_uuid');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('channel_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('channel_uuid', 'The given channel uuid is not a valid UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('public_encryption_key'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('public_encryption_key');
|
||||||
|
}
|
||||||
|
elseif(!Cryptography::validatePublicEncryptionKey('public_encryption_key'))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('public_encryption_key', 'The given public encryption key is invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$receivingPeer = $request->getPeer();
|
||||||
|
$encryptionChannel = EncryptionChannelManager::getChannel($rpcRequest->getParameter('channel_uuid'));
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to obtain the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The requested encryption channel was not found');
|
||||||
|
}
|
||||||
|
elseif($encryptionChannel->getReceivingPeerAddress()->getAddress() !== $receivingPeer->getAddress())
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The requested encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
elseif($encryptionChannel->getStatus() !== EncryptionChannelStatus::AWAITING_RECEIVER)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The encryption channel is not awaiting the receiver');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel->getCallingPeerAddress()->isExternal())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$rpcClient = Socialbox::getExternalSession($encryptionChannel->getCallingPeerAddress()->getDomain());
|
||||||
|
$rpcClient->encryptionAcceptChannel(
|
||||||
|
channelUuid: $rpcRequest->getParameter('channel_uuid'),
|
||||||
|
publicEncryptionKey: $rpcRequest->getParameter('public_encryption_key'),
|
||||||
|
identifiedAs: $receivingPeer->getAddress()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::declineChannel($rpcRequest->getParameter('channel_uuid'), true);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
Logger::getLogger()->error('Error declining channel as server', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($e instanceof RpcException)
|
||||||
|
{
|
||||||
|
throw StandardRpcException::fromRpcException($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new StandardRpcException('There was an error while trying to notify the external server of the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::acceptChannel(
|
||||||
|
channelUuid: $rpcRequest->getParameter('channel_uuid'),
|
||||||
|
publicEncryptionKey: $rpcRequest->getParameter('public_encryption_key')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to accept the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClientRequest $request
|
||||||
|
* @param RpcRequest $rpcRequest
|
||||||
|
* @return SerializableInterface|null
|
||||||
|
* @throws StandardRpcException
|
||||||
|
*/
|
||||||
|
private static function handleExternal(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if($request->getIdentifyAs() === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::BAD_REQUEST, 'Missing required header IdentifyAs');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('channel_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('channel_uuid');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('channel_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('channel_uuid', 'The given channel uuid is not a valid UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('public_encryption_key'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('public_encryption_key');
|
||||||
|
}
|
||||||
|
elseif(!Cryptography::validatePublicEncryptionKey('public_encryption_key'))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('public_encryption_key', 'The given public encryption key is invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$receivingPeer = Socialbox::resolvePeer($request->getIdentifyAs());
|
||||||
|
$encryptionChannel = EncryptionChannelManager::getChannel($rpcRequest->getParameter('channel_uuid'));
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to obtain the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The requested encryption channel was not found');
|
||||||
|
}
|
||||||
|
elseif($encryptionChannel->getReceivingPeerAddress() !== $receivingPeer->getPeerAddress())
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The requested encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
elseif($encryptionChannel->getStatus() !== EncryptionChannelStatus::AWAITING_RECEIVER)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The encryption channel is not awaiting the receiver');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::acceptChannel(
|
||||||
|
channelUuid: $rpcRequest->getParameter('channel_uuid'),
|
||||||
|
publicEncryptionKey: $rpcRequest->getParameter('public_encryption_key')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to accept the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,221 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\EncryptionChannel;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Classes\Logger;
|
||||||
|
use Socialbox\Classes\Validator;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Enums\Status\EncryptionChannelStatus;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\RpcException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\EncryptionChannelManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\Database\EncryptionChannelRecord;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
|
||||||
|
class EncryptionChannelAcknowledgeMessage extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('channel_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('channel_uuid');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('channel_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('channel_uuid', 'The given channel uuid is not a valid UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('message_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('message_uuid');
|
||||||
|
}
|
||||||
|
elseif(!is_string($rpcRequest->getParameter('message_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('message_uuid', 'Must be type string');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('message_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('message_uuid', 'Invalid message UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$channelUuid = $rpcRequest->getParameter('channel_uuid');
|
||||||
|
$encryptionChannel = EncryptionChannelManager::getChannel($channelUuid);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to retrieve the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The encryption channel does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ($request->isExternal())
|
||||||
|
{
|
||||||
|
return self::handleExternal($request, $rpcRequest, $encryptionChannel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to acknowledge the message', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::handleInternal($request, $rpcRequest, $encryptionChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the external execution of the method.
|
||||||
|
*
|
||||||
|
* @param ClientRequest $request The client request instance.
|
||||||
|
* @param RpcRequest $rpcRequest The RPC request instance.
|
||||||
|
* @param EncryptionChannelRecord $encryptionChannel The encryption channel record.
|
||||||
|
* @return SerializableInterface|null The response to the request.
|
||||||
|
* @throws StandardRpcException If an error occurs.
|
||||||
|
*/
|
||||||
|
public static function handleExternal(ClientRequest $request, RpcRequest $rpcRequest, EncryptionChannelRecord $encryptionChannel): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if($request->getIdentifyAs() === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::BAD_REQUEST, 'The IdentifyAs header is missing');
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestingPeerAddress = $request->getIdentifyAs();
|
||||||
|
if(!$encryptionChannel->isParticipant($requestingPeerAddress))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$message = EncryptionChannelManager::getMessageRecord($rpcRequest->getParameter('channel_uuid'), $rpcRequest->getParameter('message_uuid'));
|
||||||
|
|
||||||
|
if($message === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The message does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($message->getReceiver($encryptionChannel)->getAddress() !== $requestingPeerAddress)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The message is not for the requesting peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
EncryptionChannelManager::acknowledgeMessage(
|
||||||
|
$rpcRequest->getParameter('channel_uuid'), $rpcRequest->getParameter('message_uuid')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to acknowledge the message', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the internal execution of the method.
|
||||||
|
*
|
||||||
|
* @param ClientRequest $request The client request instance.
|
||||||
|
* @param RpcRequest $rpcRequest The RPC request instance.
|
||||||
|
* @param EncryptionChannelRecord $encryptionChannel The encryption channel record.
|
||||||
|
* @return SerializableInterface|null The response to the request.
|
||||||
|
* @throws StandardRpcException If an error occurs.
|
||||||
|
*/
|
||||||
|
public static function handleInternal(ClientRequest $request, RpcRequest $rpcRequest, EncryptionChannelRecord $encryptionChannel): ?SerializableInterface
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$requestingPeer = $request->getPeer();
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to retrieve the peer', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($requestingPeer === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The peer is not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$encryptionChannel->isParticipant($requestingPeer->getAddress()))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
elseif($encryptionChannel->getStatus() !== EncryptionChannelStatus::OPENED)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The encryption channel is not opened');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$message = EncryptionChannelManager::getMessageRecord($rpcRequest->getParameter('channel_uuid'), $rpcRequest->getParameter('message_uuid'));
|
||||||
|
|
||||||
|
if($message === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The message does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($message->getReceiver($encryptionChannel)->getAddress() !== $requestingPeer->getAddress())
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The message is not for the requesting peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
EncryptionChannelManager::acknowledgeMessage(
|
||||||
|
$rpcRequest->getParameter('channel_uuid'), $rpcRequest->getParameter('message_uuid')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to acknowledge the message', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($message->getOwner($encryptionChannel)->isExternal())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$rpcClient = Socialbox::getExternalSession($message->getOwner($encryptionChannel)->getDomain());
|
||||||
|
$rpcClient->encryptionChannelAcknowledgeMessage(
|
||||||
|
channelUuid: $rpcRequest->getParameter('channel_uuid'),
|
||||||
|
messageUuid: $rpcRequest->getParameter('message_uuid'),
|
||||||
|
identifiedAs: $requestingPeer->getAddress()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::rejectMessage($rpcRequest->getParameter('channel_uuid'), $rpcRequest->getParameter('message_uuid'), true);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
Logger::getLogger()->error('Error rejecting message as server', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($e instanceof RpcException)
|
||||||
|
{
|
||||||
|
throw StandardRpcException::fromRpcException($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new StandardRpcException('Failed to acknowledge the message with the external server', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\EncryptionChannel;
|
||||||
|
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\EncryptionChannelManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
|
class EncryptionChannelExists extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('channel_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('channel_uuid');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceResponse(EncryptionChannelManager::channelUuidExists($rpcRequest->getParameter('channel_uuid')));
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('An error occurred while checking if the channel exists', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\EncryptionChannel;
|
||||||
|
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Classes\Validator;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Enums\Status\EncryptionChannelStatus;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\EncryptionChannelManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
|
class EncryptionChannelReceive extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('channel_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('channel_uuid');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('channel_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('channel_uuid', 'The given channel uuid is not a valid UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$channelUuid = $rpcRequest->getParameter('channel_uuid');
|
||||||
|
$encryptionChannel = EncryptionChannelManager::getChannel($channelUuid);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to retrieve the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The encryption channel does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$requestingPeer = $request->getPeer();
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to retrieve the peer', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($requestingPeer === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The peer is not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$encryptionChannel->isParticipant($requestingPeer->getAddress()))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
elseif($encryptionChannel->getStatus() !== EncryptionChannelStatus::OPENED)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The encryption channel is not opened');
|
||||||
|
}
|
||||||
|
|
||||||
|
$acknowledge = false;
|
||||||
|
if($rpcRequest->containsParameter('acknowledge') && is_bool($rpcRequest->getParameter('acknowledge')) && $rpcRequest->getParameter('acknowledge'))
|
||||||
|
{
|
||||||
|
$acknowledge = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
$messages = EncryptionChannelManager::receiveData(
|
||||||
|
$rpcRequest->getParameter('channel_uuid'), $encryptionChannel->determineRecipient($requestingPeer->getAddress(), true)
|
||||||
|
);
|
||||||
|
|
||||||
|
if($acknowledge)
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::acknowledgeMessagesBatch($rpcRequest->getParameter('channel_uuid'), array_map(fn($message) => $message->getUuid(), $messages));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to retrieve the messages', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(array_map(fn($message) => $message->toStandard(), $messages));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,221 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\EncryptionChannel;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Classes\Logger;
|
||||||
|
use Socialbox\Classes\Validator;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Enums\Status\EncryptionChannelStatus;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\RpcException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\EncryptionChannelManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\Database\EncryptionChannelRecord;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
|
||||||
|
class EncryptionChannelRejectMessage extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('channel_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('channel_uuid');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('channel_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('channel_uuid', 'The given channel uuid is not a valid UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('message_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('message_uuid');
|
||||||
|
}
|
||||||
|
elseif(!is_string($rpcRequest->getParameter('message_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('message_uuid', 'Must be type string');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('message_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('message_uuid', 'Invalid message UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$channelUuid = $rpcRequest->getParameter('channel_uuid');
|
||||||
|
$encryptionChannel = EncryptionChannelManager::getChannel($channelUuid);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to retrieve the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The encryption channel does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ($request->isExternal())
|
||||||
|
{
|
||||||
|
return self::handleExternal($request, $rpcRequest, $encryptionChannel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to reject the message', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::handleInternal($request, $rpcRequest, $encryptionChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the external execution of the method.
|
||||||
|
*
|
||||||
|
* @param ClientRequest $request The client request instance.
|
||||||
|
* @param RpcRequest $rpcRequest The RPC request instance.
|
||||||
|
* @param EncryptionChannelRecord $encryptionChannel The encryption channel record.
|
||||||
|
* @return SerializableInterface|null The response to the request.
|
||||||
|
* @throws StandardRpcException If an error occurs.
|
||||||
|
*/
|
||||||
|
public static function handleExternal(ClientRequest $request, RpcRequest $rpcRequest, EncryptionChannelRecord $encryptionChannel): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if($request->getIdentifyAs() === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::BAD_REQUEST, 'The IdentifyAs header is missing');
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestingPeerAddress = $request->getIdentifyAs();
|
||||||
|
if(!$encryptionChannel->isParticipant($requestingPeerAddress))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$message = EncryptionChannelManager::getMessageRecord($rpcRequest->getParameter('channel_uuid'), $rpcRequest->getParameter('message_uuid'));
|
||||||
|
|
||||||
|
if($message === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The message does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($message->getReceiver($encryptionChannel)->getAddress() !== $requestingPeerAddress)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The message is not for the requesting peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
EncryptionChannelManager::rejectMessage(
|
||||||
|
$rpcRequest->getParameter('channel_uuid'), $rpcRequest->getParameter('message_uuid')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to reject the message', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the internal execution of the method.
|
||||||
|
*
|
||||||
|
* @param ClientRequest $request The client request instance.
|
||||||
|
* @param RpcRequest $rpcRequest The RPC request instance.
|
||||||
|
* @param EncryptionChannelRecord $encryptionChannel The encryption channel record.
|
||||||
|
* @return SerializableInterface|null The response to the request.
|
||||||
|
* @throws StandardRpcException If an error occurs.
|
||||||
|
*/
|
||||||
|
public static function handleInternal(ClientRequest $request, RpcRequest $rpcRequest, EncryptionChannelRecord $encryptionChannel): ?SerializableInterface
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$requestingPeer = $request->getPeer();
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to retrieve the peer', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($requestingPeer === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The peer is not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$encryptionChannel->isParticipant($requestingPeer->getAddress()))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
elseif($encryptionChannel->getStatus() !== EncryptionChannelStatus::OPENED)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The encryption channel is not opened');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$message = EncryptionChannelManager::getMessageRecord($rpcRequest->getParameter('channel_uuid'), $rpcRequest->getParameter('message_uuid'));
|
||||||
|
|
||||||
|
if($message === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The message does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($message->getReceiver($encryptionChannel)->getAddress() !== $requestingPeer->getAddress())
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The message is not for the requesting peer');
|
||||||
|
}
|
||||||
|
|
||||||
|
EncryptionChannelManager::acknowledgeMessage(
|
||||||
|
$rpcRequest->getParameter('channel_uuid'), $rpcRequest->getParameter('message_uuid')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to acknowledge the message', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($message->getOwner($encryptionChannel)->isExternal())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$rpcClient = Socialbox::getExternalSession($message->getOwner($encryptionChannel)->getDomain());
|
||||||
|
$rpcClient->encryptionChannelRejectMessage(
|
||||||
|
channelUuid: $rpcRequest->getParameter('channel_uuid'),
|
||||||
|
messageUuid: $rpcRequest->getParameter('message_uuid'),
|
||||||
|
identifiedAs: $requestingPeer->getAddress()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::rejectMessage($rpcRequest->getParameter('channel_uuid'), $rpcRequest->getParameter('message_uuid'), true);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
Logger::getLogger()->error('Error rejecting message as server', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($e instanceof RpcException)
|
||||||
|
{
|
||||||
|
throw StandardRpcException::fromRpcException($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new StandardRpcException('Failed to acknowledge the message with the external server', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,237 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\EncryptionChannel;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Classes\Cryptography;
|
||||||
|
use Socialbox\Classes\Validator;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Enums\Status\EncryptionChannelStatus;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\RpcException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\EncryptionChannelManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
use Symfony\Component\Uid\Uuid;
|
||||||
|
|
||||||
|
class EncryptionChannelSend extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('channel_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('channel_uuid');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('channel_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('channel_uuid', 'The given channel uuid is not a valid UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ($request->isExternal())
|
||||||
|
{
|
||||||
|
return self::executeExternal($request, $rpcRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('An error occurred while checking the request type', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::executeInternal($request, $rpcRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClientRequest $request
|
||||||
|
* @param RpcRequest $rpcRequest
|
||||||
|
* @return SerializableInterface
|
||||||
|
* @throws StandardRpcException
|
||||||
|
*/
|
||||||
|
private static function executeInternal(ClientRequest $request, RpcRequest $rpcRequest): SerializableInterface
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$channelUuid = $rpcRequest->getParameter('channel_uuid');
|
||||||
|
$encryptionChannel = EncryptionChannelManager::getChannel($channelUuid);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to retrieve the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The encryption channel does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$requestingPeer = $request->getPeer();
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to retrieve the peer', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($requestingPeer === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The peer is not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$encryptionChannel->isParticipant($requestingPeer->getAddress()))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
elseif($encryptionChannel->getStatus() !== EncryptionChannelStatus::OPENED)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The encryption channel is not opened');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('checksum'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('checksum');
|
||||||
|
}
|
||||||
|
elseif(!Cryptography::validateSha512($rpcRequest->getParameter('checksum')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('checksum', 'The given checksum is not a valid SHA-512 checksum');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('data'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('data');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$messageUuid = Uuid::v4()->toRfc4122();
|
||||||
|
$messageTimestamp = time();
|
||||||
|
|
||||||
|
EncryptionChannelManager::sendMessage(
|
||||||
|
channelUuid: $channelUuid,
|
||||||
|
recipient: $encryptionChannel->determineRecipient($requestingPeer->getAddress()),
|
||||||
|
checksum: $rpcRequest->getParameter('checksum'),
|
||||||
|
data: $rpcRequest->getParameter('data'),
|
||||||
|
messageUuid: $messageUuid,
|
||||||
|
messageTimestamp: $messageTimestamp
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to send the message', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel->determineReceiver($requestingPeer->getAddress())->isExternal())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$rpcClient = Socialbox::getExternalSession($encryptionChannel->determineReceiver($requestingPeer->getAddress())->getDomain());
|
||||||
|
$rpcClient->encryptionChannelSend(
|
||||||
|
channelUuid: $rpcRequest->getParameter('channel_uuid'),
|
||||||
|
checksum: $rpcRequest->getParameter('checksum'),
|
||||||
|
data: $rpcRequest->getParameter('data'),
|
||||||
|
identifiedAs: $requestingPeer->getAddress(),
|
||||||
|
messageUuid: $messageUuid,
|
||||||
|
timestamp: $messageTimestamp
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
if($e instanceof RpcException)
|
||||||
|
{
|
||||||
|
throw StandardRpcException::fromRpcException($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new StandardRpcException('There was an error while trying to notify the external server of the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClientRequest $request
|
||||||
|
* @param RpcRequest $rpcRequest
|
||||||
|
* @return SerializableInterface
|
||||||
|
* @throws StandardRpcException
|
||||||
|
*/
|
||||||
|
private static function executeExternal(ClientRequest $request, RpcRequest $rpcRequest): SerializableInterface
|
||||||
|
{
|
||||||
|
if($request->getIdentifyAs() === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::BAD_REQUEST, 'The IdentifyAs header is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$channelUuid = $rpcRequest->getParameter('channel_uuid');
|
||||||
|
$encryptionChannel = EncryptionChannelManager::getChannel($channelUuid);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to retrieve the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The encryption channel does not exist');
|
||||||
|
}
|
||||||
|
elseif(!$encryptionChannel->isParticipant($request->getIdentifyAs()))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('checksum'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('checksum');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('data'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('data');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('message_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('message_uuid');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('timestamp'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('timestamp');
|
||||||
|
}
|
||||||
|
elseif(!is_int($rpcRequest->getParameter('timestamp')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('timestamp', 'The given timestamp must be type integer');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::sendMessage(
|
||||||
|
channelUuid: $channelUuid,
|
||||||
|
recipient: $encryptionChannel->determineRecipient($request->getIdentifyAs()),
|
||||||
|
checksum: $rpcRequest->getParameter('checksum'),
|
||||||
|
data: $rpcRequest->getParameter('data'),
|
||||||
|
messageUuid: $rpcRequest->getParameter('message_uuid'),
|
||||||
|
messageTimestamp: (int)$rpcRequest->getParameter('timestamp')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('Failed to send the message', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\EncryptionChannel;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Classes\Logger;
|
||||||
|
use Socialbox\Classes\Validator;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Enums\Status\EncryptionChannelStatus;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\RpcException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\EncryptionChannelManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
|
||||||
|
class EncryptionCloseChannel extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('channel_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('channel_uuid');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('channel_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('channel_uuid', 'The given channel uuid is not a valid UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($request->isExternal())
|
||||||
|
{
|
||||||
|
return self::handleExternal($request, $rpcRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::handleInternal($request, $rpcRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClientRequest $request
|
||||||
|
* @param RpcRequest $rpcRequest
|
||||||
|
* @return SerializableInterface|null
|
||||||
|
* @throws StandardRpcException
|
||||||
|
*/
|
||||||
|
private static function handleInternal(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$requestingPeer = $request->getPeer();
|
||||||
|
$encryptionChannel = EncryptionChannelManager::getChannel($rpcRequest->getParameter('channel_uuid'));
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to obtain the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The requested encryption channel was not found');
|
||||||
|
}
|
||||||
|
elseif(!$encryptionChannel->isParticipant($requestingPeer->getAddress()))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The requested encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
elseif($encryptionChannel->getStatus() === EncryptionChannelStatus::CLOSED)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceResponse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::closeChannel($encryptionChannel->getUuid());
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('An error occurred while trying to close the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$externalPeer = $encryptionChannel->getExternalPeer();
|
||||||
|
if($externalPeer !== null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$rpcClient = Socialbox::getExternalSession($encryptionChannel->getCallingPeerAddress()->getDomain());
|
||||||
|
$rpcClient->encryptionCloseChannel(
|
||||||
|
channelUuid: $rpcRequest->getParameter('channel_uuid'),
|
||||||
|
identifiedAs: $requestingPeer->getAddress()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::declineChannel($rpcRequest->getParameter('channel_uuid'), true);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
Logger::getLogger()->error('Error declining channel as server', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($e instanceof RpcException)
|
||||||
|
{
|
||||||
|
throw StandardRpcException::fromRpcException($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new StandardRpcException('There was an error while trying to notify the external server of the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClientRequest $request
|
||||||
|
* @param RpcRequest $rpcRequest
|
||||||
|
* @return SerializableInterface|null
|
||||||
|
* @throws StandardRpcException
|
||||||
|
*/
|
||||||
|
private static function handleExternal(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if($request->getIdentifyAs() === null)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('The IdentifyAs field is required for external requests', StandardError::UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$encryptionChannel = EncryptionChannelManager::getChannel($rpcRequest->getParameter('channel_uuid'));
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('An error occurred while trying to obtain the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The requested encryption channel was not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$encryptionChannel->isParticipant($request->getIdentifyAs()))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The requested encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel->getStatus() === EncryptionChannelStatus::CLOSED)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceResponse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::closeChannel($encryptionChannel->getUuid());
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('An error occurred while trying to close the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,263 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\EncryptionChannel;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Classes\Cryptography;
|
||||||
|
use Socialbox\Classes\Logger;
|
||||||
|
use Socialbox\Classes\Validator;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\RpcException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\EncryptionChannelManager;
|
||||||
|
use Socialbox\Managers\RegisteredPeerManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\PeerAddress;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
|
||||||
|
class EncryptionCreateChannel extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ($request->isExternal())
|
||||||
|
{
|
||||||
|
return self::handleExternal($request, $rpcRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::handleInternal($request, $rpcRequest);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('An error occurred while checking the request type', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClientRequest $request
|
||||||
|
* @param RpcRequest $rpcRequest
|
||||||
|
* @return SerializableInterface|null
|
||||||
|
* @throws StandardRpcException
|
||||||
|
*/
|
||||||
|
private static function handleInternal(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('receiving_peer'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('receiving_peer');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validatePeerAddress($rpcRequest->getParameter('receiving_peer')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('receiving_peer', 'Invalid Receiving Peer Address');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('public_encryption_key'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('public_encryption_key');
|
||||||
|
}
|
||||||
|
elseif(!Cryptography::validatePublicEncryptionKey($rpcRequest->getParameter('public_encryption_key')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('public_encryption_key', 'The given public encryption key is invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
$receivingPeerAddress = PeerAddress::fromAddress($rpcRequest->getParameter('receiving_peer'));
|
||||||
|
Socialbox::resolvePeer($receivingPeerAddress);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$callingPeer = $request->getPeer();
|
||||||
|
$callingPeerAddress = PeerAddress::fromAddress($callingPeer->getAddress());
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to obtain the calling peer', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$uuid = EncryptionChannelManager::createChannel(
|
||||||
|
callingPeer: $callingPeerAddress,
|
||||||
|
receivingPeer: $receivingPeerAddress,
|
||||||
|
callingPublicEncryptionKey: $rpcRequest->getParameter('public_encryption_ke')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(InvalidArgumentException $e)
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException(null, $e);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to create a new encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the receiver is in an external server, we must notify the external server as a client
|
||||||
|
if($receivingPeerAddress->isExternal())
|
||||||
|
{
|
||||||
|
// Obtain the RPC Client, if for any reason it fails; we set the encryption channel as declined.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$rpcClient = Socialbox::getExternalSession($receivingPeerAddress->getDomain());
|
||||||
|
$externalUuid = $rpcClient->encryptionCreateChannel(
|
||||||
|
receivingPeer: $receivingPeerAddress,
|
||||||
|
publicEncryptionKey: $rpcRequest->getParameter('public_encryption_key'),
|
||||||
|
channelUuid: $uuid,
|
||||||
|
identifiedAs: $callingPeerAddress
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::declineChannel($uuid, true);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
Logger::getLogger()->error('Error declining channel as server', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($e instanceof RpcException)
|
||||||
|
{
|
||||||
|
throw StandardRpcException::fromRpcException($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new StandardRpcException('There was an error while trying to notify the external server of the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for sanity reasons
|
||||||
|
if($externalUuid !== $uuid)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::declineChannel($uuid, true);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
Logger::getLogger()->error('Error declining channel as server', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new StandardRpcException('The external server did not return the correct UUID', StandardError::UUID_MISMATCH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClientRequest $request
|
||||||
|
* @param RpcRequest $rpcRequest
|
||||||
|
* @return SerializableInterface|null
|
||||||
|
* @throws StandardRpcException
|
||||||
|
*/
|
||||||
|
private static function handleExternal(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if($request->getIdentifyAs() === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::BAD_REQUEST, 'Missing IdentifyAs request header');
|
||||||
|
}
|
||||||
|
|
||||||
|
$callingPeer = $request->getIdentifyAs();
|
||||||
|
Socialbox::resolvePeer($callingPeer);
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('receiving_peer'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('receiving_peer');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validatePeerAddress($rpcRequest->getParameter('receiving_peer')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('receiving_peer', 'Invalid Receiving Peer Address');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('public_encryption_key'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('public_encryption_key');
|
||||||
|
}
|
||||||
|
elseif(!Cryptography::validatePublicEncryptionKey($rpcRequest->getParameter('public_encryption_key')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('public_encryption_key', 'The given public encryption key is invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for an additional required parameter 'channel_uuid'
|
||||||
|
if(!$rpcRequest->containsParameter('channel_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('channel_uuid');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('channel_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('channel_uuid', 'The given UUID is not a valid UUID v4 format');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the UUID already is used on this server
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(EncryptionChannelManager::channelUuidExists($rpcRequest->getParameter('channel_uuid')))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UUID_CONFLICT, 'The given UUID already exists with another existing encryption channel on this server');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while checking the existence of the channel UUID', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$receivingPeerAddress = PeerAddress::fromAddress($rpcRequest->getParameter('receiving_peer'));
|
||||||
|
if($receivingPeerAddress->isExternal())
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::PEER_NOT_FOUND, 'The receiving peer does not belong to this server');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$receivingPeer = RegisteredPeerManager::getPeerByAddress($rpcRequest->getParameter('receiving_peer'));
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to obtain the receiving peer', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($receivingPeer === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::PEER_NOT_FOUND, 'The receiving peer does not exist on this server');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$uuid = EncryptionChannelManager::createChannel(
|
||||||
|
callingPeer: $callingPeer,
|
||||||
|
receivingPeer: $receivingPeerAddress,
|
||||||
|
callingPublicEncryptionKey: $rpcRequest->getParameter('public_encryption_key'),
|
||||||
|
channelUUid: $rpcRequest->getParameter('channel_uuid')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to create the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($uuid !== $rpcRequest->getParameter('channel_uuid'))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::declineChannel($rpcRequest->getParameter('channel_uuid'), true);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
Logger::getLogger()->error('There was an error while trying to decline the encryption channel as a server', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceError(StandardError::UUID_MISMATCH, 'The created UUID in the server does not match the UUID that was received');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse($uuid);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\EncryptionChannel;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Classes\Logger;
|
||||||
|
use Socialbox\Classes\Validator;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Enums\Status\EncryptionChannelStatus;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\RpcException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\EncryptionChannelManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
use Socialbox\Socialbox;
|
||||||
|
|
||||||
|
class EncryptionDeclineChannel extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ($request->isExternal())
|
||||||
|
{
|
||||||
|
return self::handleExternal($request, $rpcRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::handleInternal($request, $rpcRequest);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('An error occurred while checking the request type', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClientRequest $request
|
||||||
|
* @param RpcRequest $rpcRequest
|
||||||
|
* @return SerializableInterface|null
|
||||||
|
* @throws StandardRpcException
|
||||||
|
*/
|
||||||
|
private static function handleInternal(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('channel_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('channel_uuid');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('channel_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('channel_uuid', 'The given channel uuid is not a valid UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$receivingPeer = $request->getPeer();
|
||||||
|
$encryptionChannel = EncryptionChannelManager::getChannel($rpcRequest->getParameter('channel_uuid'));
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to obtain the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The requested encryption channel was not found');
|
||||||
|
}
|
||||||
|
elseif($encryptionChannel->getReceivingPeerAddress()->getAddress() !== $receivingPeer->getAddress())
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The requested encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
elseif($encryptionChannel->getStatus() !== EncryptionChannelStatus::AWAITING_RECEIVER)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The encryption channel is not awaiting the receiver');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel->getCallingPeerAddress()->isExternal())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$rpcClient = Socialbox::getExternalSession($encryptionChannel->getCallingPeerAddress()->getDomain());
|
||||||
|
$rpcClient->encryptionDeclineChannel(
|
||||||
|
channelUuid: $rpcRequest->getParameter('channel_uuid'),
|
||||||
|
identifiedAs: $receivingPeer->getAddress()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::declineChannel($rpcRequest->getParameter('channel_uuid'), true);
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
Logger::getLogger()->error('Error declining channel as server', $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($e instanceof RpcException)
|
||||||
|
{
|
||||||
|
throw StandardRpcException::fromRpcException($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new StandardRpcException('There was an error while trying to notify the external server of the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::declineChannel(
|
||||||
|
channelUuid: $rpcRequest->getParameter('channel_uuid')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to decline the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClientRequest $request
|
||||||
|
* @param RpcRequest $rpcRequest
|
||||||
|
* @return SerializableInterface|null
|
||||||
|
* @throws StandardRpcException
|
||||||
|
*/
|
||||||
|
private static function handleExternal(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if($request->getIdentifyAs() === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::BAD_REQUEST, 'Missing required header IdentifyAs');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$rpcRequest->containsParameter('channel_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('channel_uuid');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('channel_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('channel_uuid', 'The given channel uuid is not a valid UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$receivingPeer = Socialbox::resolvePeer($request->getIdentifyAs());
|
||||||
|
$encryptionChannel = EncryptionChannelManager::getChannel($rpcRequest->getParameter('channel_uuid'));
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to obtain the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The requested encryption channel was not found');
|
||||||
|
}
|
||||||
|
elseif($encryptionChannel->getReceivingPeerAddress() !== $receivingPeer->getPeerAddress())
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The requested encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
elseif($encryptionChannel->getStatus() !== EncryptionChannelStatus::AWAITING_RECEIVER)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::FORBIDDEN, 'The encryption channel is not awaiting the receiver');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EncryptionChannelManager::declineChannel(
|
||||||
|
channelUuid: $rpcRequest->getParameter('channel_uuid')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to decline the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Socialbox\Classes\StandardMethods\EncryptionChannel;
|
||||||
|
|
||||||
|
use Socialbox\Abstracts\Method;
|
||||||
|
use Socialbox\Classes\Validator;
|
||||||
|
use Socialbox\Enums\StandardError;
|
||||||
|
use Socialbox\Exceptions\DatabaseOperationException;
|
||||||
|
use Socialbox\Exceptions\Standard\InvalidRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\MissingRpcArgumentException;
|
||||||
|
use Socialbox\Exceptions\Standard\StandardRpcException;
|
||||||
|
use Socialbox\Interfaces\SerializableInterface;
|
||||||
|
use Socialbox\Managers\EncryptionChannelManager;
|
||||||
|
use Socialbox\Objects\ClientRequest;
|
||||||
|
use Socialbox\Objects\RpcRequest;
|
||||||
|
|
||||||
|
class EncryptionGetChannel extends Method
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function execute(ClientRequest $request, RpcRequest $rpcRequest): ?SerializableInterface
|
||||||
|
{
|
||||||
|
if(!$rpcRequest->containsParameter('channel_uuid'))
|
||||||
|
{
|
||||||
|
throw new MissingRpcArgumentException('channel_uuid');
|
||||||
|
}
|
||||||
|
elseif(!Validator::validateUuid($rpcRequest->getParameter('channel_uuid')))
|
||||||
|
{
|
||||||
|
throw new InvalidRpcArgumentException('channel_uuid', 'The given channel uuid is not a valid UUID V4');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$requestingPeer = $request->getPeer();
|
||||||
|
$encryptionChannel = EncryptionChannelManager::getChannel($rpcRequest->getParameter('channel_uuid'));
|
||||||
|
}
|
||||||
|
catch(DatabaseOperationException $e)
|
||||||
|
{
|
||||||
|
throw new StandardRpcException('There was an error while trying to obtain the encryption channel', StandardError::INTERNAL_SERVER_ERROR, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($encryptionChannel === null)
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::NOT_FOUND, 'The requested encryption channel was not found');
|
||||||
|
}
|
||||||
|
elseif(!$encryptionChannel->isParticipant($requestingPeer->getAddress()))
|
||||||
|
{
|
||||||
|
return $rpcRequest->produceError(StandardError::UNAUTHORIZED, 'The requested encryption channel is not accessible');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rpcRequest->produceResponse($encryptionChannel->toStandard());
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue