You've already forked OpenIntegrations
mirror of
https://github.com/Bayselonarrend/OpenIntegrations.git
synced 2025-11-23 22:05:15 +02:00
Функция создания токена для Service аккаунта Google
This commit is contained in:
BIN
data.json.gpg
BIN
data.json.gpg
Binary file not shown.
BIN
docs/docusaurus/static/img/Docs/GoogleCalendar/13.png
vendored
Normal file
BIN
docs/docusaurus/static/img/Docs/GoogleCalendar/13.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
BIN
docs/docusaurus/static/img/Docs/GoogleCalendar/14.png
vendored
Normal file
BIN
docs/docusaurus/static/img/Docs/GoogleCalendar/14.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
BIN
docs/docusaurus/static/img/Docs/GoogleCalendar/15.png
vendored
Normal file
BIN
docs/docusaurus/static/img/Docs/GoogleCalendar/15.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
docs/docusaurus/static/img/Docs/GoogleCalendar/16.png
vendored
Normal file
BIN
docs/docusaurus/static/img/Docs/GoogleCalendar/16.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
BIN
docs/docusaurus/static/img/Docs/GoogleCalendar/17.png
vendored
Normal file
BIN
docs/docusaurus/static/img/Docs/GoogleCalendar/17.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -16,7 +16,7 @@ opt-level = "z"
|
||||
[dependencies]
|
||||
addin1c = "0.5.0"
|
||||
hmac = "0.12"
|
||||
sha1 = "0.10"
|
||||
sha2 = "0.10"
|
||||
sha1 = { version = "0.10", features = ["oid"] }
|
||||
sha2 = { version = "0.10", features = ["oid"] }
|
||||
rsa = "0.9"
|
||||
digest = "0.10.7"
|
||||
@@ -1,9 +1,9 @@
|
||||
"MAIN ---"
|
||||
linux-vdso.so.1 (0x00007ffd2bba8000)
|
||||
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f8e3f32c000)
|
||||
libc.so.6 => /lib64/libc.so.6 (0x00007f8e3ef55000)
|
||||
libdl.so.2 => /lib64/libdl.so.2 (0x00007f8e3ed51000)
|
||||
/lib64/ld-linux-x86-64.so.2 (0x00007f8e3f54c000)
|
||||
linux-vdso.so.1 (0x00007fff64dcd000)
|
||||
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f075273c000)
|
||||
libc.so.6 => /lib64/libc.so.6 (0x00007f0752365000)
|
||||
libdl.so.2 => /lib64/libdl.so.2 (0x00007f0752161000)
|
||||
/lib64/ld-linux-x86-64.so.2 (0x00007f075295c000)
|
||||
GLIBC_2.2.5
|
||||
GLIBC_2.3
|
||||
GLIBC_2.14
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha1::Sha1;
|
||||
use sha2::{Sha256};
|
||||
use rsa::{RsaPrivateKey, pkcs1::DecodeRsaPrivateKey};
|
||||
use rsa::pkcs1v15::SigningKey;
|
||||
use sha2::Sha256;
|
||||
use rsa::{RsaPrivateKey, pkcs1::DecodeRsaPrivateKey, pkcs8::DecodePrivateKey};
|
||||
use rsa::pkcs1v15::{Pkcs1v15Sign};
|
||||
use rsa::signature::digest::{Digest, FixedOutput};
|
||||
use rsa::signature::{Signer, SignatureEncoding};
|
||||
|
||||
// ===== HMAC ===== //
|
||||
|
||||
@@ -30,44 +29,48 @@ pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Result<Vec<u8>, String> {
|
||||
|
||||
// ===== RSA ===== //
|
||||
|
||||
fn load_rsa_key(key: &[u8]) -> Result<RsaPrivateKey, String> {
|
||||
pub fn load_rsa_key(key_data: &[u8]) -> Result<RsaPrivateKey, String> {
|
||||
if let Ok(key) = RsaPrivateKey::from_pkcs1_der(key_data) {
|
||||
return Ok(key);
|
||||
}
|
||||
|
||||
match RsaPrivateKey::from_pkcs1_der(key){
|
||||
Ok(v) => Ok(v),
|
||||
Err(_) => {
|
||||
if let Ok(key) = RsaPrivateKey::from_pkcs8_der(key_data) {
|
||||
return Ok(key);
|
||||
}
|
||||
|
||||
let pem_string = match std::str::from_utf8(key){
|
||||
Ok(pem_str) => pem_str,
|
||||
Err(_) => return Err("Invalid RSA key format".to_string()),
|
||||
let pem_str = match std::str::from_utf8(key_data) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return Err("Invalid key format: not DER or PEM".to_string()),
|
||||
};
|
||||
|
||||
match RsaPrivateKey::from_pkcs1_pem(pem_string){
|
||||
Ok(v) => Ok(v),
|
||||
Err(_) => Err("Invalid RSA key format".to_string()),
|
||||
if let Ok(key) = RsaPrivateKey::from_pkcs1_pem(pem_str) {
|
||||
return Ok(key);
|
||||
}
|
||||
|
||||
if let Ok(key) = RsaPrivateKey::from_pkcs8_pem(pem_str) {
|
||||
return Ok(key);
|
||||
}
|
||||
}
|
||||
|
||||
Err("Invalid RSA key format. Expected: PKCS#1/8 PEM or DER".to_string())
|
||||
}
|
||||
|
||||
fn rsa_sign<D>(key: &RsaPrivateKey, data: &[u8]) -> Result<Vec<u8>, String>
|
||||
where
|
||||
D: Digest + FixedOutput,
|
||||
SigningKey<D>: From<RsaPrivateKey>,
|
||||
D: Digest + FixedOutput + rsa::pkcs8::AssociatedOid,
|
||||
{
|
||||
let signing_key = SigningKey::<D>::new_unprefixed(key.clone());
|
||||
// Создаем схему подписи с указанием хеш-алгоритма
|
||||
let scheme = Pkcs1v15Sign::new::<D>();
|
||||
|
||||
// Хешируем данные
|
||||
let mut hasher = D::new();
|
||||
Digest::update(&mut hasher, data);
|
||||
let digest = hasher.finalize();
|
||||
|
||||
let sign_result = signing_key.try_sign(&digest);
|
||||
// Подписываем хеш
|
||||
let signature = key.sign(scheme, &digest).map_err(|e| format!("RSA signing error: {}", e))?;
|
||||
|
||||
match sign_result {
|
||||
Ok(signature) => Ok(signature.to_vec()),
|
||||
Err(e) => Err(format!("RSA signing error: {}", e)),
|
||||
Ok(signature)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Подписывает данные с помощью RSA-SHA1
|
||||
pub fn rsa_sha1(key: &[u8], data: &[u8]) -> Result<Vec<u8>, String> {
|
||||
|
||||
BIN
src/en/OInt/addins/OPI_Cryptography.zip
vendored
BIN
src/en/OInt/addins/OPI_Cryptography.zip
vendored
Binary file not shown.
Binary file not shown.
BIN
src/ru/OInt/addins/OPI_Cryptography.zip
vendored
BIN
src/ru/OInt/addins/OPI_Cryptography.zip
vendored
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
// OneScript: ./OInt/core/Modules/OPI_GoogleWorkspace.os
|
||||
// OneScript: ./OInt/core/Modules/OPI_GoogleWorkspace.os
|
||||
// Lib: Google Workspace
|
||||
// CLI: google
|
||||
|
||||
@@ -136,6 +136,73 @@
|
||||
|
||||
КонецФункции
|
||||
|
||||
// Получить токен service аккаунта
|
||||
// Получает токен авторизации по данным service аккаунта
|
||||
//
|
||||
// Примечание:
|
||||
// Список доступных областей действия: [developers.google.com](https://developers.google.com/identity/protocols/oauth2/scopes)
|
||||
//
|
||||
// Параметры:
|
||||
// Данные - Произвольный - JSON данные авторизации как файл, коллекция или двоичные данные - auth
|
||||
// ОбластиДействия - Массив Из Строка - Область действия (scope) или массив областей - scope
|
||||
// ВремяЖизни - Число - Время жизни токена в секундах - exp
|
||||
//
|
||||
// Возвращаемое значение:
|
||||
// Соответствие Из КлючИЗначение - сериализованный JSON ответа от Google
|
||||
Функция ПолучитьТокенServiceАккаунта(Знач Данные, Знач ОбластиДействия, Знач ВремяЖизни = 3600) Экспорт
|
||||
|
||||
ТекстОшибки = "Переданные данные service аккаунта не являются валидным JSON";
|
||||
OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(Данные, ТекстОшибки);
|
||||
OPI_ПреобразованиеТипов.ПолучитьЧисло(ВремяЖизни);
|
||||
OPI_ПреобразованиеТипов.ПолучитьМассив(ОбластиДействия);
|
||||
|
||||
МассивОбязательныхПолей = Новый Массив;
|
||||
МассивОбязательныхПолей.Добавить("token_uri");
|
||||
МассивОбязательныхПолей.Добавить("client_email");
|
||||
МассивОбязательныхПолей.Добавить("private_key");
|
||||
МассивОбязательныхПолей.Добавить("private_key_id");
|
||||
|
||||
ОтсутствующиеПоля = OPI_Инструменты.НайтиОтсутствующиеПоляКоллекции(Данные, МассивОбязательныхПолей);
|
||||
|
||||
Если ЗначениеЗаполнено(ОтсутствующиеПоля) Тогда
|
||||
|
||||
ШаблонОшибкиПолей = "В данных service аккаунта отсутствуют обязательные поля: %1";
|
||||
ТекстОшибкиПолей = СтрШаблон(ШаблонОшибкиПолей, СтрСоединить(ОтсутствующиеПоля, ", "));
|
||||
ВызватьИсключение ТекстОшибкиПолей;
|
||||
|
||||
КонецЕсли;
|
||||
|
||||
ОбластиДействияСтрокой = СтрСоединить(ОбластиДействия, " ");
|
||||
КлючПодписи = Данные["private_key"];
|
||||
URL = Данные["token_uri"];
|
||||
|
||||
ТекущаяДата = ТекущаяУниверсальнаяДата();
|
||||
ДатаСгорания = ТекущаяДата + ВремяЖизни;
|
||||
|
||||
UnixTime = OPI_Инструменты.UnixTime(ТекущаяДата);
|
||||
ExpTime = OPI_Инструменты.UnixTime(ДатаСгорания);
|
||||
|
||||
Payload = Новый Структура;
|
||||
|
||||
Payload.Вставить("iss" , Данные["client_email"]);
|
||||
Payload.Вставить("scope", ОбластиДействияСтрокой);
|
||||
Payload.Вставить("aud" , URL);
|
||||
Payload.Вставить("exp" , Число(ExpTime));
|
||||
Payload.Вставить("iat" , Число(UnixTime));
|
||||
|
||||
ДопЗаголовки = Новый Структура("kid", Данные["private_key_id"]);
|
||||
|
||||
JWT = OPI_Криптография.JWT(Payload, КлючПодписи, "RS256", ДопЗаголовки);
|
||||
|
||||
Грант = "urn:ietf:params:oauth:grant-type:jwt-bearer";
|
||||
СтруктураТела = Новый Структура("grant_type,assertion", Грант, JWT);
|
||||
|
||||
Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, СтруктураТела, , Ложь);
|
||||
|
||||
Возврат Ответ;
|
||||
|
||||
КонецФункции
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
#Область СлужебныйПрограммныйИнтерфейс
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// OneScript: ./OInt/tools/Modules/internal/Modules/OPI_Криптография.os
|
||||
// OneScript: ./OInt/tools/Modules/internal/Modules/OPI_Криптография.os
|
||||
|
||||
// MIT License
|
||||
|
||||
@@ -76,8 +76,62 @@
|
||||
|
||||
КонецФункции
|
||||
|
||||
Функция JWT(Знач Payload, Знач КлючПодписи, Знач Метод, Знач ДопЗаголовки = "") Экспорт
|
||||
|
||||
#Область БСП
|
||||
OPI_ПреобразованиеТипов.ПолучитьСтроку(Метод);
|
||||
OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(Payload);
|
||||
OPI_ПреобразованиеТипов.ПолучитьДвоичныеДанные(КлючПодписи, Истина, Ложь);
|
||||
|
||||
Метод = вРег(Метод);
|
||||
|
||||
Если Метод = "HS256" Тогда
|
||||
|
||||
Алгоритм = "HMAC";
|
||||
ФункцияХеша = "SHA256";
|
||||
|
||||
ИначеЕсли Метод = "RS256" Тогда
|
||||
|
||||
Алгоритм = "RSA";
|
||||
ФункцияХеша = "SHA256";
|
||||
|
||||
Иначе
|
||||
ВызватьИсключение "JWT: Неподдерживаемый метод";
|
||||
КонецЕсли;
|
||||
|
||||
Заголовки = Новый Структура("alg,typ", Метод, "JWT");
|
||||
|
||||
Если ЗначениеЗаполнено(ДопЗаголовки) Тогда
|
||||
|
||||
OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(ДопЗаголовки);
|
||||
|
||||
Для Каждого КлючЗначение Из ДопЗаголовки Цикл
|
||||
Заголовки.Вставить(КлючЗначение.Ключ, КлючЗначение.Значение);
|
||||
КонецЦикла;
|
||||
|
||||
КонецЕсли;
|
||||
|
||||
PayloadСтрокой = OPI_Инструменты.JSONСтрокой(Payload, , Ложь);
|
||||
ЗаголовкиСтркой = OPI_Инструменты.JSONСтрокой(Заголовки, , Ложь);
|
||||
|
||||
PayloadДвоичные = ПолучитьДвоичныеДанныеИзСтроки(PayloadСтрокой);
|
||||
ЗаголовкиДвоичные = ПолучитьДвоичныеДанныеИзСтроки(ЗаголовкиСтркой);
|
||||
|
||||
PayloadBase64 = Base64UrlEncode(PayloadДвоичные);
|
||||
ЗаголовкиBase64 = Base64UrlEncode(ЗаголовкиДвоичные);
|
||||
|
||||
Токен = СтрШаблон("%1.%2", ЗаголовкиBase64, PayloadBase64);
|
||||
ТокенДвоичные = ПолучитьДвоичныеДанныеИзСтроки(Токен);
|
||||
|
||||
Подпись = СоздатьПодпись(КлючПодписи, ТокенДвоичные, Алгоритм, ФункцияХеша);
|
||||
ПодписьBase64 = Base64UrlEncode(Подпись);
|
||||
|
||||
Токен = СтрШаблон("%1.%2", Токен, ПодписьBase64);
|
||||
|
||||
Возврат Токен;
|
||||
|
||||
КонецФункции
|
||||
|
||||
#Область Заимстованные
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (c) 2019, ООО 1С-Софт
|
||||
@@ -148,6 +202,41 @@
|
||||
|
||||
КонецФункции
|
||||
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2017 Vasily Pintov
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// https://github.com/pintov/1c-jwt
|
||||
|
||||
Функция Base64UrlEncode(Знач Значение)
|
||||
|
||||
Вывод = Base64Строка(Значение);
|
||||
Вывод = СтрРазделить(Вывод, "=")[0];
|
||||
Вывод = СтрЗаменить(Вывод, Символы.ВК + Символы.ПС, "");
|
||||
Вывод = СтрЗаменить(Вывод, "+", "-");
|
||||
Вывод = СтрЗаменить(Вывод, "/", "_");
|
||||
Возврат Вывод;
|
||||
|
||||
КонецФункции
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
@@ -841,10 +841,13 @@
|
||||
OPI_ПолучениеДанныхТестов.ПараметрВКоллекцию("Google_ClientSecret", ПараметрыТеста);
|
||||
OPI_ПолучениеДанныхТестов.ПараметрВКоллекцию("Google_Code" , ПараметрыТеста);
|
||||
OPI_ПолучениеДанныхТестов.ПараметрВКоллекцию("Google_Refresh" , ПараметрыТеста);
|
||||
OPI_ПолучениеДанныхТестов.ПараметрВКоллекцию("Google_ServiceData" , ПараметрыТеста);
|
||||
OPI_ПолучениеДанныхТестов.ПараметрВКоллекцию("Access_Token" , ПараметрыТеста);
|
||||
|
||||
GoogleWorkspace_СформироватьСсылкуПолученияКода(ПараметрыТеста);
|
||||
GoogleWorkspace_ПолучитьТокенПоКоду(ПараметрыТеста);
|
||||
GoogleWorkspace_ОбновитьТокен(ПараметрыТеста);
|
||||
GoogleWorkspace_ПолучитьТокенServiceАккаунта(ПараметрыТеста);
|
||||
|
||||
КонецПроцедуры
|
||||
|
||||
@@ -5416,13 +5419,38 @@
|
||||
// END
|
||||
|
||||
OPI_ПолучениеДанныхТестов.Проверка_ГуглТокен(Результат);
|
||||
|
||||
OPI_ПолучениеДанныхТестов.ЗаписатьПараметр("Google_Token", Результат["access_token"]);
|
||||
|
||||
OPI_Инструменты.Пауза(5);
|
||||
|
||||
КонецПроцедуры
|
||||
|
||||
Процедура GoogleWorkspace_ПолучитьТокенServiceАккаунта(ПараметрыФункции)
|
||||
|
||||
Данные = ПараметрыФункции["Google_ServiceData"]; // URL, двоичные данные, файл или коллекция
|
||||
|
||||
Токен = ПараметрыФункции["Access_Token"]; // SKIP
|
||||
Данные = OPI_ЗапросыHTTP // SKIP
|
||||
.НовыйЗапрос() // SKIP
|
||||
.Инициализировать(Данные) // SKIP
|
||||
.ДобавитьBearerАвторизацию(Токен) // SKIP
|
||||
.ОбработатьЗапрос("GET") // SKIP
|
||||
.ВернутьОтветКакДвоичныеДанные(); // SKIP
|
||||
|
||||
ОбластиДействия = Новый Массив;
|
||||
ОбластиДействия.Добавить("https://www.googleapis.com/auth/calendar");
|
||||
ОбластиДействия.Добавить("https://www.googleapis.com/auth/drive");
|
||||
ОбластиДействия.Добавить("https://www.googleapis.com/auth/spreadsheets");
|
||||
|
||||
Результат = OPI_GoogleWorkspace.ПолучитьТокенServiceАккаунта(Данные, ОбластиДействия);
|
||||
|
||||
// END
|
||||
|
||||
OPI_ПолучениеДанныхТестов.Проверка_ГуглТокен(Результат);
|
||||
OPI_ПолучениеДанныхТестов.ЗаписатьПараметр("Google_ServiceToken", Результат["access_token"]);
|
||||
|
||||
КонецПроцедуры
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
#Область GoogleCalendar
|
||||
|
||||
Binary file not shown.
@@ -1104,6 +1104,7 @@
|
||||
ДобавитьЛог("ОбработатьЗапрос: перенос тела в объект HTTPЗапроса");
|
||||
Если УстановитьТелоЗапроса().Ошибка Тогда Возврат ЭтотОбъект; КонецЕсли;
|
||||
|
||||
ГарантироватьТелоКоллекцию();
|
||||
ДополнитьЗаголовки();
|
||||
|
||||
Если ВыполнитьСразу Тогда
|
||||
@@ -2430,18 +2431,6 @@
|
||||
ТаблицаПараметров.Колонки.Добавить("Ключ");
|
||||
ТаблицаПараметров.Колонки.Добавить("Значение");
|
||||
|
||||
Если Не ЗначениеЗаполнено(ЗапросТелоКоллекция)
|
||||
Или Не OPI_Инструменты.ЭтоКоллекция(ЗапросТелоКоллекция, Истина) Тогда
|
||||
|
||||
Попытка
|
||||
ЗапросТелоКоллекция = ЗапросТело;
|
||||
OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(ЗапросТелоКоллекция);
|
||||
Исключение
|
||||
ЗапросТелоКоллекция = Новый Структура;
|
||||
КонецПопытки;
|
||||
|
||||
КонецЕсли;
|
||||
|
||||
Если ПолучитьНастройку("MultipartВOAuth") Или Не Multipart Тогда
|
||||
Для Каждого Поле Из ЗапросТелоКоллекция Цикл
|
||||
|
||||
@@ -2630,6 +2619,24 @@
|
||||
|
||||
КонецПроцедуры
|
||||
|
||||
Процедура ГарантироватьТелоКоллекцию()
|
||||
|
||||
Если Не ЗначениеЗаполнено(ЗапросТелоКоллекция)
|
||||
Или Не OPI_Инструменты.ЭтоКоллекция(ЗапросТелоКоллекция, Истина) Тогда
|
||||
|
||||
Попытка
|
||||
ЗапросТелоКоллекция = ЗапросТело;
|
||||
OPI_ПреобразованиеТипов.ПолучитьКоллекциюКлючИЗначение(ЗапросТелоКоллекция);
|
||||
Исключение
|
||||
ЗапросТелоКоллекция = Новый Структура;
|
||||
КонецПопытки;
|
||||
|
||||
ЗапросТелоКоллекция = ?(ЗначениеЗаполнено(ЗапросТелоКоллекция), ЗапросТелоКоллекция, Новый Структура);
|
||||
|
||||
КонецЕсли;
|
||||
|
||||
КонецПроцедуры
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
Reference in New Issue
Block a user