This commit is contained in:
144 changed files with 20649 additions and 0 deletions

2
.env Normal file
View File

@@ -0,0 +1,2 @@
USER_ID=1000
GROUP_ID=1000

23
.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
.DS_Store
node_modules
dist
mysql/data
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
*.log
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

7
README.md Normal file
View File

@@ -0,0 +1,7 @@
## Docker + php-fpm + PhpStorm + Xdebug
Подробности на https://habr.com/ru/post/473184/

64
aliases/php71/bash.bashrc Normal file
View File

@@ -0,0 +1,64 @@
# System-wide .bashrc file for interactive bash(1) shells.
# To enable the settings / commands in this file for login shells as well,
# this file has to be sourced in /etc/profile.
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
debian_chroot=$(cat /etc/debian_chroot)
fi
# set a fancy prompt (non-color, overwrite the one in /etc/profile)
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
# Commented out, don't overwrite xterm -T "title" -n "icontitle" by default.
# If this is an xterm set the title to user@host:dir
#case "$TERM" in
#xterm*|rxvt*)
# PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"'
# ;;
#*)
# ;;
#esac
# enable bash completion in interactive shells
#if ! shopt -oq posix; then
# if [ -f /usr/share/bash-completion/bash_completion ]; then
# . /usr/share/bash-completion/bash_completion
# elif [ -f /etc/bash_completion ]; then
# . /etc/bash_completion
# fi
#fi
# if the command-not-found package is installed, use it
if [ -x /usr/lib/command-not-found -o -x /usr/share/command-not-found/command-not-found ]; then
function command_not_found_handle {
# check because c-n-f could've been removed in the meantime
if [ -x /usr/lib/command-not-found ]; then
/usr/lib/command-not-found -- "$1"
return $?
elif [ -x /usr/share/command-not-found/command-not-found ]; then
/usr/share/command-not-found/command-not-found -- "$1"
return $?
else
printf "%s: command not found\n" "$1" >&2
return 127
fi
}
fi
alias cl='clear'
alias ll='ls -alF'
alias l='ls -la'
alias cdf='cd /var/www/first.loc'
alias cdt='cd /var/www/two.loc'

3
backup.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
sudo docker exec tsd_docker_mysql_1 mysqldump --opt --compatible=postgresql --default-character-set=utf8 -u root -p'secret' tsd > tsd_main.sql
sudo docker exec tsd_docker_mysql_1 mysqldump --opt --compatible=postgresql --default-character-set=utf8 -u root -p'secret' authentication_jwt > tsd_users.sql

24
dnet.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
if [ $# -eq 0 ]; then
nw=($(docker network ls | awk 'NR>=2 {printf "%s ",$2}'))
else
nw=$@
fi
echo ${nw[@]}
for network in ${nw[@]};do
docker network ls | grep $network 1>/dev/null 2>/dev/null
if [ $? -ne 0 ]; then
echo -e "\nNetwork $network not found!"
exit 1
fi
ids=$(docker network inspect $network | grep -E "^\s+\".{64}\"" | awk -F\" '{printf "%s ",substr($2,0,12)}')
echo -e "\nContainers in network $network:"
for id in ${ids[@]}; do
name=$(docker ps -a | grep $id | awk '{print $NF}')
echo -e "\t$id: $name"
done
shift
done

57
docker-compose.yml Normal file
View File

@@ -0,0 +1,57 @@
version: '3'
services:
php-backend-tsd:
restart: unless-stopped
build:
context: .
dockerfile: ./images/php82/Dockerfile
args:
- USER_ID
- GROUP_ID
volumes:
- ./www:/var/www
- ./aliases/php71/bash.bashrc:/etc/bash.bashrc
environment:
XDEBUG_CONFIG: "remote_host=192.168.225.1 remote_enable=1 remote_autostart=off remote_port=9008"
PHP_IDE_CONFIG: "serverName=first"
networks:
- tsd-network
vuecli-tsd:
build:
context: ./images/vuecli
working_dir: /srv/app
volumes:
- ./www/first.loc:/srv/app
- ./www/first.loc/node_modules:/srv/app/node_modules
stdin_open: true
environment:
- HOST=0.0.0.0
- CHOKIDAR_USEPOLLING=true
ports:
- "8079:8080"
# Команда не указана — используется CMD из Dockerfile (обычно npm run serve)
nginx-tsd:
restart: unless-stopped
image: nginx
volumes:
- ./hosts:/etc/nginx/conf.d
- ./www:/var/www
- ./logs:/var/log/nginx
ports:
- "8100:80"
depends_on:
- php-backend-tsd
networks:
tsd-network:
aliases:
- first.loc
networks:
tsd-network:
driver: bridge
ipam:
driver: default
config:
- subnet: 192.168.225.0/28

68
hosts/first.conf Normal file
View File

@@ -0,0 +1,68 @@
server {
listen 80;
index index.html index.php;
server_name mpc;
error_log /var/log/nginx/first_error.log;
root /var/www/first.loc;
# location ~* /(tsd) {
# root /var/www/first.loc/dist;
# try_files $uri $uri/ /index.html;
# }
location / {
root /var/www/first.loc/dist;
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|ico)$ {
root /var/www/first.loc/dist;
expires 30d;
}
location ~* /(fonts|images) {
root /var/www/first.loc/dist;
expires 30d;
}
location ~* /api/tmp {
root /var/www/first.loc;
expires 30d;
}
location ~ \.jpg$ {
try_files $uri /api/image-processor.php?file=$uri;
}
location ~ \.png$ {
try_files $uri /api/image-processor.php?file=$uri;
}
location ~* /api {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# контейнер php-fpm
#fastcgi_pass 127.0.0.1:9000;
fastcgi_pass php-backend-tsd:9000;
fastcgi_index index.php;
fastcgi_read_timeout 1000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# контейнер php-fpm
#fastcgi_pass 127.0.0.1:9000;
fastcgi_pass php-backend-tsd:9000;
fastcgi_index index.php;
fastcgi_read_timeout 1000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

21
hosts/two.conf_ Normal file
View File

@@ -0,0 +1,21 @@
server {
listen 80;
index index.php;
server_name two.loc;
error_log /var/log/nginx/two_error.log;
root /var/www/two.loc;
location / {
try_files $uri /index.php?$args;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php71-two:9000;
fastcgi_index index.php;
fastcgi_read_timeout 1000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}

18
images/hub/Dockerfile Normal file
View File

@@ -0,0 +1,18 @@
FROM php:7.1-fpm
RUN apt-get update && apt-get install -y \
git \
curl \
wget \
libfreetype6-dev \
libjpeg62-turbo-dev \
libmcrypt-dev \
libpng-dev zlib1g-dev libicu-dev g++ libmagickwand-dev --no-install-recommends libxml2-dev \
&& docker-php-ext-configure intl \
&& docker-php-ext-install intl \
&& docker-php-ext-install mbstring zip xml gd mcrypt pdo_mysql \
&& pecl install imagick \
&& docker-php-ext-enable imagick \
&& pecl install xdebug-2.5.0 \
&& docker-php-ext-enable xdebug

20
images/hub70/Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
FROM php:7.0-fpm
RUN apt-get update && apt-get install -y \
git \
wkhtmltopdf xvfb \
curl \
wget \
libfreetype6-dev \
libjpeg62-turbo-dev \
libmcrypt-dev \
libpng-dev zlib1g-dev libicu-dev g++ libmagickwand-dev libxml2-dev \
&& docker-php-ext-configure intl \
&& docker-php-ext-install intl \
&& docker-php-ext-install mbstring zip xml gd mcrypt pdo_mysql \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd \
&& pecl install imagick \
&& docker-php-ext-enable imagick \
&& pecl install xdebug \
&& docker-php-ext-enable xdebug

9
images/nginx/Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
FROM nginx
COPY www/first.loc /var/www/first.loc
COPY hosts/first.conf /etc/nginx/conf.d/
RUN rm -f /etc/nginx/conf.d/default.conf
RUN rm -rf /var/www/first.loc/src
WORKDIR /var/www/first.loc

189
images/php82/Dockerfile Normal file
View File

@@ -0,0 +1,189 @@
FROM composer:latest AS composer
FROM php:8.2-fpm
COPY --from=composer /usr/bin/composer /usr/bin/composer
ARG USER_ID
ARG GROUP_ID
ADD images/php82/php.ini /usr/local/etc/php/php.ini
ADD www/first.loc/api /var/www/first.loc/api
# ============================================
# БЛОК 1: Настройка пользователя и прав
# ============================================
RUN chown -R 1000:1000 /var/www/ && \
usermod -u 1000 www-data && \
groupmod -g 1000 www-data
# ============================================
# БЛОК 2: Базовая система и обновление пакетов
# ============================================
RUN apt-get update -y && \
apt-get -y install \
gcc \
make \
autoconf \
libc-dev \
pkg-config \
libzip-dev \
locales
# ============================================
# БЛОК 3: Основные инструменты и библиотеки
# ============================================
RUN apt-get install -y --no-install-recommends \
git \
libz-dev \
libpq-dev \
libxml2-dev \
libmemcached-dev \
libldap2-dev \
libbz2-dev \
zlib1g-dev \
libicu-dev \
g++ \
libssl-dev \
libssl-doc \
libsasl2-dev \
curl \
libcurl4-openssl-dev
# ============================================
# БЛОК 4: Специализированные библиотеки
# ============================================
RUN apt-get install -y --no-install-recommends \
libgmp-dev \
firebird-dev \
libib-util
# ============================================
# БЛОК 5: Библиотеки для изображений и графики
# ============================================
RUN apt-get install -y --no-install-recommends \
re2c \
libpng++-dev \
libwebp-dev \
libjpeg-dev \
libjpeg62-turbo-dev \
libpng-dev \
libxpm-dev \
libvpx-dev \
libfreetype6-dev
# ============================================
# БЛОК 6: Python и ImageMagick
# ============================================
RUN apt-get install -y --no-install-recommends \
python3 \
python3-pip \
libmagick++-dev \
libmagickwand-dev
# ============================================
# БЛОК 7: Дополнительные утилиты и библиотеки
# ============================================
RUN apt-get install -y --no-install-recommends \
zlib1g-dev \
libgd-dev \
unzip \
libpcre2-dev \
libtidy-dev \
libxslt1-dev \
libmagic-dev \
libexif-dev \
file \
libmhash2 \
libmhash-dev \
libkrb5-dev \
libssh2-1-dev \
poppler-utils \
ghostscript \
libmagickwand-dev \
libsnmp-dev \
libedit-dev \
libreadline-dev \
libsodium-dev \
freetds-bin \
freetds-dev \
freetds-common \
libct4 \
libsybdb5 \
tdsodbc \
librecode-dev \
libpspell-dev \
libonig-dev
# ============================================
# БЛОК 8: Пропускаем IMAP (не нужен для работы)
# ============================================
RUN echo "IMAP extension skipped - continuing with other extensions"
# ============================================
# БЛОК 9: Установка расширений PHP (часть 1)
# ============================================
RUN ln -s /usr/lib/x86_64-linux-gnu/libsybdb.so /usr/lib/ && \
docker-php-ext-install pdo_dblib && \
docker-php-ext-install dba && \
docker-php-ext-install curl && \
docker-php-ext-install fileinfo && \
docker-php-ext-install filter && \
docker-php-ext-install exif && \
docker-php-ext-install gettext && \
docker-php-ext-install gmp && \
docker-php-ext-install iconv
# ============================================
# БЛОК 10: Установка расширений PHP (часть 2 - базы данных)
# ============================================
RUN docker-php-ext-install mysqli && \
docker-php-ext-install pdo_mysql && \
docker-php-ext-install pdo_pgsql && \
docker-php-ext-install pgsql && \
docker-php-ext-install session && \
docker-php-ext-install zip && \
docker-php-ext-install xml
# ============================================
# БЛОК 11: Установка GD с конфигурацией
# ============================================
RUN docker-php-ext-configure gd \
--with-jpeg \
--with-xpm \
--with-webp \
--with-freetype && \
docker-php-ext-install -j$(nproc) gd
# ============================================
# БЛОК 12: Установка MongoDB через PECL
# ============================================
RUN pecl install mongodb && \
docker-php-ext-enable mongodb
# ============================================
# БЛОК 13: Настройка локали
# ============================================
RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && \
locale-gen en_US.UTF-8 && \
update-locale LANG=en_US.UTF-8
# ============================================
# БЛОК 14: Очистка и финальные права
# ============================================
RUN chmod +x /usr/local/bin/* && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# ============================================
# Финальные настройки
# ============================================
ENV LANG='en_US.UTF-8' \
LANGUAGE='en_US:en' \
LC_ALL='en_US.UTF-8'
WORKDIR /var/www
USER 1000:1000
RUN mkdir -p /var/www/first.loc/api/tmp
CMD ["php-fpm"]

1
images/php82/php.ini Normal file
View File

@@ -0,0 +1 @@

18
images/vuecli/Dockerfile Normal file
View File

@@ -0,0 +1,18 @@
FROM ubuntu:latest
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get install -y nodejs npm && rm -rf /var/lib/apt/lists/*
RUN npm install -g yarn
WORKDIR /srv/app
#COPY app/package*.json ./
RUN yarn global add @vue/cli
RUN yarn install
# Bundle app source
#COPY ./app .
EXPOSE 8080
#CMD [ "yarn", "serve"]

2
logs/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

3
remove.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
#docker stop $(docker ps -a -q) && docker rm $(docker ps -a -q) && docker rmi $(docker images -a -q)

0
tsd_main.sql Normal file
View File

0
tsd_users.sql Normal file
View File

3
up.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
docker-compose up

2
www/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.composer/
.bash_history

21
www/first.loc/.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

19
www/first.loc/README.md Normal file
View File

@@ -0,0 +1,19 @@
# tsd
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

View File

@@ -0,0 +1,40 @@
<?php
class DataForms{
public function __construct($db)
{
$this->conn = $db;
}
function to_UTF8($arr) {
foreach($arr as $k=>$v){
$v=iconv('Windows-1251','UTF-8',$v);
$arr[$k]=$v;
}
return $arr;
}
function Query()
{
$result=[
1,2,3
];
return $result;
}
}

View File

@@ -0,0 +1,36 @@
<?php
// Используем для подключения к базе данных MySQL
class Database
{
// Учётные данные базы данных
# private $host = "10.10.1.246";
# public $db_name = "tsd_main";
# private $username = "tsd";
#private $password = "gcIL6UrWFtSdDTbz";
private $host = "10.10.0.20";
public $db_name = "tsd";
private $username = "tsd_user";
private $password = "stptoamh";
public $conn;
// Получаем соединение с базой данных
public function getConnection()
{
$this->conn = null;
try {
$this->conn = new PDO("pgsql:host=".$this->host.";port=5432;dbname=".$this->db_name, $this->username, $this->password);
//$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
} catch (PDOException $exception) {
echo "Ошибка соединения с БД: " . $exception->getMessage();
}
return $this->conn;
}
}

View File

@@ -0,0 +1,28 @@
<?php
// Используем для подключения к базе данных MySQL
class DatabaseMS
{
// Учётные данные базы данных
private $host = "omega-server";
public $db_name = "SKL_BLOKSQL";
private $username = "sa";
private $password = "haqdvega#";
public $conn;
// Получаем соединение с базой данных
public function getConnection()
{
$this->conn = null;
try {
$this->conn = new PDO("dblib:host=" . $this->host . ";dbname=" . $this->db_name.";charset=CP1251", $this->username, $this->password);
} catch (PDOException $exception) {
echo "Ошибка соединения с БД: " . $exception->getMessage();
}
return $this->conn;
}
}

View File

@@ -0,0 +1,31 @@
<?php
// Используем для подключения к базе данных MySQL
class Database
{
// Учётные данные базы данных
//private $host = "localhost:3308";
private $host = "10.10.1.246";
//public $db_name = "authentication_jwt";
//private $username = "tsd_user";
private $username = "tsd";
//private $password = "Dfrgvm3@1";
private $password = "gcIL6UrWFtSdDTbz";
public $conn;
// Получаем соединение с базой данных
public function getConnection()
{
$this->conn = null;
try {
$this->conn = new PDO("pgsql:host=".$this->host.";port=5432;dbname=".$this->db_name, $this->username, $this->password);
//$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
} catch (PDOException $exception) {
echo "Ошибка соединения с БД: " . $exception->getMessage();
}
return $this->conn;
}
}

View File

@@ -0,0 +1,340 @@
<?php
#[AllowDynamicProperties]
Class MainClass {
private $headers = Array("Authorization: fi9tzpIHgp84E1Pf45rMzR/hy8lA2zE5", 'Content-Type: "application/json"');
protected object $conn;
public function __construct($db)
{
$this->conn = $db;
}
function WrFile($param){
//запись в файл. Не понятно зачем, но пока оставил
$path='/var/www/tmp';
$file_name=$param['date_query'].'.json';
if(!file_exists($path)){
mkdir($path);
chmod ( $path, 0777 );
}
$path=$path.'/beru';
if(!file_exists($path)){
mkdir($path);
chmod ( $path, 0777 );
}
if(file_exists($path.'/'.$file_name)){
unlink($path.'/'.$file_name);
}
file_put_contents($path.'/'.$file_name, $param['data_json']);
}
function getSSLPage($url,$post_data,$headers) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if(strlen(trim($post_data))>0){
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
}
curl_setopt ( $ch , CURLOPT_SSL_VERIFYPEER, 0 );
curl_setopt ( $ch , CURLOPT_SSL_VERIFYHOST, 0 );
//$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
function wrDatabase($param){
/*
$param=[
`'date_query' => $date_query,
'data_json' => $data_json,
'db'=> $db,
'user'= $user
];`
*/
$opers=json_decode($param['data_json'],true);
try{
//echo json_encode($opereturnrs);
//die('stop');
$user=$param['user'];
$date_query=$param['date_query'];
$firma=$param['kag']['firma'];
for($i=0;$i<count($opers);$i++){
$item=$opers[$i];
$vars=[
'idoper'=>$item['id'],
'data_json' => json_encode($item),
'user'=>$user,
'date_d'=>date('Y-m-d H:i:s'),
'date_query'=>$date_query,
'firma'=>$firma,
];
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->conn->beginTransaction();
//удалить предыдущие загрузки по дате загрузки и номеру акта
$sth = $this->conn->prepare("delete from oper where idoper=:id and firma=:firma and date_query=:date_query");
$sth->execute([
'id'=>$item['id'],
'firma'=>$firma,
'date_query'=>$date_query,
]);
$query="insert into oper
(idoper,firma, data_json,date_d,\"user\",date_query)
values
(:idoper,:firma, :data_json,:date_d,:user,:date_query)";
$stmt = $this->conn->prepare($query);
$stmt->execute($vars);
$error=$stmt->errorCode();
$ids=$this->conn->lastInsertId();
$this->conn->commit();
//idoper == >
$ids_arr[$item['id']]=$ids;
}
$result= ['result'=>1,'result'=>$ids_arr];
}catch (Exception $e) {
$this->conn->rollBack();
$error = "Ошибка: " . $e->getMessage();
$result = ['result'=>0,'error'=>$error];
}
return $result;
}
function DataCollect($param){
//результаты сканирования актов
$result=[];
$query = "select
id,
barcode,
date_up
from oper_collect
where idoper=:id and firma=:firma ORDER BY date_up";
$stmt = $this->conn->prepare($query);
$vars['id'] = $param['idoper'];
$vars['firma'] = $param['kag']['firma'];
$stmt->execute($vars);
$error = $stmt->errorCode();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row;
}
return ['idoper'=>$param['idoper'] ,'collect'=>$result,'v'=>$vars];
}
function History($param){
$limit=$param['limit'];
$kag=$param['kag'];
$vars=[
'firma'=>$param['kag']['firma'],
];
$query = "select
id,
idoper,
date_query,
to_char(date_query,'DD.MM.YY') as date_q,
data_json
from oper
where firma=:firma
ORDER BY date_query DESC, id DESC
limit 10
";
$stmt = $this->conn->prepare($query);
$stmt->execute($vars);
$error = $stmt->errorCode();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$row['data_json']=json_decode($row['data_json'],true);
$row['data_collect']=$this->DataCollect(['idoper'=>$row['idoper'],'kag'=>$param['kag']]);
$row['param']=$param;
$result[] = $row;
}
return ['data'=>$result];
}
function pushCollect($data){
//записать данные по сборке
try{
//--Повтор------------>
$query = "select
id
from oper_collect
where idoper=:id and firma=:firma and (barcode=:barcode OR barcode_json like '%".$data['barcode']."%') ORDER BY date_up";
$stmt = $this->conn->prepare($query);
$vars['id'] = $data['idoper'];
$vars['firma'] = $data['kag']['firma'];
$vars['barcode'] = $data['barcode'];
$stmt->execute($vars);
$error = $stmt->errorCode();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$result_b[] = $row;
}
// if(!$result_b){
// $result = ['result'=>0,'error'=>$error,'rrr'=>$vars];
// return $result;
// }
if($result_b and count($result_b)){
$error = "Повтор ".$data['barcode'];
$result = ['result'=>0,'error'=>$error];
return $result;
}
//<--Повтор------------
$vars=[
'idoper'=>$data['idoper'],
'date_query'=>$data['date_query'],
'barcode' => $data['barcode'],
'barcode_json'=> json_encode($data['barcode_json']),
'date_up'=>date('Y-m-d H:i:s'),
'iduser'=>$data['user']?$data['user']:0,
'firma'=>$data['kag']['firma'],
];
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->conn->beginTransaction();
$query="insert into oper_collect
(idoper, firma , date_query, barcode, barcode_json, date_up, \"user\")
values
(:idoper, :firma, :date_query,:barcode,:barcode_json,:date_up,:iduser)";
$stmt = $this->conn->prepare($query);
$stmt->execute($vars);
$error=$stmt->errorCode();
$id_new=$this->conn->lastInsertId();
$this->conn->commit();
$result= ['result'=>1];
}catch (Exception $e) {
$this->conn->rollBack();
$error = "Ошибка: " . $e->getMessage();
$result = ['result'=>0,'error'=>$error,'data'=>$data];
}
return $result;
}
function PostingStatus($params)
{
//$kag=$params['kag'];
//$posting_number=$params['posting_number'];
$url='https://marketplace.book-online.ru/api/posting_status/';
$json=json_encode($params);
$result=$this->getSSLPage($url,$json,$this->headers);
return json_decode($result);
}
function DeleteCollect($params)
{
$oper=$params['oper'];
$firma=$params['firma'];
if(strlen(trim($oper))>0 and strlen($firma)>0){
$query="delete from oper_collect
where idoper=:id and firma=:firma";
$vars=['id'=>$oper,'firma'=>$firma];
$stmt = $this->conn->prepare($query);
$stmt->execute($vars);
$error=$stmt->errorCode();
return ['result'=>1,'error'=>$error,'vars'=>$vars];
}
return ['result'=>0,'error'=>'неправильные входящие переменные!'];
}
}

View File

@@ -0,0 +1,154 @@
<?php
function http_code($code = NULL) {
if ($code !== NULL) {
switch ($code) {
case 100: $text = 'Continue'; break;
case 101: $text = 'Switching Protocols'; break;
case 200: $text = 'OK'; break;
case 201: $text = 'Created'; break;
case 202: $text = 'Accepted'; break;
case 203: $text = 'Non-Authoritative Information'; break;
case 204: $text = 'No Content'; break;
case 205: $text = 'Reset Content'; break;
case 206: $text = 'Partial Content'; break;
case 300: $text = 'Multiple Choices'; break;
case 301: $text = 'Moved Permanently'; break;
case 302: $text = 'Moved Temporarily'; break;
case 303: $text = 'See Other'; break;
case 304: $text = 'Not Modified'; break;
case 305: $text = 'Use Proxy'; break;
case 400: $text = 'Bad Request'; break;
case 401: $text = 'Unauthorized'; break;
case 402: $text = 'Payment Required'; break;
case 403: $text = 'Forbidden'; break;
case 404: $text = 'Not Found'; break;
case 405: $text = 'Method Not Allowed'; break;
case 406: $text = 'Not Acceptable'; break;
case 407: $text = 'Proxy Authentication Required'; break;
case 408: $text = 'Request Time-out'; break;
case 409: $text = 'Conflict'; break;
case 410: $text = 'Gone'; break;
case 411: $text = 'Length Required'; break;
case 412: $text = 'Precondition Failed'; break;
case 413: $text = 'Request Entity Too Large'; break;
case 414: $text = 'Request-URI Too Large'; break;
case 415: $text = 'Unsupported Media Type'; break;
case 500: $text = 'Internal Server Error'; break;
case 501: $text = 'Not Implemented'; break;
case 502: $text = 'Bad Gateway'; break;
case 503: $text = 'Service Unavailable'; break;
case 504: $text = 'Gateway Time-out'; break;
case 505: $text = 'HTTP Version not supported'; break;
default:
exit('Unknown http status code "' . htmlentities($code) . '"');
break;
}
$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
header($protocol . ' ' . $code . ' ' . $text);
$GLOBALS['http_response_code'] = $code;
} else {
$code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);
}
return $code;
}
function getQ_($url,$headers){
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
$output = curl_exec($ch);
curl_close($ch);
$ara=json_decode($output, true);
return $ara;
}
function getQ($url,$headers){
//return [$url];
// Инициализация сеанса cURL
$ch = curl_init();
// Установка URL
curl_setopt($ch, CURLOPT_URL, $url);
// Установка CURLOPT_RETURNTRANSFER (вернуть ответ в виде строки)
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// Выполнение запроса cURL
//$output содержит полученную строку
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
$output = curl_exec($ch);
// return $output;
$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
if(stristr($output,'Something went wrong')){
return [];//отправляем пустой массив
}
if($http_code>=400 and $http_code<500){
return [];//отправляем пустой массив
}
if($http_code>=500){
return $http_code;
}
$ara=json_decode($output, true);
// закрытие сеанса curl для освобождения системных ресурсов
curl_close($ch);
return $ara;
}
function getSSLPage($url,$post_data,$headers) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if(strlen(trim($post_data))>0){
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
}
curl_setopt ( $ch , CURLOPT_SSL_VERIFYPEER, 0 );
curl_setopt ( $ch , CURLOPT_SSL_VERIFYHOST, 0 );
$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
//$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
$output = curl_exec($ch);
curl_close($ch);
if($http_code>=400 or $http_code>=500){
return $http_code;
}
return $output;
}

View File

@@ -0,0 +1,35 @@
<?php
// Используем для подключения к базе данных MySQL
class Database
{
// Учётные данные базы данных
# private $host = "10.10.1.246";
# public $db_name = "tsd_users";
#private $username = "tsd";
##private $password = "gcIL6UrWFtSdDTbz";
private $host = "10.10.0.20";
public $db_name = "tsd";
private $username = "tsd_user";
private $password = "stptoamh";
public $conn;
// Получаем соединение с базой данных
public function getConnection()
{
$this->conn = null;
try {
$this->conn = new PDO("pgsql:host=".$this->host.";port=5432;dbname=".$this->db_name, $this->username, $this->password);
//$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
} catch (PDOException $exception) {
echo "Ошибка соединения с БД: " . $exception->getMessage();
}
return $this->conn;
}
}

View File

@@ -0,0 +1,31 @@
<?php
// Используем для подключения к базе данных MySQL
class Database
{
// Учётные данные базы данных
//private $host = "localhost:3308";
private $host = "10.10.1.246";
//public $db_name = "authentication_jwt";
private $db_name = "tsd_user";
private $username = "tsd";
//private $password = "Dfrgvm3@1";
private $password = "gcIL6UrWFtSdDTbz";
public $conn;
// Получаем соединение с базой данных
public function getConnection()
{
$this->conn = null;
try {
$this->conn = new PDO("pgsql:host=".$this->host.";port=5432;dbname=".$this->db_name, $this->username, $this->password);
//$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
} catch (PDOException $exception) {
echo "Ошибка соединения с БД: " . $exception->getMessage();
}
return $this->conn;
}
}

View File

@@ -0,0 +1,16 @@
<?php
// Показ сообщений об ошибках
error_reporting(E_ALL);
// Установим часовой пояс по умолчанию
date_default_timezone_set("Europe/Moscow");
// Переменные, используемые для JWT
$key = "d;rflgmd;lthkr;tlgd';tglldkfdfbkjldkaJNLKJDLKJDL2392093420lskfgj!!2";
$iss = "https://tsd.book-online.ru";
$aud = "https://tsd.book-online.ru";
//$iat = 1356999524;
//$nbf = 1357000000;
$iat = time();
$nbf = time();

View File

@@ -0,0 +1,157 @@
<?php
class User
{
// Подключение к БД таблице "users"
private $conn;
private $table_name = "tsd_users";
// Свойства
public $id;
public $firstname;
public $lastname;
public $email;
public $password;
// Конструктор класса User
public function __construct($db)
{
$this->conn = $db;
}
// Метод для создания нового пользователя
function create()
{
// Запрос для добавления нового пользователя в БД
$query = "INSERT INTO " . $this->table_name . "
SET
firstname = :firstname,
lastname = :lastname,
email = :email,
password = :password";
// Подготовка запроса
$stmt = $this->conn->prepare($query);
// Инъекция
$this->firstname = htmlspecialchars(strip_tags($this->firstname));
$this->lastname = htmlspecialchars(strip_tags($this->lastname));
$this->email = htmlspecialchars(strip_tags($this->email));
$this->password = htmlspecialchars(strip_tags($this->password));
// Привязываем значения
$stmt->bindParam(":firstname", $this->firstname);
$stmt->bindParam(":lastname", $this->lastname);
$stmt->bindParam(":email", $this->email);
// Для защиты пароля
// Хешируем пароль перед сохранением в базу данных
$password_hash = password_hash($this->password, PASSWORD_BCRYPT);
$stmt->bindParam(":password", $password_hash);
// Выполняем запрос
// Если выполнение успешно, то информация о пользователе будет сохранена в базе данных
if ($stmt->execute()) {
return true;
}
return false;
}
// Проверка, существует ли электронная почта в нашей базе данных
function emailExists() {
// Запрос, чтобы проверить, существует ли электронная почта
$query = "SELECT id, firstname, lastname, password
FROM " . $this->table_name . "
WHERE email = :email
limit 1
";
// Подготовка запроса
$stmt = $this->conn->prepare($query);
// Инъекция
$this->email=htmlspecialchars(strip_tags($this->email));
// Привязываем значение e-mail
// $stmt->bindParam("email", $this->email);
// Выполняем запрос
$stmt->execute(["email"=>$this->email]);
// Получаем количество строк
$num = $stmt->rowCount();
// Если электронная почта существует,
// Присвоим значения свойствам объекта для легкого доступа и использования для php сессий
if ($num > 0) {
// Получаем значения
$row = $stmt->fetch(PDO::FETCH_ASSOC);
// Присвоим значения свойствам объекта
$this->id = $row["id"];
$this->firstname = $row["firstname"];
$this->lastname = $row["lastname"];
$this->password = $row["password"];
// Вернём "true", потому что в базе данных существует электронная почта
return ['result'=>true];
}
// Вернём "false", если адрес электронной почты не существует в базе данных
return ['result'=>false,'error'=>$this->conn->errorInfo()];
}
// Здесь будет метод update()
// Обновить запись пользователя
public function update() {
// Если в HTML-форме был введен пароль (необходимо обновить пароль)
$password_set=!empty($this->password) ? ", password = :password" : "";
// Если не введен пароль - не обновлять пароль
$query = "UPDATE " . $this->table_name . "
SET
firstname = :firstname,
lastname = :lastname,
email = :email
{$password_set}
WHERE id = :id";
// Подготовка запроса
$stmt = $this->conn->prepare($query);
// Инъекция (очистка)
$this->firstname=htmlspecialchars(strip_tags($this->firstname));
$this->lastname=htmlspecialchars(strip_tags($this->lastname));
$this->email=htmlspecialchars(strip_tags($this->email));
// Привязываем значения с HTML формы
$stmt->bindParam(":firstname", $this->firstname);
$stmt->bindParam(":lastname", $this->lastname);
$stmt->bindParam(":email", $this->email);
// Метод password_hash () для защиты пароля пользователя в базе данных
if(!empty($this->password)){
$this->password=htmlspecialchars(strip_tags($this->password));
$password_hash = password_hash($this->password, PASSWORD_BCRYPT);
$stmt->bindParam(":password", $password_hash);
}
// Уникальный идентификатор записи для редактирования
$stmt->bindParam(":id", $this->id);
// Если выполнение успешно, то информация о пользователе будет сохранена в базе данных
if($stmt->execute()) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,68 @@
<?php
// Заголовки
header("Access-Control-Allow-Origin:*");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Подключение к БД
// Файлы, необходимые для подключения к базе данных
include_once __DIR__."/Config/Database.php";
//include_once __DIR__."/Config/DatabasePGSQL.php";
include_once __DIR__."/Objects/User.php";
// Получаем соединение с базой данных
$database = new Database();
$db = $database->getConnection();
// Создание объекта "User"
$user = new User($db);
// Отправляемые данные будут здесь
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
// Устанавливаем значения
$user->firstname = $data->firstname;
$user->lastname = $data->lastname;
$user->email = $data->email;
$user->password = $data->password;
// Поверка на существование e-mail в БД
// $email_exists = $user->emailExists();
// Здесь будет метод create()
// Создание пользователя
if (
!empty($user->firstname) &&
!empty($user->email) &&
// $email_exists == 0 &&
!empty($user->password) &&
$user->create()
) {
// Устанавливаем код ответа
http_response_code(200);
// Покажем сообщение о том, что пользователь был создан
echo json_encode(array("message" => "Пользователь был создан"));
}
// Сообщение, если не удаётся создать пользователя
else {
// Устанавливаем код ответа
http_response_code(400);
// Покажем сообщение о том, что создать пользователя не удалось
echo json_encode(array("message" => "Невозможно создать пользователя"));
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Firebase\JWT;
class BeforeValidException extends \UnexpectedValueException
{
}

View File

@@ -0,0 +1,258 @@
<?php
namespace Firebase\JWT;
use ArrayAccess;
use InvalidArgumentException;
use LogicException;
use OutOfBoundsException;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use RuntimeException;
use UnexpectedValueException;
/**
* @implements ArrayAccess<string, Key>
*/
class CachedKeySet implements ArrayAccess
{
/**
* @var string
*/
private $jwksUri;
/**
* @var ClientInterface
*/
private $httpClient;
/**
* @var RequestFactoryInterface
*/
private $httpFactory;
/**
* @var CacheItemPoolInterface
*/
private $cache;
/**
* @var ?int
*/
private $expiresAfter;
/**
* @var ?CacheItemInterface
*/
private $cacheItem;
/**
* @var array<string, array<mixed>>
*/
private $keySet;
/**
* @var string
*/
private $cacheKey;
/**
* @var string
*/
private $cacheKeyPrefix = 'jwks';
/**
* @var int
*/
private $maxKeyLength = 64;
/**
* @var bool
*/
private $rateLimit;
/**
* @var string
*/
private $rateLimitCacheKey;
/**
* @var int
*/
private $maxCallsPerMinute = 10;
/**
* @var string|null
*/
private $defaultAlg;
public function __construct(
string $jwksUri,
ClientInterface $httpClient,
RequestFactoryInterface $httpFactory,
CacheItemPoolInterface $cache,
int $expiresAfter = null,
bool $rateLimit = false,
string $defaultAlg = null
) {
$this->jwksUri = $jwksUri;
$this->httpClient = $httpClient;
$this->httpFactory = $httpFactory;
$this->cache = $cache;
$this->expiresAfter = $expiresAfter;
$this->rateLimit = $rateLimit;
$this->defaultAlg = $defaultAlg;
$this->setCacheKeys();
}
/**
* @param string $keyId
* @return Key
*/
public function offsetGet($keyId): Key
{
if (!$this->keyIdExists($keyId)) {
throw new OutOfBoundsException('Key ID not found');
}
return JWK::parseKey($this->keySet[$keyId], $this->defaultAlg);
}
/**
* @param string $keyId
* @return bool
*/
public function offsetExists($keyId): bool
{
return $this->keyIdExists($keyId);
}
/**
* @param string $offset
* @param Key $value
*/
public function offsetSet($offset, $value): void
{
throw new LogicException('Method not implemented');
}
/**
* @param string $offset
*/
public function offsetUnset($offset): void
{
throw new LogicException('Method not implemented');
}
/**
* @return array<mixed>
*/
private function formatJwksForCache(string $jwks): array
{
$jwks = json_decode($jwks, true);
if (!isset($jwks['keys'])) {
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
}
if (empty($jwks['keys'])) {
throw new InvalidArgumentException('JWK Set did not contain any keys');
}
$keys = [];
foreach ($jwks['keys'] as $k => $v) {
$kid = isset($v['kid']) ? $v['kid'] : $k;
$keys[(string) $kid] = $v;
}
return $keys;
}
private function keyIdExists(string $keyId): bool
{
if (null === $this->keySet) {
$item = $this->getCacheItem();
// Try to load keys from cache
if ($item->isHit()) {
// item found! retrieve it
$this->keySet = $item->get();
// If the cached item is a string, the JWKS response was cached (previous behavior).
// Parse this into expected format array<kid, jwk> instead.
if (\is_string($this->keySet)) {
$this->keySet = $this->formatJwksForCache($this->keySet);
}
}
}
if (!isset($this->keySet[$keyId])) {
if ($this->rateLimitExceeded()) {
return false;
}
$request = $this->httpFactory->createRequest('GET', $this->jwksUri);
$jwksResponse = $this->httpClient->sendRequest($request);
$this->keySet = $this->formatJwksForCache((string) $jwksResponse->getBody());
if (!isset($this->keySet[$keyId])) {
return false;
}
$item = $this->getCacheItem();
$item->set($this->keySet);
if ($this->expiresAfter) {
$item->expiresAfter($this->expiresAfter);
}
$this->cache->save($item);
}
return true;
}
private function rateLimitExceeded(): bool
{
if (!$this->rateLimit) {
return false;
}
$cacheItem = $this->cache->getItem($this->rateLimitCacheKey);
if (!$cacheItem->isHit()) {
$cacheItem->expiresAfter(1); // # of calls are cached each minute
}
$callsPerMinute = (int) $cacheItem->get();
if (++$callsPerMinute > $this->maxCallsPerMinute) {
return true;
}
$cacheItem->set($callsPerMinute);
$this->cache->save($cacheItem);
return false;
}
private function getCacheItem(): CacheItemInterface
{
if (\is_null($this->cacheItem)) {
$this->cacheItem = $this->cache->getItem($this->cacheKey);
}
return $this->cacheItem;
}
private function setCacheKeys(): void
{
if (empty($this->jwksUri)) {
throw new RuntimeException('JWKS URI is empty');
}
// ensure we do not have illegal characters
$key = preg_replace('|[^a-zA-Z0-9_\.!]|', '', $this->jwksUri);
// add prefix
$key = $this->cacheKeyPrefix . $key;
// Hash keys if they exceed $maxKeyLength of 64
if (\strlen($key) > $this->maxKeyLength) {
$key = substr(hash('sha256', $key), 0, $this->maxKeyLength);
}
$this->cacheKey = $key;
if ($this->rateLimit) {
// add prefix
$rateLimitKey = $this->cacheKeyPrefix . 'ratelimit' . $key;
// Hash keys if they exceed $maxKeyLength of 64
if (\strlen($rateLimitKey) > $this->maxKeyLength) {
$rateLimitKey = substr(hash('sha256', $rateLimitKey), 0, $this->maxKeyLength);
}
$this->rateLimitCacheKey = $rateLimitKey;
}
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Firebase\JWT;
class ExpiredException extends \UnexpectedValueException
{
}

View File

@@ -0,0 +1,323 @@
<?php
namespace Firebase\JWT;
use DomainException;
use InvalidArgumentException;
use UnexpectedValueException;
/**
* JSON Web Key implementation, based on this spec:
* https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
*
* PHP version 5
*
* @category Authentication
* @package Authentication_JWT
* @author Bui Sy Nguyen <nguyenbs@gmail.com>
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
* @link https://github.com/firebase/php-jwt
*/
class JWK
{
private const OID = '1.2.840.10045.2.1';
private const ASN1_OBJECT_IDENTIFIER = 0x06;
private const ASN1_SEQUENCE = 0x10; // also defined in JWT
private const ASN1_BIT_STRING = 0x03;
private const EC_CURVES = [
'P-256' => '1.2.840.10045.3.1.7', // Len: 64
'secp256k1' => '1.3.132.0.10', // Len: 64
// 'P-384' => '1.3.132.0.34', // Len: 96 (not yet supported)
// 'P-521' => '1.3.132.0.35', // Len: 132 (not supported)
];
/**
* Parse a set of JWK keys
*
* @param array<mixed> $jwks The JSON Web Key Set as an associative array
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
* JSON Web Key Set
*
* @return array<string, Key> An associative array of key IDs (kid) to Key objects
*
* @throws InvalidArgumentException Provided JWK Set is empty
* @throws UnexpectedValueException Provided JWK Set was invalid
* @throws DomainException OpenSSL failure
*
* @uses parseKey
*/
public static function parseKeySet(array $jwks, string $defaultAlg = null): array
{
$keys = [];
if (!isset($jwks['keys'])) {
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
}
if (empty($jwks['keys'])) {
throw new InvalidArgumentException('JWK Set did not contain any keys');
}
foreach ($jwks['keys'] as $k => $v) {
$kid = isset($v['kid']) ? $v['kid'] : $k;
if ($key = self::parseKey($v, $defaultAlg)) {
$keys[(string) $kid] = $key;
}
}
if (0 === \count($keys)) {
throw new UnexpectedValueException('No supported algorithms found in JWK Set');
}
return $keys;
}
/**
* Parse a JWK key
*
* @param array<mixed> $jwk An individual JWK
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
* JSON Web Key Set
*
* @return Key The key object for the JWK
*
* @throws InvalidArgumentException Provided JWK is empty
* @throws UnexpectedValueException Provided JWK was invalid
* @throws DomainException OpenSSL failure
*
* @uses createPemFromModulusAndExponent
*/
public static function parseKey(array $jwk, string $defaultAlg = null): ?Key
{
if (empty($jwk)) {
throw new InvalidArgumentException('JWK must not be empty');
}
if (!isset($jwk['kty'])) {
throw new UnexpectedValueException('JWK must contain a "kty" parameter');
}
if (!isset($jwk['alg'])) {
if (\is_null($defaultAlg)) {
// The "alg" parameter is optional in a KTY, but an algorithm is required
// for parsing in this library. Use the $defaultAlg parameter when parsing the
// key set in order to prevent this error.
// @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4
throw new UnexpectedValueException('JWK must contain an "alg" parameter');
}
$jwk['alg'] = $defaultAlg;
}
switch ($jwk['kty']) {
case 'RSA':
if (!empty($jwk['d'])) {
throw new UnexpectedValueException('RSA private keys are not supported');
}
if (!isset($jwk['n']) || !isset($jwk['e'])) {
throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
}
$pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
$publicKey = \openssl_pkey_get_public($pem);
if (false === $publicKey) {
throw new DomainException(
'OpenSSL error: ' . \openssl_error_string()
);
}
return new Key($publicKey, $jwk['alg']);
case 'EC':
if (isset($jwk['d'])) {
// The key is actually a private key
throw new UnexpectedValueException('Key data must be for a public key');
}
if (empty($jwk['crv'])) {
throw new UnexpectedValueException('crv not set');
}
if (!isset(self::EC_CURVES[$jwk['crv']])) {
throw new DomainException('Unrecognised or unsupported EC curve');
}
if (empty($jwk['x']) || empty($jwk['y'])) {
throw new UnexpectedValueException('x and y not set');
}
$publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']);
return new Key($publicKey, $jwk['alg']);
default:
// Currently only RSA is supported
break;
}
return null;
}
/**
* Converts the EC JWK values to pem format.
*
* @param string $crv The EC curve (only P-256 is supported)
* @param string $x The EC x-coordinate
* @param string $y The EC y-coordinate
*
* @return string
*/
private static function createPemFromCrvAndXYCoordinates(string $crv, string $x, string $y): string
{
$pem =
self::encodeDER(
self::ASN1_SEQUENCE,
self::encodeDER(
self::ASN1_SEQUENCE,
self::encodeDER(
self::ASN1_OBJECT_IDENTIFIER,
self::encodeOID(self::OID)
)
. self::encodeDER(
self::ASN1_OBJECT_IDENTIFIER,
self::encodeOID(self::EC_CURVES[$crv])
)
) .
self::encodeDER(
self::ASN1_BIT_STRING,
\chr(0x00) . \chr(0x04)
. JWT::urlsafeB64Decode($x)
. JWT::urlsafeB64Decode($y)
)
);
return sprintf(
"-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
wordwrap(base64_encode($pem), 64, "\n", true)
);
}
/**
* Create a public key represented in PEM format from RSA modulus and exponent information
*
* @param string $n The RSA modulus encoded in Base64
* @param string $e The RSA exponent encoded in Base64
*
* @return string The RSA public key represented in PEM format
*
* @uses encodeLength
*/
private static function createPemFromModulusAndExponent(
string $n,
string $e
): string {
$mod = JWT::urlsafeB64Decode($n);
$exp = JWT::urlsafeB64Decode($e);
$modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod);
$publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp);
$rsaPublicKey = \pack(
'Ca*a*a*',
48,
self::encodeLength(\strlen($modulus) + \strlen($publicExponent)),
$modulus,
$publicExponent
);
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
$rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
$rsaPublicKey = \chr(0) . $rsaPublicKey;
$rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
$rsaPublicKey = \pack(
'Ca*a*',
48,
self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
$rsaOID . $rsaPublicKey
);
return "-----BEGIN PUBLIC KEY-----\r\n" .
\chunk_split(\base64_encode($rsaPublicKey), 64) .
'-----END PUBLIC KEY-----';
}
/**
* DER-encode the length
*
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
*
* @param int $length
* @return string
*/
private static function encodeLength(int $length): string
{
if ($length <= 0x7F) {
return \chr($length);
}
$temp = \ltrim(\pack('N', $length), \chr(0));
return \pack('Ca*', 0x80 | \strlen($temp), $temp);
}
/**
* Encodes a value into a DER object.
* Also defined in Firebase\JWT\JWT
*
* @param int $type DER tag
* @param string $value the value to encode
* @return string the encoded object
*/
private static function encodeDER(int $type, string $value): string
{
$tag_header = 0;
if ($type === self::ASN1_SEQUENCE) {
$tag_header |= 0x20;
}
// Type
$der = \chr($tag_header | $type);
// Length
$der .= \chr(\strlen($value));
return $der . $value;
}
/**
* Encodes a string into a DER-encoded OID.
*
* @param string $oid the OID string
* @return string the binary DER-encoded OID
*/
private static function encodeOID(string $oid): string
{
$octets = explode('.', $oid);
// Get the first octet
$first = (int) array_shift($octets);
$second = (int) array_shift($octets);
$oid = \chr($first * 40 + $second);
// Iterate over subsequent octets
foreach ($octets as $octet) {
if ($octet == 0) {
$oid .= \chr(0x00);
continue;
}
$bin = '';
while ($octet) {
$bin .= \chr(0x80 | ($octet & 0x7f));
$octet >>= 7;
}
$bin[0] = $bin[0] & \chr(0x7f);
// Convert to big endian if necessary
if (pack('V', 65534) == pack('L', 65534)) {
$oid .= strrev($bin);
} else {
$oid .= $bin;
}
}
return $oid;
}
}

View File

@@ -0,0 +1,638 @@
<?php
namespace Firebase\JWT;
use ArrayAccess;
use DateTime;
use DomainException;
use Exception;
use InvalidArgumentException;
use OpenSSLAsymmetricKey;
use OpenSSLCertificate;
use stdClass;
use UnexpectedValueException;
/**
* JSON Web Token implementation, based on this spec:
* https://tools.ietf.org/html/rfc7519
*
* PHP version 5
*
* @category Authentication
* @package Authentication_JWT
* @author Neuman Vong <neuman@twilio.com>
* @author Anant Narayanan <anant@php.net>
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
* @link https://github.com/firebase/php-jwt
*/
class JWT
{
private const ASN1_INTEGER = 0x02;
private const ASN1_SEQUENCE = 0x10;
private const ASN1_BIT_STRING = 0x03;
/**
* When checking nbf, iat or expiration times,
* we want to provide some extra leeway time to
* account for clock skew.
*
* @var int
*/
public static $leeway = 0;
/**
* Allow the current timestamp to be specified.
* Useful for fixing a value within unit testing.
* Will default to PHP time() value if null.
*
* @var ?int
*/
public static $timestamp = null;
/**
* @var array<string, string[]>
*/
public static $supported_algs = [
'ES384' => ['openssl', 'SHA384'],
'ES256' => ['openssl', 'SHA256'],
'ES256K' => ['openssl', 'SHA256'],
'HS256' => ['hash_hmac', 'SHA256'],
'HS384' => ['hash_hmac', 'SHA384'],
'HS512' => ['hash_hmac', 'SHA512'],
'RS256' => ['openssl', 'SHA256'],
'RS384' => ['openssl', 'SHA384'],
'RS512' => ['openssl', 'SHA512'],
'EdDSA' => ['sodium_crypto', 'EdDSA'],
];
/**
* Decodes a JWT string into a PHP object.
*
* @param string $jwt The JWT
* @param Key|array<string,Key> $keyOrKeyArray The Key or associative array of key IDs (kid) to Key objects.
* If the algorithm used is asymmetric, this is the public key
* Each Key object contains an algorithm and matching key.
* Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
* 'HS512', 'RS256', 'RS384', and 'RS512'
*
* @return stdClass The JWT's payload as a PHP object
*
* @throws InvalidArgumentException Provided key/key-array was empty or malformed
* @throws DomainException Provided JWT is malformed
* @throws UnexpectedValueException Provided JWT was invalid
* @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
* @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
* @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'
* @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim
*
* @uses jsonDecode
* @uses urlsafeB64Decode
*/
public static function decode(
string $jwt,
$keyOrKeyArray
): stdClass {
// Validate JWT
$timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
if (empty($keyOrKeyArray)) {
throw new InvalidArgumentException('Key may not be empty');
}
$tks = \explode('.', $jwt);
if (\count($tks) !== 3) {
throw new UnexpectedValueException('Wrong number of segments');
}
list($headb64, $bodyb64, $cryptob64) = $tks;
$headerRaw = static::urlsafeB64Decode($headb64);
if (null === ($header = static::jsonDecode($headerRaw))) {
throw new UnexpectedValueException('Invalid header encoding');
}
$payloadRaw = static::urlsafeB64Decode($bodyb64);
if (null === ($payload = static::jsonDecode($payloadRaw))) {
throw new UnexpectedValueException('Invalid claims encoding');
}
if (\is_array($payload)) {
// prevent PHP Fatal Error in edge-cases when payload is empty array
$payload = (object) $payload;
}
if (!$payload instanceof stdClass) {
throw new UnexpectedValueException('Payload must be a JSON object');
}
$sig = static::urlsafeB64Decode($cryptob64);
if (empty($header->alg)) {
throw new UnexpectedValueException('Empty algorithm');
}
if (empty(static::$supported_algs[$header->alg])) {
throw new UnexpectedValueException('Algorithm not supported');
}
$key = self::getKey($keyOrKeyArray, property_exists($header, 'kid') ? $header->kid : null);
// Check the algorithm
if (!self::constantTimeEquals($key->getAlgorithm(), $header->alg)) {
// See issue #351
throw new UnexpectedValueException('Incorrect key for this algorithm');
}
if (\in_array($header->alg, ['ES256', 'ES256K', 'ES384'], true)) {
// OpenSSL expects an ASN.1 DER sequence for ES256/ES256K/ES384 signatures
$sig = self::signatureToDER($sig);
}
if (!self::verify("{$headb64}.{$bodyb64}", $sig, $key->getKeyMaterial(), $header->alg)) {
throw new SignatureInvalidException('Signature verification failed');
}
// Check the nbf if it is defined. This is the time that the
// token can actually be used. If it's not yet that time, abort.
if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
throw new BeforeValidException(
'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf)
);
}
// Check that this token has been created before 'now'. This prevents
// using tokens that have been created for later use (and haven't
// correctly used the nbf claim).
if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
throw new BeforeValidException(
'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat)
);
}
// Check if this token has expired.
if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
throw new ExpiredException('Expired token');
}
return $payload;
}
/**
* Converts and signs a PHP array into a JWT string.
*
* @param array<mixed> $payload PHP array
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
* @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
* @param string $keyId
* @param array<string, string> $head An array with header elements to attach
*
* @return string A signed JWT
*
* @uses jsonEncode
* @uses urlsafeB64Encode
*/
public static function encode(
array $payload,
$key,
string $alg,
string $keyId = null,
array $head = null
): string {
$header = ['typ' => 'JWT', 'alg' => $alg];
if ($keyId !== null) {
$header['kid'] = $keyId;
}
if (isset($head) && \is_array($head)) {
$header = \array_merge($head, $header);
}
$segments = [];
$segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header));
$segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload));
$signing_input = \implode('.', $segments);
$signature = static::sign($signing_input, $key, $alg);
$segments[] = static::urlsafeB64Encode($signature);
return \implode('.', $segments);
}
/**
* Sign a string with a given key and algorithm.
*
* @param string $msg The message to sign
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
* @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
*
* @return string An encrypted message
*
* @throws DomainException Unsupported algorithm or bad key was specified
*/
public static function sign(
string $msg,
$key,
string $alg
): string {
if (empty(static::$supported_algs[$alg])) {
throw new DomainException('Algorithm not supported');
}
list($function, $algorithm) = static::$supported_algs[$alg];
switch ($function) {
case 'hash_hmac':
if (!\is_string($key)) {
throw new InvalidArgumentException('key must be a string when using hmac');
}
return \hash_hmac($algorithm, $msg, $key, true);
case 'openssl':
$signature = '';
$success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line
if (!$success) {
throw new DomainException('OpenSSL unable to sign data');
}
if ($alg === 'ES256' || $alg === 'ES256K') {
$signature = self::signatureFromDER($signature, 256);
} elseif ($alg === 'ES384') {
$signature = self::signatureFromDER($signature, 384);
}
return $signature;
case 'sodium_crypto':
if (!\function_exists('sodium_crypto_sign_detached')) {
throw new DomainException('libsodium is not available');
}
if (!\is_string($key)) {
throw new InvalidArgumentException('key must be a string when using EdDSA');
}
try {
// The last non-empty line is used as the key.
$lines = array_filter(explode("\n", $key));
$key = base64_decode((string) end($lines));
if (\strlen($key) === 0) {
throw new DomainException('Key cannot be empty string');
}
return sodium_crypto_sign_detached($msg, $key);
} catch (Exception $e) {
throw new DomainException($e->getMessage(), 0, $e);
}
}
throw new DomainException('Algorithm not supported');
}
/**
* Verify a signature with the message, key and method. Not all methods
* are symmetric, so we must have a separate verify and sign method.
*
* @param string $msg The original message (header and body)
* @param string $signature The original signature
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
* @param string $alg The algorithm
*
* @return bool
*
* @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure
*/
private static function verify(
string $msg,
string $signature,
$keyMaterial,
string $alg
): bool {
if (empty(static::$supported_algs[$alg])) {
throw new DomainException('Algorithm not supported');
}
list($function, $algorithm) = static::$supported_algs[$alg];
switch ($function) {
case 'openssl':
$success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); // @phpstan-ignore-line
if ($success === 1) {
return true;
}
if ($success === 0) {
return false;
}
// returns 1 on success, 0 on failure, -1 on error.
throw new DomainException(
'OpenSSL error: ' . \openssl_error_string()
);
case 'sodium_crypto':
if (!\function_exists('sodium_crypto_sign_verify_detached')) {
throw new DomainException('libsodium is not available');
}
if (!\is_string($keyMaterial)) {
throw new InvalidArgumentException('key must be a string when using EdDSA');
}
try {
// The last non-empty line is used as the key.
$lines = array_filter(explode("\n", $keyMaterial));
$key = base64_decode((string) end($lines));
if (\strlen($key) === 0) {
throw new DomainException('Key cannot be empty string');
}
if (\strlen($signature) === 0) {
throw new DomainException('Signature cannot be empty string');
}
return sodium_crypto_sign_verify_detached($signature, $msg, $key);
} catch (Exception $e) {
throw new DomainException($e->getMessage(), 0, $e);
}
case 'hash_hmac':
default:
if (!\is_string($keyMaterial)) {
throw new InvalidArgumentException('key must be a string when using hmac');
}
$hash = \hash_hmac($algorithm, $msg, $keyMaterial, true);
return self::constantTimeEquals($hash, $signature);
}
}
/**
* Decode a JSON string into a PHP object.
*
* @param string $input JSON string
*
* @return mixed The decoded JSON string
*
* @throws DomainException Provided string was invalid JSON
*/
public static function jsonDecode(string $input)
{
$obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
if ($errno = \json_last_error()) {
self::handleJsonError($errno);
} elseif ($obj === null && $input !== 'null') {
throw new DomainException('Null result with non-null input');
}
return $obj;
}
/**
* Encode a PHP array into a JSON string.
*
* @param array<mixed> $input A PHP array
*
* @return string JSON representation of the PHP array
*
* @throws DomainException Provided object could not be encoded to valid JSON
*/
public static function jsonEncode(array $input): string
{
if (PHP_VERSION_ID >= 50400) {
$json = \json_encode($input, \JSON_UNESCAPED_SLASHES);
} else {
// PHP 5.3 only
$json = \json_encode($input);
}
if ($errno = \json_last_error()) {
self::handleJsonError($errno);
} elseif ($json === 'null' && $input !== null) {
throw new DomainException('Null result with non-null input');
}
if ($json === false) {
throw new DomainException('Provided object could not be encoded to valid JSON');
}
return $json;
}
/**
* Decode a string with URL-safe Base64.
*
* @param string $input A Base64 encoded string
*
* @return string A decoded string
*
* @throws InvalidArgumentException invalid base64 characters
*/
public static function urlsafeB64Decode(string $input): string
{
$remainder = \strlen($input) % 4;
if ($remainder) {
$padlen = 4 - $remainder;
$input .= \str_repeat('=', $padlen);
}
return \base64_decode(\strtr($input, '-_', '+/'));
}
/**
* Encode a string with URL-safe Base64.
*
* @param string $input The string you want encoded
*
* @return string The base64 encode of what you passed in
*/
public static function urlsafeB64Encode(string $input): string
{
return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
}
/**
* Determine if an algorithm has been provided for each Key
*
* @param Key|ArrayAccess<string,Key>|array<string,Key> $keyOrKeyArray
* @param string|null $kid
*
* @throws UnexpectedValueException
*
* @return Key
*/
private static function getKey(
$keyOrKeyArray,
?string $kid
): Key {
if ($keyOrKeyArray instanceof Key) {
return $keyOrKeyArray;
}
if (empty($kid)) {
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
}
if ($keyOrKeyArray instanceof CachedKeySet) {
// Skip "isset" check, as this will automatically refresh if not set
return $keyOrKeyArray[$kid];
}
if (!isset($keyOrKeyArray[$kid])) {
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
}
return $keyOrKeyArray[$kid];
}
/**
* @param string $left The string of known length to compare against
* @param string $right The user-supplied string
* @return bool
*/
public static function constantTimeEquals(string $left, string $right): bool
{
if (\function_exists('hash_equals')) {
return \hash_equals($left, $right);
}
$len = \min(self::safeStrlen($left), self::safeStrlen($right));
$status = 0;
for ($i = 0; $i < $len; $i++) {
$status |= (\ord($left[$i]) ^ \ord($right[$i]));
}
$status |= (self::safeStrlen($left) ^ self::safeStrlen($right));
return ($status === 0);
}
/**
* Helper method to create a JSON error.
*
* @param int $errno An error number from json_last_error()
*
* @throws DomainException
*
* @return void
*/
private static function handleJsonError(int $errno): void
{
$messages = [
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
];
throw new DomainException(
isset($messages[$errno])
? $messages[$errno]
: 'Unknown JSON error: ' . $errno
);
}
/**
* Get the number of bytes in cryptographic strings.
*
* @param string $str
*
* @return int
*/
private static function safeStrlen(string $str): int
{
if (\function_exists('mb_strlen')) {
return \mb_strlen($str, '8bit');
}
return \strlen($str);
}
/**
* Convert an ECDSA signature to an ASN.1 DER sequence
*
* @param string $sig The ECDSA signature to convert
* @return string The encoded DER object
*/
private static function signatureToDER(string $sig): string
{
// Separate the signature into r-value and s-value
$length = max(1, (int) (\strlen($sig) / 2));
list($r, $s) = \str_split($sig, $length);
// Trim leading zeros
$r = \ltrim($r, "\x00");
$s = \ltrim($s, "\x00");
// Convert r-value and s-value from unsigned big-endian integers to
// signed two's complement
if (\ord($r[0]) > 0x7f) {
$r = "\x00" . $r;
}
if (\ord($s[0]) > 0x7f) {
$s = "\x00" . $s;
}
return self::encodeDER(
self::ASN1_SEQUENCE,
self::encodeDER(self::ASN1_INTEGER, $r) .
self::encodeDER(self::ASN1_INTEGER, $s)
);
}
/**
* Encodes a value into a DER object.
*
* @param int $type DER tag
* @param string $value the value to encode
*
* @return string the encoded object
*/
private static function encodeDER(int $type, string $value): string
{
$tag_header = 0;
if ($type === self::ASN1_SEQUENCE) {
$tag_header |= 0x20;
}
// Type
$der = \chr($tag_header | $type);
// Length
$der .= \chr(\strlen($value));
return $der . $value;
}
/**
* Encodes signature from a DER object.
*
* @param string $der binary signature in DER format
* @param int $keySize the number of bits in the key
*
* @return string the signature
*/
private static function signatureFromDER(string $der, int $keySize): string
{
// OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
list($offset, $_) = self::readDER($der);
list($offset, $r) = self::readDER($der, $offset);
list($offset, $s) = self::readDER($der, $offset);
// Convert r-value and s-value from signed two's compliment to unsigned
// big-endian integers
$r = \ltrim($r, "\x00");
$s = \ltrim($s, "\x00");
// Pad out r and s so that they are $keySize bits long
$r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
$s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
return $r . $s;
}
/**
* Reads binary DER-encoded data and decodes into a single object
*
* @param string $der the binary data in DER format
* @param int $offset the offset of the data stream containing the object
* to decode
*
* @return array{int, string|null} the new offset and the decoded object
*/
private static function readDER(string $der, int $offset = 0): array
{
$pos = $offset;
$size = \strlen($der);
$constructed = (\ord($der[$pos]) >> 5) & 0x01;
$type = \ord($der[$pos++]) & 0x1f;
// Length
$len = \ord($der[$pos++]);
if ($len & 0x80) {
$n = $len & 0x1f;
$len = 0;
while ($n-- && $pos < $size) {
$len = ($len << 8) | \ord($der[$pos++]);
}
}
// Value
if ($type === self::ASN1_BIT_STRING) {
$pos++; // Skip the first contents octet (padding indicator)
$data = \substr($der, $pos, $len - 1);
$pos += $len - 1;
} elseif (!$constructed) {
$data = \substr($der, $pos, $len);
$pos += $len;
} else {
$data = null;
}
return [$pos, $data];
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Firebase\JWT;
use InvalidArgumentException;
use OpenSSLAsymmetricKey;
use OpenSSLCertificate;
use TypeError;
class Key
{
/** @var string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate */
private $keyMaterial;
/** @var string */
private $algorithm;
/**
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
* @param string $algorithm
*/
public function __construct(
$keyMaterial,
string $algorithm
) {
if (
!\is_string($keyMaterial)
&& !$keyMaterial instanceof OpenSSLAsymmetricKey
&& !$keyMaterial instanceof OpenSSLCertificate
&& !\is_resource($keyMaterial)
) {
throw new TypeError('Key material must be a string, resource, or OpenSSLAsymmetricKey');
}
if (empty($keyMaterial)) {
throw new InvalidArgumentException('Key material must not be empty');
}
if (empty($algorithm)) {
throw new InvalidArgumentException('Algorithm must not be empty');
}
// TODO: Remove in PHP 8.0 in favor of class constructor property promotion
$this->keyMaterial = $keyMaterial;
$this->algorithm = $algorithm;
}
/**
* Return the algorithm valid for this key
*
* @return string
*/
public function getAlgorithm(): string
{
return $this->algorithm;
}
/**
* @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate
*/
public function getKeyMaterial()
{
return $this->keyMaterial;
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Firebase\JWT;
class SignatureInvalidException extends \UnexpectedValueException
{
}

View File

@@ -0,0 +1,78 @@
<?php
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Здесь будет соединение с БД
// Файлы необходимые для соединения с БД
//include_once __DIR__."/Config/DatabasePGSQL.php";
include_once __DIR__."/Config/Database.php";
//include_once __DIR__."/Config/DatabasePGSQL.php";
include_once __DIR__."/Objects/User.php";
// Получаем соединение с базой данных
$database = new Database();
$db = $database->getConnection();
// Создание объекта "User"
$user = new User($db);
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
// Устанавливаем значения
$user->email = $data->email;
$email_exists = $user->emailExists();
// Файлы для JWT будут здесь
// Подключение файлов JWT
include_once __DIR__."/Config/core.php";
include_once __DIR__."/libs/php-jwt/BeforeValidException.php";
include_once __DIR__."/libs/php-jwt/ExpiredException.php";
include_once __DIR__."/libs/php-jwt/SignatureInvalidException.php";
include_once __DIR__."/libs/php-jwt/JWT.php";
use \Firebase\JWT\JWT;
// Существует ли электронная почта и соответствует ли пароль тому, что находится в базе данных
if ($email_exists['result'] && password_verify($data->password, $user->password)) {
$token = array(
"iss" => $iss,
"aud" => $aud,
"iat" => $iat,
"nbf" => $nbf,
"data" => array(
"id" => $user->id,
"firstname" => $user->firstname,
"lastname" => $user->lastname,
"email" => $user->email
)
);
// Код ответа
http_response_code(200);
// Создание jwt
$jwt = JWT::encode($token, $key, 'HS256');
echo json_encode(
array(
"message" => "Успешный вход в систему",
"jwt" => $jwt
)
);
}
// Если электронная почта не существует или пароль не совпадает,
// Сообщим пользователю, что он не может войти в систему
else {
// Код ответа
// http_response_code(401);
// Скажем пользователю что войти не удалось
echo json_encode(array("message" => "Ошибка входа","error"=>$email_exists['error']));
}
?>

View File

@@ -0,0 +1,124 @@
<?php
// Заголовки
header("Access-Control-Allow-Origin:*");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Требуется для кодирования веб-токена JSON
include_once "Config/core.php";
include_once "libs/php-jwt/BeforeValidException.php";
include_once "libs/php-jwt/ExpiredException.php";
include_once "libs/php-jwt/SignatureInvalidException.php";
include_once "libs/php-jwt/JWT.php";
include_once "libs/php-jwt/Key.php";
use \Firebase\JWT\JWT;
use \Firebase\JWT\Key;
// Файлы, необходимые для подключения к базе данных
include_once "Config/Database.php";
//include_once __DIR__."/Config/DatabasePGSQL.php";
include_once "Objects/User.php";
// Получаем соединение с БД
$database = new Database();
$db = $database->getConnection();
// Создание объекта "User"
$user = new User($db);
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
// Получаем jwt
$jwt = isset($data->jwt) ? $data->jwt : "";
// Если JWT не пуст
if ($jwt) {
// Если декодирование выполнено успешно, показать данные пользователя
try {
// Декодирование jwt
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
// Нам нужно установить отправленные данные (через форму HTML) в свойствах объекта пользователя
$user->firstname = $data->firstname;
$user->lastname = $data->lastname;
$user->email = $data->email;
$user->password = $data->password;
$user->id = $decoded->data->id;
// Создание пользователя
if ($user->update()) {
// сгенерировать заново JWT здесь --->
// Нам нужно заново сгенерировать JWT, потому что данные пользователя могут отличаться
$token = array(
"iss" => $iss,
"aud" => $aud,
"iat" => $iat,
"nbf" => $nbf,
"data" => array(
"id" => $user->id,
"firstname" => $user->firstname,
"lastname" => $user->lastname,
"email" => $user->email
)
);
$jwt = JWT::encode($token, $key, 'HS256');
// Код ответа
http_response_code(200);
// Ответ в формате JSON
echo json_encode(
array(
"message" => "Пользователь был обновлён",
"jwt" => $jwt
)
);
//<-----
}
// Сообщение, если не удается обновить пользователя
else {
// Код ответа
http_response_code(401);
// Показать сообщение об ошибке
echo json_encode(array("message" => "Невозможно обновить пользователя"));
}
}
// Если декодирование не удалось, это означает, что JWT является недействительным
catch (Exception $e) {
// Код ответа
http_response_code(401);
// Сообщение об ошибке
echo json_encode(array(
"message" => "Доступ закрыт",
"error" => $e->getMessage()
));
}
}
// Показать сообщение об ошибке, если jwt пуст
else {
// Код ответа
http_response_code(401);
// Сообщить пользователю что доступ запрещен
echo json_encode(array("message" => "Доступ закрыт"));
}

View File

@@ -0,0 +1,69 @@
<?php
// Заголовки
header("Access-Control-Allow-Origin:*");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Требуется для декодирования JWT
include_once "Config/core.php";
include_once "libs/php-jwt/BeforeValidException.php";
include_once "libs/php-jwt/ExpiredException.php";
include_once "libs/php-jwt/SignatureInvalidException.php";
include_once "libs/php-jwt/JWT.php";
include_once "libs/php-jwt/Key.php";
use \Firebase\JWT\JWT;
use \Firebase\JWT\Key;
// Получаем значение веб-токена JSON
$data = json_decode(file_get_contents("php://input"));
// Получаем JWT
$jwt = isset($data->jwt) ? $data->jwt : "";
// Если JWT не пуст
if ($jwt) {
// Если декодирование выполнено успешно, показать данные пользователя
try {
// Декодирование jwt
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
// Код ответа
http_response_code(200);
// Покажем детали
echo json_encode(array(
"result"=>1,
"message" => "Доступ разрешен",
"data" => $decoded->data
));
}
// Если декодирование не удалось, это означает, что JWT является недействительным
catch (Exception $e) {
// Код ответа
http_response_code(401);
// Сообщим пользователю что ему отказано в доступе и покажем сообщение об ошибке
echo json_encode(array(
"result"=>0,
"message" => "Close ".$jwt,
"error" => $e->getMessage()
));
}
}
// Покажем сообщение об ошибке, если JWT пуст
else {
// Код ответа
// http_response_code(401);
// Сообщим пользователю что доступ запрещен
echo json_encode(array("message" => "Access denied"));
}

View File

@@ -0,0 +1,33 @@
<?php
error_reporting(0);
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Получаем данные
$data = json_decode(file_get_contents("php://input"),true);
include_once "Class/function.php";
include_once "Class/Database.php";
//include_once "Class/DatabasePGSQL.php";
include_once "Class/MainClass.php";
$database = new Database();
//$database = new Database();//PGSQL
$db = $database->getConnection();
$main_class = new MainClass($db);
$result=$main_class->DeleteCollect($data);
echo json_encode($result);

View File

@@ -0,0 +1,31 @@
<?php
error_reporting(0);
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
include_once "Class/function.php";
include_once "Class/Database.php";
//include_once "Class/DatabasePGSQL.php";
include_once "Class/MainClass.php";
$arr= json_decode($data -> data,true);
$database = new Database();
//$database = new Database();//PGSQL
$db = $database->getConnection();
$main_class = new MainClass($db);
$result=$main_class->DataCollect($arr);
echo json_encode($result);

View File

@@ -0,0 +1,3 @@
<?php
echo "OOOOOO";
?>

View File

@@ -0,0 +1,40 @@
<?php
class DataForms{
public function __construct($db)
{
$this->conn = $db;
}
function to_UTF8($arr) {
foreach($arr as $k=>$v){
$v=iconv('Windows-1251','UTF-8',$v);
$arr[$k]=$v;
}
return $arr;
}
function Query()
{
$result=[
1,2,3
];
return $result;
}
}

View File

@@ -0,0 +1,31 @@
<?php
// Используем для подключения к базе данных MySQL
class Database
{
// Учётные данные базы данных
private $host = "mysql:3306";
public $db_name = "tsd";
private $username = "tsduser";
private $password = "sdWs@a201!";
// private $username = "root";
// private $password = "secret";
public $conn;
// Получаем соединение с базой данных
public function getConnection()
{
$this->conn = null;
try {
$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
} catch (PDOException $exception) {
echo "Ошибка соединения с БД: " . $exception->getMessage();
}
return $this->conn;
}
}

View File

@@ -0,0 +1,28 @@
<?php
// Используем для подключения к базе данных MySQL
class DatabaseMS
{
// Учётные данные базы данных
private $host = "omega-server";
public $db_name = "SKL_BLOKSQL";
private $username = "sa";
private $password = "haqdvega#";
public $conn;
// Получаем соединение с базой данных
public function getConnection()
{
$this->conn = null;
try {
$this->conn = new PDO("dblib:host=" . $this->host . ";dbname=" . $this->db_name.";charset=CP1251", $this->username, $this->password);
} catch (PDOException $exception) {
echo "Ошибка соединения с БД: " . $exception->getMessage();
}
return $this->conn;
}
}

View File

@@ -0,0 +1,31 @@
<?php
// Используем для подключения к базе данных MySQL
class Database
{
// Учётные данные базы данных
//private $host = "localhost:3308";
private $host = "10.10.1.246";
public $db_name = "tsd_users";
//private $username = "tsd_user";
private $username = "tsd";
//private $password = "Dfrgvm3@1";
private $password = "gcIL6UrWFtSdDTbz";
public $conn;
// Получаем соединение с базой данных
public function getConnection()
{
$this->conn = null;
try {
$this->conn = new PDO("pgsql:host=".$this->host.";port=5432;dbname=".$this->db_name, $this->username, $this->password);
//$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
} catch (PDOException $exception) {
echo "Ошибка соединения с БД: " . $exception->getMessage();
}
return $this->conn;
}
}

View File

@@ -0,0 +1,331 @@
<?php
class MainClass {
private $headers = Array("Authorization: fi9tzpIHgp84E1Pf45rMzR/hy8lA2zE5", 'Content-Type: "application/json"');
public function __construct($db)
{
$this->conn = $db;
}
function WrFile($param){
//запись в файл. Не понятно зачем, но пока оставил
$path='/var/www/tmp';
$file_name=$param['date_query'].'.json';
if(!file_exists($path)){
mkdir($path);
chmod ( $path, 0777 );
}
$path=$path.'/beru';
if(!file_exists($path)){
mkdir($path);
chmod ( $path, 0777 );
}
if(file_exists($path.'/'.$file_name)){
unlink($path.'/'.$file_name);
}
file_put_contents($path.'/'.$file_name, $param['data_json']);
}
function getSSLPage($url,$post_data,$headers) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if(strlen(trim($post_data))>0){
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
}
curl_setopt ( $ch , CURLOPT_SSL_VERIFYPEER, 0 );
curl_setopt ( $ch , CURLOPT_SSL_VERIFYHOST, 0 );
//$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
function wrDatabase($param){
/*
$param=[
`'date_query' => $date_query,
'data_json' => $data_json,
'db'=> $db,
'user'= $user
];`
*/
$opers=json_decode($param['data_json'],true);
$user=$param['user'];
$date_query=$param['date_query'];
$firma=$param['kag']['firma'];
try{
for($i=0;$i<count($opers);$i++){
$item=$opers[$i];
$vars=[
'idoper'=>$item['id'],
'data_json' => json_encode($item),
'user'=>$user,
'date_d'=>date('Y-m-d H:i:s'),
'date_query'=>$date_query,
'firma'=>$firma,
];
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->conn->beginTransaction();
//удалить предыдущие загрузки по дате загрузки и номеру акта
$sth = $this->conn->prepare("delete from oper where idoper=:id and firma=:firma and date_query=:date_query");
$sth->execute([
'id'=>$item['id'],
'firma'=>$firma,
'date_query'=>$date_query,
]);
$query="insert into oper
(idoper,firma, data_json,date_d,user,date_query)
values
(:idoper,:firma, :data_json,:date_d,:user,:date_query)";
$stmt = $this->conn->prepare($query);
$stmt->execute($vars);
$error=$stmt->errorCode();
$ids=$this->conn->lastInsertId();
$this->conn->commit();
//idoper == >
$ids_arr[$item['id']]=$ids;
}
$result= ['result'=>1,'result'=>$ids_arr];
}catch (Exception $e) {
$this->conn->rollBack();
$error = "Ошибка: " . $e->getMessage();
$result = ['result'=>0,'error'=>$error];
}
return $result;
}
function DataCollect($param){
//результаты сканирования актов
$query = "select
id,
barcode,
date_up
from oper_collect
where idoper=:id and firma=:firma ORDER BY date_up";
$stmt = $this->conn->prepare($query);
$vars['id'] = $param['idoper'];
$vars['firma'] = $param['kag']['firma'];
$stmt->execute($vars);
$error = $stmt->errorCode();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row;
}
return ['idoper'=>$param['idoper'] ,'collect'=>$result,'v'=>$vars];
}
function History($param){
$limit=$param['limit'];
$kag=$param['kag'];
$vars=[
'firma'=>$param['kag']['firma'],
];
$query = "select
id,
idoper,
date_query,
DATE_FORMAT(date_query,'%d.%m.%Y') as date_q,
data_json
from oper
where firma=:firma
ORDER BY date_query DESC, id DESC
limit 10
";
$stmt = $this->conn->prepare($query);
$stmt->execute($vars);
$error = $stmt->errorCode();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$row['data_json']=json_decode($row['data_json'],true);
$row['data_collect']=$this->DataCollect(['idoper'=>$row['idoper'],'kag'=>$param['kag']]);
$row['param']=$param;
$result[] = $row;
}
return ['data'=>$result];
}
function pushCollect($data){
//записать данные по сборке
try{
//--Повтор------------>
$query = "select
id
from oper_collect
where idoper=:id and firma=:firma and (barcode=:barcode OR barcode_json like '%".$data['barcode']."%') ORDER BY date_up";
$stmt = $this->conn->prepare($query);
$vars['id'] = $data['idoper'];
$vars['firma'] = $data['kag']['firma'];
$vars['barcode'] = $data['barcode'];
$stmt->execute($vars);
$error = $stmt->errorCode();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$result_b[] = $row;
}
if(count($result_b)){
$error = "Повтор ".$data['barcode'];
$result = ['result'=>0,'error'=>$error];
return $result;
}
//<--Повтор------------
$vars=[
'idoper'=>$data['idoper'],
'date_query'=>$data['date_query'],
'barcode' => $data['barcode'],
'barcode_json'=> json_encode($data['barcode_json']),
'date_up'=>date('Y-m-d H:i:s'),
'user'=>$data['user'],
'firma'=>$data['kag']['firma'],
];
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->conn->beginTransaction();
$query="insert into oper_collect
(idoper,firma , date_query, barcode,barcode_json,date_up,user)
values
(:idoper, :firma, :date_query,:barcode,:barcode_json,:date_up,:user)";
$stmt = $this->conn->prepare($query);
$stmt->execute($vars);
$error=$stmt->errorCode();
$id_new=$this->conn->lastInsertId();
$this->conn->commit();
$result= ['result'=>1];
}catch (Exception $e) {
$this->conn->rollBack();
$error = "Ошибка: " . $e->getMessage();
$result = ['result'=>0,'error'=>$error,'data'=>$data];
}
return $result;
}
function PostingStatus($params)
{
//$kag=$params['kag'];
//$posting_number=$params['posting_number'];
$url='https://marketplace.book-online.ru/api/posting_status/';
$json=json_encode($params);
$result=$this->getSSLPage($url,$json,$this->headers);
return json_decode($result);
}
function DeleteCollect($params)
{
$oper=$params['oper'];
$firma=$params['firma'];
if(strlen(trim($oper))>0 and strlen($firma)>0){
$query="delete from oper_collect
where idoper=:id and firma=:firma";
$vars=['id'=>$oper,'firma'=>$firma];
$stmt = $this->conn->prepare($query);
$stmt->execute($vars);
$error=$stmt->errorCode();
return ['result'=>1,'error'=>$error,'vars'=>$vars];
}
return ['result'=>0,'error'=>'неправильные входящие переменные!'];
}
}

View File

@@ -0,0 +1,145 @@
<?php
function http_code($code = NULL) {
if ($code !== NULL) {
switch ($code) {
case 100: $text = 'Continue'; break;
case 101: $text = 'Switching Protocols'; break;
case 200: $text = 'OK'; break;
case 201: $text = 'Created'; break;
case 202: $text = 'Accepted'; break;
case 203: $text = 'Non-Authoritative Information'; break;
case 204: $text = 'No Content'; break;
case 205: $text = 'Reset Content'; break;
case 206: $text = 'Partial Content'; break;
case 300: $text = 'Multiple Choices'; break;
case 301: $text = 'Moved Permanently'; break;
case 302: $text = 'Moved Temporarily'; break;
case 303: $text = 'See Other'; break;
case 304: $text = 'Not Modified'; break;
case 305: $text = 'Use Proxy'; break;
case 400: $text = 'Bad Request'; break;
case 401: $text = 'Unauthorized'; break;
case 402: $text = 'Payment Required'; break;
case 403: $text = 'Forbidden'; break;
case 404: $text = 'Not Found'; break;
case 405: $text = 'Method Not Allowed'; break;
case 406: $text = 'Not Acceptable'; break;
case 407: $text = 'Proxy Authentication Required'; break;
case 408: $text = 'Request Time-out'; break;
case 409: $text = 'Conflict'; break;
case 410: $text = 'Gone'; break;
case 411: $text = 'Length Required'; break;
case 412: $text = 'Precondition Failed'; break;
case 413: $text = 'Request Entity Too Large'; break;
case 414: $text = 'Request-URI Too Large'; break;
case 415: $text = 'Unsupported Media Type'; break;
case 500: $text = 'Internal Server Error'; break;
case 501: $text = 'Not Implemented'; break;
case 502: $text = 'Bad Gateway'; break;
case 503: $text = 'Service Unavailable'; break;
case 504: $text = 'Gateway Time-out'; break;
case 505: $text = 'HTTP Version not supported'; break;
default:
exit('Unknown http status code "' . htmlentities($code) . '"');
break;
}
$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
header($protocol . ' ' . $code . ' ' . $text);
$GLOBALS['http_response_code'] = $code;
} else {
$code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);
}
return $code;
}
function getQ_($url,$headers){
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
$output = curl_exec($ch);
curl_close($ch);
$ara=json_decode($output, true);
return $ara;
}
function getQ($url,$headers){
//return [$url];
// Инициализация сеанса cURL
$ch = curl_init();
// Установка URL
curl_setopt($ch, CURLOPT_URL, $url);
// Установка CURLOPT_RETURNTRANSFER (вернуть ответ в виде строки)
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// Выполнение запроса cURL
//$output содержит полученную строку
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
$output = curl_exec($ch);
// return $output;
$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
if(stristr($output,'Something went wrong')){
return [];//отправляем пустой массив
}
if($http_code>=400 and $http_code<500){
return [];//отправляем пустой массив
}
if($http_code>=500){
return $http_code;
}
$ara=json_decode($output, true);
// закрытие сеанса curl для освобождения системных ресурсов
curl_close($ch);
return $ara;
}
function getSSLPage($url,$post_data,$headers) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER,$headers );
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if(strlen(trim($post_data))>0){
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
}
curl_setopt ( $ch , CURLOPT_SSL_VERIFYPEER, 0 );
curl_setopt ( $ch , CURLOPT_SSL_VERIFYHOST, 0 );
//$http_code=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}

View File

@@ -0,0 +1,26 @@
<?php
// Используем для подключения к базе данных MySQL
class Database
{
// Учётные данные базы данных
private $host = "mysql:3306";
private $db_name = "authentication_jwt";
private $username = "tsduser";
private $password = "sdWs@a201!";
public $conn;
// Получаем соединение с базой данных
public function getConnection()
{
$this->conn = null;
try {
$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
} catch (PDOException $exception) {
echo "Ошибка соединения с БД: " . $exception->getMessage();
}
return $this->conn;
}
}

View File

@@ -0,0 +1,31 @@
<?php
// Используем для подключения к базе данных MySQL
class Database
{
// Учётные данные базы данных
//private $host = "localhost:3308";
private $host = "10.10.1.246";
public $db_name = "authentication_jwt";
//private $username = "tsd_user";
private $username = "tsd";
//private $password = "Dfrgvm3@1";
private $password = "gcIL6UrWFtSdDTbz";
public $conn;
// Получаем соединение с базой данных
public function getConnection()
{
$this->conn = null;
try {
$this->conn = new PDO("pgsql:host=".$this->host.";port=5432;dbname=".$this->db_name, $this->username, $this->password);
//$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name.";charset=UTF8", $this->username, $this->password);
} catch (PDOException $exception) {
echo "Ошибка соединения с БД: " . $exception->getMessage();
}
return $this->conn;
}
}

View File

@@ -0,0 +1,16 @@
<?php
// Показ сообщений об ошибках
error_reporting(E_ALL);
// Установим часовой пояс по умолчанию
date_default_timezone_set("Europe/Moscow");
// Переменные, используемые для JWT
$key = "d;rflgmd;lthkr;tlgd';tglldkfdfbkjldkaJNLKJDLKJDL2392093420lskfgj!!2";
$iss = "https://tsd.book-online.ru";
$aud = "https://tsd.book-online.ru";
//$iat = 1356999524;
//$nbf = 1357000000;
$iat = time();
$nbf = time();

View File

@@ -0,0 +1,157 @@
<?php
class User
{
// Подключение к БД таблице "users"
private $conn;
private $table_name = "users";
// Свойства
public $id;
public $firstname;
public $lastname;
public $email;
public $password;
// Конструктор класса User
public function __construct($db)
{
$this->conn = $db;
}
// Метод для создания нового пользователя
function create()
{
// Запрос для добавления нового пользователя в БД
$query = "INSERT INTO " . $this->table_name . "
SET
firstname = :firstname,
lastname = :lastname,
email = :email,
password = :password";
// Подготовка запроса
$stmt = $this->conn->prepare($query);
// Инъекция
$this->firstname = htmlspecialchars(strip_tags($this->firstname));
$this->lastname = htmlspecialchars(strip_tags($this->lastname));
$this->email = htmlspecialchars(strip_tags($this->email));
$this->password = htmlspecialchars(strip_tags($this->password));
// Привязываем значения
$stmt->bindParam(":firstname", $this->firstname);
$stmt->bindParam(":lastname", $this->lastname);
$stmt->bindParam(":email", $this->email);
// Для защиты пароля
// Хешируем пароль перед сохранением в базу данных
$password_hash = password_hash($this->password, PASSWORD_BCRYPT);
$stmt->bindParam(":password", $password_hash);
// Выполняем запрос
// Если выполнение успешно, то информация о пользователе будет сохранена в базе данных
if ($stmt->execute()) {
return true;
}
return false;
}
// Проверка, существует ли электронная почта в нашей базе данных
function emailExists() {
// Запрос, чтобы проверить, существует ли электронная почта
$query = "SELECT id, firstname, lastname, password
FROM " . $this->table_name . "
WHERE email = :email
limit 1
";
// Подготовка запроса
$stmt = $this->conn->prepare($query);
// Инъекция
$this->email=htmlspecialchars(strip_tags($this->email));
// Привязываем значение e-mail
// $stmt->bindParam("email", $this->email);
// Выполняем запрос
$stmt->execute(["email"=>$this->email]);
// Получаем количество строк
$num = $stmt->rowCount();
// Если электронная почта существует,
// Присвоим значения свойствам объекта для легкого доступа и использования для php сессий
if ($num > 0) {
// Получаем значения
$row = $stmt->fetch(PDO::FETCH_ASSOC);
// Присвоим значения свойствам объекта
$this->id = $row["id"];
$this->firstname = $row["firstname"];
$this->lastname = $row["lastname"];
$this->password = $row["password"];
// Вернём "true", потому что в базе данных существует электронная почта
return ['result'=>true];
}
// Вернём "false", если адрес электронной почты не существует в базе данных
return ['result'=>false,'error'=>$this->conn->errorInfo()];
}
// Здесь будет метод update()
// Обновить запись пользователя
public function update() {
// Если в HTML-форме был введен пароль (необходимо обновить пароль)
$password_set=!empty($this->password) ? ", password = :password" : "";
// Если не введен пароль - не обновлять пароль
$query = "UPDATE " . $this->table_name . "
SET
firstname = :firstname,
lastname = :lastname,
email = :email
{$password_set}
WHERE id = :id";
// Подготовка запроса
$stmt = $this->conn->prepare($query);
// Инъекция (очистка)
$this->firstname=htmlspecialchars(strip_tags($this->firstname));
$this->lastname=htmlspecialchars(strip_tags($this->lastname));
$this->email=htmlspecialchars(strip_tags($this->email));
// Привязываем значения с HTML формы
$stmt->bindParam(":firstname", $this->firstname);
$stmt->bindParam(":lastname", $this->lastname);
$stmt->bindParam(":email", $this->email);
// Метод password_hash () для защиты пароля пользователя в базе данных
if(!empty($this->password)){
$this->password=htmlspecialchars(strip_tags($this->password));
$password_hash = password_hash($this->password, PASSWORD_BCRYPT);
$stmt->bindParam(":password", $password_hash);
}
// Уникальный идентификатор записи для редактирования
$stmt->bindParam(":id", $this->id);
// Если выполнение успешно, то информация о пользователе будет сохранена в базе данных
if($stmt->execute()) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,68 @@
<?php
// Заголовки
header("Access-Control-Allow-Origin:*");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Подключение к БД
// Файлы, необходимые для подключения к базе данных
include_once __DIR__."/Config/Database.php";
//include_once __DIR__."/Config/DatabasePGSQL.php";
include_once __DIR__."/Objects/User.php";
// Получаем соединение с базой данных
$database = new Database();
$db = $database->getConnection();
// Создание объекта "User"
$user = new User($db);
// Отправляемые данные будут здесь
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
// Устанавливаем значения
$user->firstname = $data->firstname;
$user->lastname = $data->lastname;
$user->email = $data->email;
$user->password = $data->password;
// Поверка на существование e-mail в БД
// $email_exists = $user->emailExists();
// Здесь будет метод create()
// Создание пользователя
if (
!empty($user->firstname) &&
!empty($user->email) &&
// $email_exists == 0 &&
!empty($user->password) &&
$user->create()
) {
// Устанавливаем код ответа
http_response_code(200);
// Покажем сообщение о том, что пользователь был создан
echo json_encode(array("message" => "Пользователь был создан"));
}
// Сообщение, если не удаётся создать пользователя
else {
// Устанавливаем код ответа
http_response_code(400);
// Покажем сообщение о том, что создать пользователя не удалось
echo json_encode(array("message" => "Невозможно создать пользователя"));
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Firebase\JWT;
class BeforeValidException extends \UnexpectedValueException
{
}

View File

@@ -0,0 +1,258 @@
<?php
namespace Firebase\JWT;
use ArrayAccess;
use InvalidArgumentException;
use LogicException;
use OutOfBoundsException;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use RuntimeException;
use UnexpectedValueException;
/**
* @implements ArrayAccess<string, Key>
*/
class CachedKeySet implements ArrayAccess
{
/**
* @var string
*/
private $jwksUri;
/**
* @var ClientInterface
*/
private $httpClient;
/**
* @var RequestFactoryInterface
*/
private $httpFactory;
/**
* @var CacheItemPoolInterface
*/
private $cache;
/**
* @var ?int
*/
private $expiresAfter;
/**
* @var ?CacheItemInterface
*/
private $cacheItem;
/**
* @var array<string, array<mixed>>
*/
private $keySet;
/**
* @var string
*/
private $cacheKey;
/**
* @var string
*/
private $cacheKeyPrefix = 'jwks';
/**
* @var int
*/
private $maxKeyLength = 64;
/**
* @var bool
*/
private $rateLimit;
/**
* @var string
*/
private $rateLimitCacheKey;
/**
* @var int
*/
private $maxCallsPerMinute = 10;
/**
* @var string|null
*/
private $defaultAlg;
public function __construct(
string $jwksUri,
ClientInterface $httpClient,
RequestFactoryInterface $httpFactory,
CacheItemPoolInterface $cache,
int $expiresAfter = null,
bool $rateLimit = false,
string $defaultAlg = null
) {
$this->jwksUri = $jwksUri;
$this->httpClient = $httpClient;
$this->httpFactory = $httpFactory;
$this->cache = $cache;
$this->expiresAfter = $expiresAfter;
$this->rateLimit = $rateLimit;
$this->defaultAlg = $defaultAlg;
$this->setCacheKeys();
}
/**
* @param string $keyId
* @return Key
*/
public function offsetGet($keyId): Key
{
if (!$this->keyIdExists($keyId)) {
throw new OutOfBoundsException('Key ID not found');
}
return JWK::parseKey($this->keySet[$keyId], $this->defaultAlg);
}
/**
* @param string $keyId
* @return bool
*/
public function offsetExists($keyId): bool
{
return $this->keyIdExists($keyId);
}
/**
* @param string $offset
* @param Key $value
*/
public function offsetSet($offset, $value): void
{
throw new LogicException('Method not implemented');
}
/**
* @param string $offset
*/
public function offsetUnset($offset): void
{
throw new LogicException('Method not implemented');
}
/**
* @return array<mixed>
*/
private function formatJwksForCache(string $jwks): array
{
$jwks = json_decode($jwks, true);
if (!isset($jwks['keys'])) {
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
}
if (empty($jwks['keys'])) {
throw new InvalidArgumentException('JWK Set did not contain any keys');
}
$keys = [];
foreach ($jwks['keys'] as $k => $v) {
$kid = isset($v['kid']) ? $v['kid'] : $k;
$keys[(string) $kid] = $v;
}
return $keys;
}
private function keyIdExists(string $keyId): bool
{
if (null === $this->keySet) {
$item = $this->getCacheItem();
// Try to load keys from cache
if ($item->isHit()) {
// item found! retrieve it
$this->keySet = $item->get();
// If the cached item is a string, the JWKS response was cached (previous behavior).
// Parse this into expected format array<kid, jwk> instead.
if (\is_string($this->keySet)) {
$this->keySet = $this->formatJwksForCache($this->keySet);
}
}
}
if (!isset($this->keySet[$keyId])) {
if ($this->rateLimitExceeded()) {
return false;
}
$request = $this->httpFactory->createRequest('GET', $this->jwksUri);
$jwksResponse = $this->httpClient->sendRequest($request);
$this->keySet = $this->formatJwksForCache((string) $jwksResponse->getBody());
if (!isset($this->keySet[$keyId])) {
return false;
}
$item = $this->getCacheItem();
$item->set($this->keySet);
if ($this->expiresAfter) {
$item->expiresAfter($this->expiresAfter);
}
$this->cache->save($item);
}
return true;
}
private function rateLimitExceeded(): bool
{
if (!$this->rateLimit) {
return false;
}
$cacheItem = $this->cache->getItem($this->rateLimitCacheKey);
if (!$cacheItem->isHit()) {
$cacheItem->expiresAfter(1); // # of calls are cached each minute
}
$callsPerMinute = (int) $cacheItem->get();
if (++$callsPerMinute > $this->maxCallsPerMinute) {
return true;
}
$cacheItem->set($callsPerMinute);
$this->cache->save($cacheItem);
return false;
}
private function getCacheItem(): CacheItemInterface
{
if (\is_null($this->cacheItem)) {
$this->cacheItem = $this->cache->getItem($this->cacheKey);
}
return $this->cacheItem;
}
private function setCacheKeys(): void
{
if (empty($this->jwksUri)) {
throw new RuntimeException('JWKS URI is empty');
}
// ensure we do not have illegal characters
$key = preg_replace('|[^a-zA-Z0-9_\.!]|', '', $this->jwksUri);
// add prefix
$key = $this->cacheKeyPrefix . $key;
// Hash keys if they exceed $maxKeyLength of 64
if (\strlen($key) > $this->maxKeyLength) {
$key = substr(hash('sha256', $key), 0, $this->maxKeyLength);
}
$this->cacheKey = $key;
if ($this->rateLimit) {
// add prefix
$rateLimitKey = $this->cacheKeyPrefix . 'ratelimit' . $key;
// Hash keys if they exceed $maxKeyLength of 64
if (\strlen($rateLimitKey) > $this->maxKeyLength) {
$rateLimitKey = substr(hash('sha256', $rateLimitKey), 0, $this->maxKeyLength);
}
$this->rateLimitCacheKey = $rateLimitKey;
}
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Firebase\JWT;
class ExpiredException extends \UnexpectedValueException
{
}

View File

@@ -0,0 +1,323 @@
<?php
namespace Firebase\JWT;
use DomainException;
use InvalidArgumentException;
use UnexpectedValueException;
/**
* JSON Web Key implementation, based on this spec:
* https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
*
* PHP version 5
*
* @category Authentication
* @package Authentication_JWT
* @author Bui Sy Nguyen <nguyenbs@gmail.com>
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
* @link https://github.com/firebase/php-jwt
*/
class JWK
{
private const OID = '1.2.840.10045.2.1';
private const ASN1_OBJECT_IDENTIFIER = 0x06;
private const ASN1_SEQUENCE = 0x10; // also defined in JWT
private const ASN1_BIT_STRING = 0x03;
private const EC_CURVES = [
'P-256' => '1.2.840.10045.3.1.7', // Len: 64
'secp256k1' => '1.3.132.0.10', // Len: 64
// 'P-384' => '1.3.132.0.34', // Len: 96 (not yet supported)
// 'P-521' => '1.3.132.0.35', // Len: 132 (not supported)
];
/**
* Parse a set of JWK keys
*
* @param array<mixed> $jwks The JSON Web Key Set as an associative array
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
* JSON Web Key Set
*
* @return array<string, Key> An associative array of key IDs (kid) to Key objects
*
* @throws InvalidArgumentException Provided JWK Set is empty
* @throws UnexpectedValueException Provided JWK Set was invalid
* @throws DomainException OpenSSL failure
*
* @uses parseKey
*/
public static function parseKeySet(array $jwks, string $defaultAlg = null): array
{
$keys = [];
if (!isset($jwks['keys'])) {
throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
}
if (empty($jwks['keys'])) {
throw new InvalidArgumentException('JWK Set did not contain any keys');
}
foreach ($jwks['keys'] as $k => $v) {
$kid = isset($v['kid']) ? $v['kid'] : $k;
if ($key = self::parseKey($v, $defaultAlg)) {
$keys[(string) $kid] = $key;
}
}
if (0 === \count($keys)) {
throw new UnexpectedValueException('No supported algorithms found in JWK Set');
}
return $keys;
}
/**
* Parse a JWK key
*
* @param array<mixed> $jwk An individual JWK
* @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the
* JSON Web Key Set
*
* @return Key The key object for the JWK
*
* @throws InvalidArgumentException Provided JWK is empty
* @throws UnexpectedValueException Provided JWK was invalid
* @throws DomainException OpenSSL failure
*
* @uses createPemFromModulusAndExponent
*/
public static function parseKey(array $jwk, string $defaultAlg = null): ?Key
{
if (empty($jwk)) {
throw new InvalidArgumentException('JWK must not be empty');
}
if (!isset($jwk['kty'])) {
throw new UnexpectedValueException('JWK must contain a "kty" parameter');
}
if (!isset($jwk['alg'])) {
if (\is_null($defaultAlg)) {
// The "alg" parameter is optional in a KTY, but an algorithm is required
// for parsing in this library. Use the $defaultAlg parameter when parsing the
// key set in order to prevent this error.
// @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4
throw new UnexpectedValueException('JWK must contain an "alg" parameter');
}
$jwk['alg'] = $defaultAlg;
}
switch ($jwk['kty']) {
case 'RSA':
if (!empty($jwk['d'])) {
throw new UnexpectedValueException('RSA private keys are not supported');
}
if (!isset($jwk['n']) || !isset($jwk['e'])) {
throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
}
$pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
$publicKey = \openssl_pkey_get_public($pem);
if (false === $publicKey) {
throw new DomainException(
'OpenSSL error: ' . \openssl_error_string()
);
}
return new Key($publicKey, $jwk['alg']);
case 'EC':
if (isset($jwk['d'])) {
// The key is actually a private key
throw new UnexpectedValueException('Key data must be for a public key');
}
if (empty($jwk['crv'])) {
throw new UnexpectedValueException('crv not set');
}
if (!isset(self::EC_CURVES[$jwk['crv']])) {
throw new DomainException('Unrecognised or unsupported EC curve');
}
if (empty($jwk['x']) || empty($jwk['y'])) {
throw new UnexpectedValueException('x and y not set');
}
$publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']);
return new Key($publicKey, $jwk['alg']);
default:
// Currently only RSA is supported
break;
}
return null;
}
/**
* Converts the EC JWK values to pem format.
*
* @param string $crv The EC curve (only P-256 is supported)
* @param string $x The EC x-coordinate
* @param string $y The EC y-coordinate
*
* @return string
*/
private static function createPemFromCrvAndXYCoordinates(string $crv, string $x, string $y): string
{
$pem =
self::encodeDER(
self::ASN1_SEQUENCE,
self::encodeDER(
self::ASN1_SEQUENCE,
self::encodeDER(
self::ASN1_OBJECT_IDENTIFIER,
self::encodeOID(self::OID)
)
. self::encodeDER(
self::ASN1_OBJECT_IDENTIFIER,
self::encodeOID(self::EC_CURVES[$crv])
)
) .
self::encodeDER(
self::ASN1_BIT_STRING,
\chr(0x00) . \chr(0x04)
. JWT::urlsafeB64Decode($x)
. JWT::urlsafeB64Decode($y)
)
);
return sprintf(
"-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
wordwrap(base64_encode($pem), 64, "\n", true)
);
}
/**
* Create a public key represented in PEM format from RSA modulus and exponent information
*
* @param string $n The RSA modulus encoded in Base64
* @param string $e The RSA exponent encoded in Base64
*
* @return string The RSA public key represented in PEM format
*
* @uses encodeLength
*/
private static function createPemFromModulusAndExponent(
string $n,
string $e
): string {
$mod = JWT::urlsafeB64Decode($n);
$exp = JWT::urlsafeB64Decode($e);
$modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod);
$publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp);
$rsaPublicKey = \pack(
'Ca*a*a*',
48,
self::encodeLength(\strlen($modulus) + \strlen($publicExponent)),
$modulus,
$publicExponent
);
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
$rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
$rsaPublicKey = \chr(0) . $rsaPublicKey;
$rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
$rsaPublicKey = \pack(
'Ca*a*',
48,
self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
$rsaOID . $rsaPublicKey
);
return "-----BEGIN PUBLIC KEY-----\r\n" .
\chunk_split(\base64_encode($rsaPublicKey), 64) .
'-----END PUBLIC KEY-----';
}
/**
* DER-encode the length
*
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
*
* @param int $length
* @return string
*/
private static function encodeLength(int $length): string
{
if ($length <= 0x7F) {
return \chr($length);
}
$temp = \ltrim(\pack('N', $length), \chr(0));
return \pack('Ca*', 0x80 | \strlen($temp), $temp);
}
/**
* Encodes a value into a DER object.
* Also defined in Firebase\JWT\JWT
*
* @param int $type DER tag
* @param string $value the value to encode
* @return string the encoded object
*/
private static function encodeDER(int $type, string $value): string
{
$tag_header = 0;
if ($type === self::ASN1_SEQUENCE) {
$tag_header |= 0x20;
}
// Type
$der = \chr($tag_header | $type);
// Length
$der .= \chr(\strlen($value));
return $der . $value;
}
/**
* Encodes a string into a DER-encoded OID.
*
* @param string $oid the OID string
* @return string the binary DER-encoded OID
*/
private static function encodeOID(string $oid): string
{
$octets = explode('.', $oid);
// Get the first octet
$first = (int) array_shift($octets);
$second = (int) array_shift($octets);
$oid = \chr($first * 40 + $second);
// Iterate over subsequent octets
foreach ($octets as $octet) {
if ($octet == 0) {
$oid .= \chr(0x00);
continue;
}
$bin = '';
while ($octet) {
$bin .= \chr(0x80 | ($octet & 0x7f));
$octet >>= 7;
}
$bin[0] = $bin[0] & \chr(0x7f);
// Convert to big endian if necessary
if (pack('V', 65534) == pack('L', 65534)) {
$oid .= strrev($bin);
} else {
$oid .= $bin;
}
}
return $oid;
}
}

View File

@@ -0,0 +1,638 @@
<?php
namespace Firebase\JWT;
use ArrayAccess;
use DateTime;
use DomainException;
use Exception;
use InvalidArgumentException;
use OpenSSLAsymmetricKey;
use OpenSSLCertificate;
use stdClass;
use UnexpectedValueException;
/**
* JSON Web Token implementation, based on this spec:
* https://tools.ietf.org/html/rfc7519
*
* PHP version 5
*
* @category Authentication
* @package Authentication_JWT
* @author Neuman Vong <neuman@twilio.com>
* @author Anant Narayanan <anant@php.net>
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
* @link https://github.com/firebase/php-jwt
*/
class JWT
{
private const ASN1_INTEGER = 0x02;
private const ASN1_SEQUENCE = 0x10;
private const ASN1_BIT_STRING = 0x03;
/**
* When checking nbf, iat or expiration times,
* we want to provide some extra leeway time to
* account for clock skew.
*
* @var int
*/
public static $leeway = 0;
/**
* Allow the current timestamp to be specified.
* Useful for fixing a value within unit testing.
* Will default to PHP time() value if null.
*
* @var ?int
*/
public static $timestamp = null;
/**
* @var array<string, string[]>
*/
public static $supported_algs = [
'ES384' => ['openssl', 'SHA384'],
'ES256' => ['openssl', 'SHA256'],
'ES256K' => ['openssl', 'SHA256'],
'HS256' => ['hash_hmac', 'SHA256'],
'HS384' => ['hash_hmac', 'SHA384'],
'HS512' => ['hash_hmac', 'SHA512'],
'RS256' => ['openssl', 'SHA256'],
'RS384' => ['openssl', 'SHA384'],
'RS512' => ['openssl', 'SHA512'],
'EdDSA' => ['sodium_crypto', 'EdDSA'],
];
/**
* Decodes a JWT string into a PHP object.
*
* @param string $jwt The JWT
* @param Key|array<string,Key> $keyOrKeyArray The Key or associative array of key IDs (kid) to Key objects.
* If the algorithm used is asymmetric, this is the public key
* Each Key object contains an algorithm and matching key.
* Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
* 'HS512', 'RS256', 'RS384', and 'RS512'
*
* @return stdClass The JWT's payload as a PHP object
*
* @throws InvalidArgumentException Provided key/key-array was empty or malformed
* @throws DomainException Provided JWT is malformed
* @throws UnexpectedValueException Provided JWT was invalid
* @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
* @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
* @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'
* @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim
*
* @uses jsonDecode
* @uses urlsafeB64Decode
*/
public static function decode(
string $jwt,
$keyOrKeyArray
): stdClass {
// Validate JWT
$timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
if (empty($keyOrKeyArray)) {
throw new InvalidArgumentException('Key may not be empty');
}
$tks = \explode('.', $jwt);
if (\count($tks) !== 3) {
throw new UnexpectedValueException('Wrong number of segments');
}
list($headb64, $bodyb64, $cryptob64) = $tks;
$headerRaw = static::urlsafeB64Decode($headb64);
if (null === ($header = static::jsonDecode($headerRaw))) {
throw new UnexpectedValueException('Invalid header encoding');
}
$payloadRaw = static::urlsafeB64Decode($bodyb64);
if (null === ($payload = static::jsonDecode($payloadRaw))) {
throw new UnexpectedValueException('Invalid claims encoding');
}
if (\is_array($payload)) {
// prevent PHP Fatal Error in edge-cases when payload is empty array
$payload = (object) $payload;
}
if (!$payload instanceof stdClass) {
throw new UnexpectedValueException('Payload must be a JSON object');
}
$sig = static::urlsafeB64Decode($cryptob64);
if (empty($header->alg)) {
throw new UnexpectedValueException('Empty algorithm');
}
if (empty(static::$supported_algs[$header->alg])) {
throw new UnexpectedValueException('Algorithm not supported');
}
$key = self::getKey($keyOrKeyArray, property_exists($header, 'kid') ? $header->kid : null);
// Check the algorithm
if (!self::constantTimeEquals($key->getAlgorithm(), $header->alg)) {
// See issue #351
throw new UnexpectedValueException('Incorrect key for this algorithm');
}
if (\in_array($header->alg, ['ES256', 'ES256K', 'ES384'], true)) {
// OpenSSL expects an ASN.1 DER sequence for ES256/ES256K/ES384 signatures
$sig = self::signatureToDER($sig);
}
if (!self::verify("{$headb64}.{$bodyb64}", $sig, $key->getKeyMaterial(), $header->alg)) {
throw new SignatureInvalidException('Signature verification failed');
}
// Check the nbf if it is defined. This is the time that the
// token can actually be used. If it's not yet that time, abort.
if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
throw new BeforeValidException(
'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf)
);
}
// Check that this token has been created before 'now'. This prevents
// using tokens that have been created for later use (and haven't
// correctly used the nbf claim).
if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
throw new BeforeValidException(
'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat)
);
}
// Check if this token has expired.
if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
throw new ExpiredException('Expired token');
}
return $payload;
}
/**
* Converts and signs a PHP array into a JWT string.
*
* @param array<mixed> $payload PHP array
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
* @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
* @param string $keyId
* @param array<string, string> $head An array with header elements to attach
*
* @return string A signed JWT
*
* @uses jsonEncode
* @uses urlsafeB64Encode
*/
public static function encode(
array $payload,
$key,
string $alg,
string $keyId = null,
array $head = null
): string {
$header = ['typ' => 'JWT', 'alg' => $alg];
if ($keyId !== null) {
$header['kid'] = $keyId;
}
if (isset($head) && \is_array($head)) {
$header = \array_merge($head, $header);
}
$segments = [];
$segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header));
$segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload));
$signing_input = \implode('.', $segments);
$signature = static::sign($signing_input, $key, $alg);
$segments[] = static::urlsafeB64Encode($signature);
return \implode('.', $segments);
}
/**
* Sign a string with a given key and algorithm.
*
* @param string $msg The message to sign
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
* @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
*
* @return string An encrypted message
*
* @throws DomainException Unsupported algorithm or bad key was specified
*/
public static function sign(
string $msg,
$key,
string $alg
): string {
if (empty(static::$supported_algs[$alg])) {
throw new DomainException('Algorithm not supported');
}
list($function, $algorithm) = static::$supported_algs[$alg];
switch ($function) {
case 'hash_hmac':
if (!\is_string($key)) {
throw new InvalidArgumentException('key must be a string when using hmac');
}
return \hash_hmac($algorithm, $msg, $key, true);
case 'openssl':
$signature = '';
$success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line
if (!$success) {
throw new DomainException('OpenSSL unable to sign data');
}
if ($alg === 'ES256' || $alg === 'ES256K') {
$signature = self::signatureFromDER($signature, 256);
} elseif ($alg === 'ES384') {
$signature = self::signatureFromDER($signature, 384);
}
return $signature;
case 'sodium_crypto':
if (!\function_exists('sodium_crypto_sign_detached')) {
throw new DomainException('libsodium is not available');
}
if (!\is_string($key)) {
throw new InvalidArgumentException('key must be a string when using EdDSA');
}
try {
// The last non-empty line is used as the key.
$lines = array_filter(explode("\n", $key));
$key = base64_decode((string) end($lines));
if (\strlen($key) === 0) {
throw new DomainException('Key cannot be empty string');
}
return sodium_crypto_sign_detached($msg, $key);
} catch (Exception $e) {
throw new DomainException($e->getMessage(), 0, $e);
}
}
throw new DomainException('Algorithm not supported');
}
/**
* Verify a signature with the message, key and method. Not all methods
* are symmetric, so we must have a separate verify and sign method.
*
* @param string $msg The original message (header and body)
* @param string $signature The original signature
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
* @param string $alg The algorithm
*
* @return bool
*
* @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure
*/
private static function verify(
string $msg,
string $signature,
$keyMaterial,
string $alg
): bool {
if (empty(static::$supported_algs[$alg])) {
throw new DomainException('Algorithm not supported');
}
list($function, $algorithm) = static::$supported_algs[$alg];
switch ($function) {
case 'openssl':
$success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); // @phpstan-ignore-line
if ($success === 1) {
return true;
}
if ($success === 0) {
return false;
}
// returns 1 on success, 0 on failure, -1 on error.
throw new DomainException(
'OpenSSL error: ' . \openssl_error_string()
);
case 'sodium_crypto':
if (!\function_exists('sodium_crypto_sign_verify_detached')) {
throw new DomainException('libsodium is not available');
}
if (!\is_string($keyMaterial)) {
throw new InvalidArgumentException('key must be a string when using EdDSA');
}
try {
// The last non-empty line is used as the key.
$lines = array_filter(explode("\n", $keyMaterial));
$key = base64_decode((string) end($lines));
if (\strlen($key) === 0) {
throw new DomainException('Key cannot be empty string');
}
if (\strlen($signature) === 0) {
throw new DomainException('Signature cannot be empty string');
}
return sodium_crypto_sign_verify_detached($signature, $msg, $key);
} catch (Exception $e) {
throw new DomainException($e->getMessage(), 0, $e);
}
case 'hash_hmac':
default:
if (!\is_string($keyMaterial)) {
throw new InvalidArgumentException('key must be a string when using hmac');
}
$hash = \hash_hmac($algorithm, $msg, $keyMaterial, true);
return self::constantTimeEquals($hash, $signature);
}
}
/**
* Decode a JSON string into a PHP object.
*
* @param string $input JSON string
*
* @return mixed The decoded JSON string
*
* @throws DomainException Provided string was invalid JSON
*/
public static function jsonDecode(string $input)
{
$obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
if ($errno = \json_last_error()) {
self::handleJsonError($errno);
} elseif ($obj === null && $input !== 'null') {
throw new DomainException('Null result with non-null input');
}
return $obj;
}
/**
* Encode a PHP array into a JSON string.
*
* @param array<mixed> $input A PHP array
*
* @return string JSON representation of the PHP array
*
* @throws DomainException Provided object could not be encoded to valid JSON
*/
public static function jsonEncode(array $input): string
{
if (PHP_VERSION_ID >= 50400) {
$json = \json_encode($input, \JSON_UNESCAPED_SLASHES);
} else {
// PHP 5.3 only
$json = \json_encode($input);
}
if ($errno = \json_last_error()) {
self::handleJsonError($errno);
} elseif ($json === 'null' && $input !== null) {
throw new DomainException('Null result with non-null input');
}
if ($json === false) {
throw new DomainException('Provided object could not be encoded to valid JSON');
}
return $json;
}
/**
* Decode a string with URL-safe Base64.
*
* @param string $input A Base64 encoded string
*
* @return string A decoded string
*
* @throws InvalidArgumentException invalid base64 characters
*/
public static function urlsafeB64Decode(string $input): string
{
$remainder = \strlen($input) % 4;
if ($remainder) {
$padlen = 4 - $remainder;
$input .= \str_repeat('=', $padlen);
}
return \base64_decode(\strtr($input, '-_', '+/'));
}
/**
* Encode a string with URL-safe Base64.
*
* @param string $input The string you want encoded
*
* @return string The base64 encode of what you passed in
*/
public static function urlsafeB64Encode(string $input): string
{
return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
}
/**
* Determine if an algorithm has been provided for each Key
*
* @param Key|ArrayAccess<string,Key>|array<string,Key> $keyOrKeyArray
* @param string|null $kid
*
* @throws UnexpectedValueException
*
* @return Key
*/
private static function getKey(
$keyOrKeyArray,
?string $kid
): Key {
if ($keyOrKeyArray instanceof Key) {
return $keyOrKeyArray;
}
if (empty($kid)) {
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
}
if ($keyOrKeyArray instanceof CachedKeySet) {
// Skip "isset" check, as this will automatically refresh if not set
return $keyOrKeyArray[$kid];
}
if (!isset($keyOrKeyArray[$kid])) {
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
}
return $keyOrKeyArray[$kid];
}
/**
* @param string $left The string of known length to compare against
* @param string $right The user-supplied string
* @return bool
*/
public static function constantTimeEquals(string $left, string $right): bool
{
if (\function_exists('hash_equals')) {
return \hash_equals($left, $right);
}
$len = \min(self::safeStrlen($left), self::safeStrlen($right));
$status = 0;
for ($i = 0; $i < $len; $i++) {
$status |= (\ord($left[$i]) ^ \ord($right[$i]));
}
$status |= (self::safeStrlen($left) ^ self::safeStrlen($right));
return ($status === 0);
}
/**
* Helper method to create a JSON error.
*
* @param int $errno An error number from json_last_error()
*
* @throws DomainException
*
* @return void
*/
private static function handleJsonError(int $errno): void
{
$messages = [
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
];
throw new DomainException(
isset($messages[$errno])
? $messages[$errno]
: 'Unknown JSON error: ' . $errno
);
}
/**
* Get the number of bytes in cryptographic strings.
*
* @param string $str
*
* @return int
*/
private static function safeStrlen(string $str): int
{
if (\function_exists('mb_strlen')) {
return \mb_strlen($str, '8bit');
}
return \strlen($str);
}
/**
* Convert an ECDSA signature to an ASN.1 DER sequence
*
* @param string $sig The ECDSA signature to convert
* @return string The encoded DER object
*/
private static function signatureToDER(string $sig): string
{
// Separate the signature into r-value and s-value
$length = max(1, (int) (\strlen($sig) / 2));
list($r, $s) = \str_split($sig, $length);
// Trim leading zeros
$r = \ltrim($r, "\x00");
$s = \ltrim($s, "\x00");
// Convert r-value and s-value from unsigned big-endian integers to
// signed two's complement
if (\ord($r[0]) > 0x7f) {
$r = "\x00" . $r;
}
if (\ord($s[0]) > 0x7f) {
$s = "\x00" . $s;
}
return self::encodeDER(
self::ASN1_SEQUENCE,
self::encodeDER(self::ASN1_INTEGER, $r) .
self::encodeDER(self::ASN1_INTEGER, $s)
);
}
/**
* Encodes a value into a DER object.
*
* @param int $type DER tag
* @param string $value the value to encode
*
* @return string the encoded object
*/
private static function encodeDER(int $type, string $value): string
{
$tag_header = 0;
if ($type === self::ASN1_SEQUENCE) {
$tag_header |= 0x20;
}
// Type
$der = \chr($tag_header | $type);
// Length
$der .= \chr(\strlen($value));
return $der . $value;
}
/**
* Encodes signature from a DER object.
*
* @param string $der binary signature in DER format
* @param int $keySize the number of bits in the key
*
* @return string the signature
*/
private static function signatureFromDER(string $der, int $keySize): string
{
// OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
list($offset, $_) = self::readDER($der);
list($offset, $r) = self::readDER($der, $offset);
list($offset, $s) = self::readDER($der, $offset);
// Convert r-value and s-value from signed two's compliment to unsigned
// big-endian integers
$r = \ltrim($r, "\x00");
$s = \ltrim($s, "\x00");
// Pad out r and s so that they are $keySize bits long
$r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
$s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
return $r . $s;
}
/**
* Reads binary DER-encoded data and decodes into a single object
*
* @param string $der the binary data in DER format
* @param int $offset the offset of the data stream containing the object
* to decode
*
* @return array{int, string|null} the new offset and the decoded object
*/
private static function readDER(string $der, int $offset = 0): array
{
$pos = $offset;
$size = \strlen($der);
$constructed = (\ord($der[$pos]) >> 5) & 0x01;
$type = \ord($der[$pos++]) & 0x1f;
// Length
$len = \ord($der[$pos++]);
if ($len & 0x80) {
$n = $len & 0x1f;
$len = 0;
while ($n-- && $pos < $size) {
$len = ($len << 8) | \ord($der[$pos++]);
}
}
// Value
if ($type === self::ASN1_BIT_STRING) {
$pos++; // Skip the first contents octet (padding indicator)
$data = \substr($der, $pos, $len - 1);
$pos += $len - 1;
} elseif (!$constructed) {
$data = \substr($der, $pos, $len);
$pos += $len;
} else {
$data = null;
}
return [$pos, $data];
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Firebase\JWT;
use InvalidArgumentException;
use OpenSSLAsymmetricKey;
use OpenSSLCertificate;
use TypeError;
class Key
{
/** @var string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate */
private $keyMaterial;
/** @var string */
private $algorithm;
/**
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
* @param string $algorithm
*/
public function __construct(
$keyMaterial,
string $algorithm
) {
if (
!\is_string($keyMaterial)
&& !$keyMaterial instanceof OpenSSLAsymmetricKey
&& !$keyMaterial instanceof OpenSSLCertificate
&& !\is_resource($keyMaterial)
) {
throw new TypeError('Key material must be a string, resource, or OpenSSLAsymmetricKey');
}
if (empty($keyMaterial)) {
throw new InvalidArgumentException('Key material must not be empty');
}
if (empty($algorithm)) {
throw new InvalidArgumentException('Algorithm must not be empty');
}
// TODO: Remove in PHP 8.0 in favor of class constructor property promotion
$this->keyMaterial = $keyMaterial;
$this->algorithm = $algorithm;
}
/**
* Return the algorithm valid for this key
*
* @return string
*/
public function getAlgorithm(): string
{
return $this->algorithm;
}
/**
* @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate
*/
public function getKeyMaterial()
{
return $this->keyMaterial;
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Firebase\JWT;
class SignatureInvalidException extends \UnexpectedValueException
{
}

View File

@@ -0,0 +1,78 @@
<?php
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Здесь будет соединение с БД
// Файлы необходимые для соединения с БД
//include_once __DIR__."/Config/DatabasePGSQL.php";
include_once __DIR__."/Config/Database.php";
//include_once __DIR__."/Config/DatabasePGSQL.php";
include_once __DIR__."/Objects/User.php";
// Получаем соединение с базой данных
$database = new Database();
$db = $database->getConnection();
// Создание объекта "User"
$user = new User($db);
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
// Устанавливаем значения
$user->email = $data->email;
$email_exists = $user->emailExists();
// Файлы для JWT будут здесь
// Подключение файлов JWT
include_once __DIR__."/Config/core.php";
include_once __DIR__."/libs/php-jwt/BeforeValidException.php";
include_once __DIR__."/libs/php-jwt/ExpiredException.php";
include_once __DIR__."/libs/php-jwt/SignatureInvalidException.php";
include_once __DIR__."/libs/php-jwt/JWT.php";
use \Firebase\JWT\JWT;
// Существует ли электронная почта и соответствует ли пароль тому, что находится в базе данных
if ($email_exists['result'] && password_verify($data->password, $user->password)) {
$token = array(
"iss" => $iss,
"aud" => $aud,
"iat" => $iat,
"nbf" => $nbf,
"data" => array(
"id" => $user->id,
"firstname" => $user->firstname,
"lastname" => $user->lastname,
"email" => $user->email
)
);
// Код ответа
http_response_code(200);
// Создание jwt
$jwt = JWT::encode($token, $key, 'HS256');
echo json_encode(
array(
"message" => "Успешный вход в систему",
"jwt" => $jwt
)
);
}
// Если электронная почта не существует или пароль не совпадает,
// Сообщим пользователю, что он не может войти в систему
else {
// Код ответа
// http_response_code(401);
// Скажем пользователю что войти не удалось
echo json_encode(array("message" => "Ошибка входа","error"=>$email_exists['error']));
}
?>

View File

@@ -0,0 +1,124 @@
<?php
// Заголовки
header("Access-Control-Allow-Origin:*");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Требуется для кодирования веб-токена JSON
include_once "Config/core.php";
include_once "libs/php-jwt/BeforeValidException.php";
include_once "libs/php-jwt/ExpiredException.php";
include_once "libs/php-jwt/SignatureInvalidException.php";
include_once "libs/php-jwt/JWT.php";
include_once "libs/php-jwt/Key.php";
use \Firebase\JWT\JWT;
use \Firebase\JWT\Key;
// Файлы, необходимые для подключения к базе данных
include_once "Config/Database.php";
//include_once __DIR__."/Config/DatabasePGSQL.php";
include_once "Objects/User.php";
// Получаем соединение с БД
$database = new Database();
$db = $database->getConnection();
// Создание объекта "User"
$user = new User($db);
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
// Получаем jwt
$jwt = isset($data->jwt) ? $data->jwt : "";
// Если JWT не пуст
if ($jwt) {
// Если декодирование выполнено успешно, показать данные пользователя
try {
// Декодирование jwt
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
// Нам нужно установить отправленные данные (через форму HTML) в свойствах объекта пользователя
$user->firstname = $data->firstname;
$user->lastname = $data->lastname;
$user->email = $data->email;
$user->password = $data->password;
$user->id = $decoded->data->id;
// Создание пользователя
if ($user->update()) {
// сгенерировать заново JWT здесь --->
// Нам нужно заново сгенерировать JWT, потому что данные пользователя могут отличаться
$token = array(
"iss" => $iss,
"aud" => $aud,
"iat" => $iat,
"nbf" => $nbf,
"data" => array(
"id" => $user->id,
"firstname" => $user->firstname,
"lastname" => $user->lastname,
"email" => $user->email
)
);
$jwt = JWT::encode($token, $key, 'HS256');
// Код ответа
http_response_code(200);
// Ответ в формате JSON
echo json_encode(
array(
"message" => "Пользователь был обновлён",
"jwt" => $jwt
)
);
//<-----
}
// Сообщение, если не удается обновить пользователя
else {
// Код ответа
http_response_code(401);
// Показать сообщение об ошибке
echo json_encode(array("message" => "Невозможно обновить пользователя"));
}
}
// Если декодирование не удалось, это означает, что JWT является недействительным
catch (Exception $e) {
// Код ответа
http_response_code(401);
// Сообщение об ошибке
echo json_encode(array(
"message" => "Доступ закрыт",
"error" => $e->getMessage()
));
}
}
// Показать сообщение об ошибке, если jwt пуст
else {
// Код ответа
http_response_code(401);
// Сообщить пользователю что доступ запрещен
echo json_encode(array("message" => "Доступ закрыт"));
}

View File

@@ -0,0 +1,69 @@
<?php
// Заголовки
header("Access-Control-Allow-Origin:*");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Требуется для декодирования JWT
include_once "Config/core.php";
include_once "libs/php-jwt/BeforeValidException.php";
include_once "libs/php-jwt/ExpiredException.php";
include_once "libs/php-jwt/SignatureInvalidException.php";
include_once "libs/php-jwt/JWT.php";
include_once "libs/php-jwt/Key.php";
use \Firebase\JWT\JWT;
use \Firebase\JWT\Key;
// Получаем значение веб-токена JSON
$data = json_decode(file_get_contents("php://input"));
// Получаем JWT
$jwt = isset($data->jwt) ? $data->jwt : "";
// Если JWT не пуст
if ($jwt) {
// Если декодирование выполнено успешно, показать данные пользователя
try {
// Декодирование jwt
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
// Код ответа
http_response_code(200);
// Покажем детали
echo json_encode(array(
"result"=>1,
"message" => "Доступ разрешен",
"data" => $decoded->data
));
}
// Если декодирование не удалось, это означает, что JWT является недействительным
catch (Exception $e) {
// Код ответа
http_response_code(401);
// Сообщим пользователю что ему отказано в доступе и покажем сообщение об ошибке
echo json_encode(array(
"result"=>0,
"message" => "Close ".$jwt,
"error" => $e->getMessage()
));
}
}
// Покажем сообщение об ошибке, если JWT пуст
else {
// Код ответа
// http_response_code(401);
// Сообщим пользователю что доступ запрещен
echo json_encode(array("message" => "Access denied"));
}

View File

@@ -0,0 +1,32 @@
<?php
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Получаем данные
$data = json_decode(file_get_contents("php://input"),true);
include_once "Class/function.php";
include_once "Class/Database.php";
//include_once "Class/DatabasePGSQL.php";
include_once "Class/MainClass.php";
$database = new Database();
//$database = new Database();//PGSQL
$db = $database->getConnection();
$main_class = new MainClass($db);
$result=$main_class->DeleteCollect($data);
echo json_encode($result);

View File

@@ -0,0 +1,30 @@
<?php
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
include_once "Class/function.php";
include_once "Class/Database.php";
//include_once "Class/DatabasePGSQL.php";
include_once "Class/MainClass.php";
$arr= json_decode($data -> data,true);
$database = new Database();
//$database = new Database();//PGSQL
$db = $database->getConnection();
$main_class = new MainClass($db);
$result=$main_class->DataCollect($arr);
echo json_encode($result);

View File

@@ -0,0 +1,3 @@
<?php
echo "OOOOOO";
?>

View File

@@ -0,0 +1,32 @@
<?php
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
include_once "Class/function.php";
include_once "Class/Database.php";
//include_once "Class/DatabasePGSQL.php";
include_once "Class/MainClass.php";
$arr= json_decode($data -> data,true);
$database = new Database();
//$database = new Database();//PGSQL
$db = $database->getConnection();
$main_class = new MainClass($db);
$result=$main_class->PostingStatus($arr);
echo json_encode($result);

View File

@@ -0,0 +1,29 @@
<?php
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
include_once "Class/function.php";
include_once "Class/Database.php";
//include_once "Class/DatabasePGSQL.php";
include_once "Class/MainClass.php";
$arr= json_decode($data -> data,true);
$database = new Database();
//$database = new Database();//PGSQL
$db = $database->getConnection();
$main_class = new MainClass($db);
$result=$main_class->pushCollect($arr);
$result['data_collect']=$main_class->DataCollect($arr); //результаты сканирования актов
echo json_encode($result);

View File

@@ -0,0 +1,29 @@
<?php
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
include_once "Class/function.php";
include_once "Class/Database.php";
//include_once "Class/DatabasePGSQL.php";
include_once "Class/MainClass.php";
$arr= json_decode($data -> data,true);
$database = new Database();
//$database = new Database();//PGSQL
$db = $database->getConnection();
$main_class = new MainClass($db);
$result=$main_class->History($arr);
echo json_encode($result);

View File

@@ -0,0 +1,107 @@
<?php
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
include_once "Class/function.php";
$arr= json_decode($data -> data,true);
$kag=$arr['kag'];
$param_api=$arr['kag']['param_api'];
$url='https://marketplace.book-online.ru/api/shipment_barcodes/';
//переворачиваем дату для яндекса
$date_query_strtotime=strtotime($arr['date_query']);
$date_query_dmy=date('d-m-Y',$date_query_strtotime);
if($kag['firma']==0){
$post_data=json_encode(['date'=> $date_query_dmy,'kag'=>$kag['kag']]);
}else{
$post_data=json_encode(['date'=> $arr['date_query'],'kag'=>$kag['kag']]);
}
$headers = Array("Authorization: fi9tzpIHgp84E1Pf45rMzR/hy8lA2zE5", 'Content-Type: "application/json"');
$data_json=getSSLPage($url,$post_data,$headers);
if(is_numeric($data_json) and $data_json>=400){
http_code($data_json);
}
include_once "Class/Database.php";
//include_once "Class/DatabasePGSQL.php";
include_once "Class/MainClass.php";
$database = new Database();
//$database = new Database();//PGSQL
$db = $database->getConnection();
$user=0;
$param=[
'date_query' => $arr['date_query'],
'data_json' => $data_json,
'user' => $user,
'kag'=> $arr['kag']
];
$data=json_decode($data_json,true);
$main_class = new MainClass($db);
//$main_class->WrFile($param);
if($data['error'] and strlen($data['error'])){
$result=null;
}else{
$result=$main_class->wrDatabase($param);
//передается несколько актов
for($i=0;$i<count(data);$i++){
$param1=[
'idoper'=>$data[$i]['id'],
'kag'=> $arr['kag'],
];
//очень важно------------------>
$data[$i]['idoper']=$data[$i]['id'];
$data[$i]['id']=$result['result'][$data[$i]['id']];//id (в нем содержится idoper) меняется на id строки
$data[$i]['data_collect']=$main_class->DataCollect($param1); //результаты сканирования актов
$data[$i]['date_query']=$arr['date_query'];
$data[$i]['date_q']=$date_query_dmy;
$data[$i]['data_json']=['barcodes'=>$data[$i]['barcodes'],'id'=>$data[$i]['idoper']];
//unset($data[$i]['barcodes']);
//<------------------очень важно
}
}
if(!$result['result']){
$data_=json_encode(['data_json'=>$data,'server_error'=>$data['error'],'post_data'=>$post_data,'result'=>0,'error'=>$result['error'], 'id'=>0,'date_query'=>$arr['date_query'],'data_collect'=>[],'method'=>$url,'param'=>$param]);
}else{
$data_=json_encode(['data_json'=>$data,'server_error'=>$data['error'],'post_data'=>$post_data,'result'=>1,'error'=>$result['error'],'id'=>$result['ids'],'idoper'=>$result['idoper'], 'date_query'=>$arr['date_query'],'data_collect'=>$data_collect,'method'=>$url,'param'=>$param]);
}
echo $data_;

View File

@@ -0,0 +1,4 @@
<?php
// Получаем данные
echo phpinfo();

View File

@@ -0,0 +1,33 @@
<?php
error_reporting(0);
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
include_once "Class/function.php";
include_once "Class/Database.php";
//include_once "Class/DatabasePGSQL.php";
include_once "Class/MainClass.php";
$arr= json_decode($data -> data,true);
$database = new Database();
//$database = new Database();//PGSQL
$db = $database->getConnection();
$main_class = new MainClass($db);
$result=$main_class->PostingStatus($arr);
echo json_encode($result);

View File

@@ -0,0 +1,30 @@
<?php
error_reporting(E_ERROR);
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
include_once "Class/function.php";
include_once "Class/Database.php";
//include_once "Class/DatabasePGSQL.php";
include_once "Class/MainClass.php";
$arr= json_decode($data -> data,true);
$database = new Database();
//$database = new Database();//PGSQL
$db = $database->getConnection();
$main_class = new MainClass($db);
$result=$main_class->pushCollect($arr);
$result['data_collect']=$main_class->DataCollect($arr); //результаты сканирования актов
echo json_encode($result);

View File

@@ -0,0 +1,30 @@
<?php
error_reporting(0);
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
include_once "Class/function.php";
include_once "Class/Database.php";
//include_once "Class/DatabasePGSQL.php";
include_once "Class/MainClass.php";
$arr= json_decode($data -> data,true);
$database = new Database();
//$database = new Database();//PGSQL
$db = $database->getConnection();
$main_class = new MainClass($db);
$result=$main_class->History($arr);
echo json_encode($result);

View File

@@ -0,0 +1,157 @@
<?php
error_reporting(0);
header('Access-Control-Allow-Origin:*');
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
function checkJSON($string) {
$result = [
'is_valid' => false,
'type' => null,
'error' => null
];
$decoded = json_decode($string);
if (json_last_error() === JSON_ERROR_NONE) {
$result['is_valid'] = true;
$result['type'] = gettype($decoded);
} else {
$result['error'] = json_last_error_msg();
}
return $result;
}
try {
// Получаем данные
$data = json_decode(file_get_contents("php://input"));
include_once "Class/function.php";
$arr= json_decode($data -> data,true);
$kag=$arr['kag'];
$param_api=$arr['kag']['param_api'];
$url='https://marketplace.book-online.ru/api/shipment_barcodes/';
//переворачиваем дату для яндекса
$date_query_strtotime=strtotime($arr['date_query']);
$date_query_dmy=date('d-m-Y',$date_query_strtotime);
if($kag['firma']==0){
$post_data=json_encode(['date'=> $date_query_dmy,'kag'=>$kag['kag']]);
}else{
$post_data=json_encode(['date'=> $arr['date_query'],'kag'=>$kag['kag']]);
}
$headers = Array("Authorization: fi9tzpIHgp84E1Pf45rMzR/hy8lA2zE5", 'Content-Type: "application/json"');
$data_json=getSSLPage($url,$post_data,$headers);
//echo $data_json;
$check = checkJSON($data_json);
if (!$check['is_valid']) {
$data_=json_encode(['data_json'=>'','error'=>$data_json,'post_data'=>$post_data,'result'=>1,]);
echo $data_;
die('');
}
if(is_numeric($data_json) and $data_json>=400){
http_code($data_json);
}
include_once "Class/Database.php";
//include_once "Class/DatabasePGSQL.php";
include_once "Class/MainClass.php";
$database = new Database();
//$database = new Database();//PGSQL
$db = $database->getConnection();
$user=0;
$param=[
'date_query' => $arr['date_query'],
'data_json' => $data_json,
'user' => $user,
'kag'=> $arr['kag']
];
$data=json_decode($data_json,true);
$main_class = new MainClass($db);
//$main_class->WrFile($param);
if(isset($data['error']) and strlen($data['error'])){
$result=null;
}else{
$result=$main_class->wrDatabase($param);
//передается несколько актов
for($i=0;$i<count($data);$i++){
$param1=[
'idoper'=>$data[$i]['id'],
'kag'=> $arr['kag'],
];
//очень важно------------------>
$data[$i]['idoper']=$data[$i]['id'];
$data[$i]['id']=$result['result'][$data[$i]['id']];//id (в нем содержится idoper) меняется на id строки
$data[$i]['data_collect']=$main_class->DataCollect($param1); //результаты сканирования актов
$data[$i]['date_query']=$arr['date_query'];
$data[$i]['date_q']=$date_query_dmy;
$data[$i]['data_json']=['barcodes'=>$data[$i]['barcodes'],'id'=>$data[$i]['idoper']];
//unset($data[$i]['barcodes']);
//<------------------очень важно
}
}
if(!$result['result']){
$data_=json_encode(['data_json'=>$data,'server_error'=>$data['error'],'post_data'=>$post_data,'result'=>0, 'id'=>0,'date_query'=>$arr['date_query'],'data_collect'=>[],'method'=>$url,'param'=>$param]);
}else{
$data_=json_encode(['data_json'=>$data,'server_error'=>$data['error'],'post_data'=>$post_data,'result'=>1,'id'=>$result['ids'],'idoper'=>$result['idoper'], 'date_query'=>$arr['date_query'],'data_collect'=>$data_collect,'method'=>$url,'param'=>$param]);
}
echo $data_;
}catch (Exception $err){
$data_=json_encode(['data_json'=>'','server_error'=>$err,'post_data'=>'','result'=>1,]);
echo $data_;
}

4
www/first.loc/api/t1.php Normal file
View File

@@ -0,0 +1,4 @@
<?php
// Получаем данные
echo phpinfo();

View File

@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

2
www/first.loc/build.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
vue build

View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

10509
www/first.loc/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
{
"name": "tsd",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"axios": "^1.5.1",
"core-js": "^3.8.3",
"ua-parser-js": "^1.0.36",
"vue": "^3.2.13",
"vue-router": "^4.0.3",
"vue-sse": "^2.5.2",
"vuex": "^4.0.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

219
www/first.loc/src/App.vue Normal file
View File

@@ -0,0 +1,219 @@
<template>
<div v-if="load">
<div v-if="auth" class="content">
<nav>
<router-link to="/">Главная</router-link>
<div class="a_link" @click="OutLogin">Выход</div>
</nav>
<router-view/>
</div>
<authorization v-else></authorization>
</div>
<div v-else>
</div>
</template>
<script>
import {mapState,mapActions,mapMutations} from 'vuex';
import Authorization from "./components/Authorization.vue";
export default {
components: {Authorization},
data() {
return {
load:false,
info:'',
}
},
computed: {
...mapState({
auth: state=>state.ApiClass.auth,
url_api: state=>state.ApiClass.url_api,
}),
},
methods:{
...mapMutations({
setAuth:'ApiClass/setAuth',
setUser:'ApiClass/setUser',
}),
...mapActions({
loadData:'ApiClass/loadData',
}),
OutLogin(){
this.setAuth(false);
this.setUser({});
localStorage.setItem('token', '');
},
validateToken(){
//console.log(localStorage.token);
if(typeof localStorage.token!=='undefined' && localStorage.token!=''){
this.loadData({
method:'post',
url:this.url_api+'/api/authentication-jwt/validate_token.php',
json:{
jwt:localStorage.token,
},
}).then(
(r)=>{
//console.log(r)
if(typeof r.result!=='undefined' && r.result===1){
this.info='';
this.setAuth(true);
this.setUser(r.data);
}else{
if(typeof r.error!=='undefined'){
this.info=r.error;
}
this.setAuth(false);
this.setUser({});
}
}
).catch(
(e)=>{
if(typeof e.error!=='undefined'){
this.info=e.error;
}
this.setAuth(false);
this.setUser({});
}
).finally(
()=>{
this.load=true;
}
)
;
}else{
this.load=true;
}
},
},
mounted(){
this.validateToken();
}
}
</script>
<style>
@import './assets/fonts/fonts.css';
@import './assets/main.css';
*{
margin:0;
padding: 0;
font-family: Inter;
}
#app {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background: #f8f8f8;
color: #2c3e50;
}
.content{
max-width: 800px;
height: 100vh;
margin: auto;
background: #ffffff;
padding: 0px 10px;
}
nav {
margin-bottom: 15px;
display: flex;
flex-direction: row;
gap:20px;
justify-content: end;
align-items: center;
height: 30px;
}
nav a {
font-weight: bold;
color: #364d64;
}
nav a.router-link-exact-active {
color: #5f88b0;
}
.error_downloads{
display: flex;
flex-direction: column;
gap: 10px;
padding: 20px;
background: #d0b2b2;
border-radius: 8px;
color: #501717;
}
.a_link{
text-decoration: underline;
font-weight: bold;
color: #364d64;
cursor: pointer;
}
.a_link:hover{
color: #09151f !important;
}
</style>

View File

@@ -0,0 +1,6 @@
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]

View File

@@ -0,0 +1,93 @@
Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -0,0 +1,72 @@
Inter Variable Font
===================
This download contains Inter as both a variable font and static fonts.
Inter is a variable font with these axes:
slnt
wght
This means all the styles are contained in a single file:
Inter-VariableFont_slnt,wght.ttf
If your app fully supports variable fonts, you can now pick intermediate styles
that arent available as static fonts. Not all apps support variable fonts, and
in those cases you can use the static font files for Inter:
static/Inter-Thin.ttf
static/Inter-ExtraLight.ttf
static/Inter-Light.ttf
static/Inter-Regular.ttf
static/Inter-Medium.ttf
static/Inter-SemiBold.ttf
static/Inter-Bold.ttf
static/Inter-ExtraBold.ttf
static/Inter-Black.ttf
Get started
-----------
1. Install the font files you want to use
2. Use your app's font picker to view the font family and all the
available styles
Learn more about variable fonts
-------------------------------
https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
https://variablefonts.typenetwork.com
https://medium.com/variable-fonts
In desktop apps
https://theblog.adobe.com/can-variable-fonts-illustrator-cc
https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
Online
https://developers.google.com/fonts/docs/getting_started
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
Installing fonts
MacOS: https://support.apple.com/en-us/HT201749
Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
Android Apps
https://developers.google.com/fonts/docs/android
https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
License
-------
Please read the full license text (OFL.txt) to understand the permissions,
restrictions and requirements for usage, redistribution, and modification.
You can use them in your products & projects print or digital,
commercial or otherwise.
This isn't legal advice, please consider consulting a lawyer and see the full
license for all details.

Some files were not shown because too many files have changed in this diff Show More