Merge branch 'develop' into feature/integration-system

This commit is contained in:
Ilya Oleshko 2022-06-20 13:27:01 +03:00
commit b22e3da0dc
3255 changed files with 362924 additions and 34480 deletions

View File

@ -79,8 +79,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.CRM", "products\ASC.CRM
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Mail", "products\ASC.Mail\Server\ASC.Mail.csproj", "{137CA67B-D0F5-4746-B8BC-1888D2859B90}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Radicale", "common\services\ASC.Radicale\ASC.Radicale.csproj", "{74998718-3C9A-4A89-B834-14453762C60F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Web.HealthChecks.UI", "web\ASC.Web.HealthChecks.UI\ASC.Web.HealthChecks.UI.csproj", "{0C1A387E-0CD0-4BE8-82FC-9FCAD05BF289}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.ClearEvents", "common\services\ASC.ClearEvents\ASC.ClearEvents.csproj", "{448221A8-EABA-4200-9192-E08BF241A487}"
@ -103,6 +101,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.EventBus.RabbitMQ", "co
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.EventBus.Extensions.Logger", "common\ASC.EventBus.Extensions.Logger\ASC.EventBus.Extensions.Logger.csproj", "{ED8CEB38-7C95-43A8-B208-9C9828654AC1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Migration", "common\ASC.Migration\ASC.Migration.csproj", "{05B8FF27-446B-49BF-B508-4A4C096D2BB2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -245,10 +245,6 @@ Global
{137CA67B-D0F5-4746-B8BC-1888D2859B90}.Debug|Any CPU.Build.0 = Debug|Any CPU
{137CA67B-D0F5-4746-B8BC-1888D2859B90}.Release|Any CPU.ActiveCfg = Release|Any CPU
{137CA67B-D0F5-4746-B8BC-1888D2859B90}.Release|Any CPU.Build.0 = Release|Any CPU
{74998718-3C9A-4A89-B834-14453762C60F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{74998718-3C9A-4A89-B834-14453762C60F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{74998718-3C9A-4A89-B834-14453762C60F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{74998718-3C9A-4A89-B834-14453762C60F}.Release|Any CPU.Build.0 = Release|Any CPU
{0C1A387E-0CD0-4BE8-82FC-9FCAD05BF289}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0C1A387E-0CD0-4BE8-82FC-9FCAD05BF289}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0C1A387E-0CD0-4BE8-82FC-9FCAD05BF289}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -293,6 +289,10 @@ Global
{ED8CEB38-7C95-43A8-B208-9C9828654AC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED8CEB38-7C95-43A8-B208-9C9828654AC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED8CEB38-7C95-43A8-B208-9C9828654AC1}.Release|Any CPU.Build.0 = Release|Any CPU
{05B8FF27-446B-49BF-B508-4A4C096D2BB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{05B8FF27-446B-49BF-B508-4A4C096D2BB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{05B8FF27-446B-49BF-B508-4A4C096D2BB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{05B8FF27-446B-49BF-B508-4A4C096D2BB2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -16,6 +16,7 @@
"common\\ASC.Feed\\ASC.Feed.csproj",
"common\\ASC.IPSecurity\\ASC.IPSecurity.csproj",
"common\\ASC.MessagingSystem\\ASC.MessagingSystem.csproj",
"common\\ASC.Migration\\ASC.Migration.csproj",
"common\\ASC.Notify.Textile\\ASC.Notify.Textile.csproj",
"common\\ASC.Textile\\ASC.Textile.csproj",
"common\\ASC.VoipService\\ASC.VoipService.csproj",

View File

@ -1,3 +1,5 @@
@echo off
echo "##########################################################"
echo "######### Start build and deploy Personal ##############"
echo "##########################################################"
@ -6,6 +8,7 @@ echo.
PUSHD %~dp0
call runasadmin.bat "%~dpnx0"
if %errorlevel% == 0 (
call start\stop.bat nopause
@ -18,7 +21,11 @@ call build\build.static.bat nopause personal
echo "BACK-END"
call build\build.backend.bat nopause
start /b call build\start\start.bat nopause
PUSHD %~dp0
call start\start.bat nopause
echo.
pause
)

View File

@ -42,7 +42,7 @@ curl https://artifacts.elastic.co/GPG-KEY-elasticsearch | apt-key add -
echo "deb https://artifacts.elastic.co/packages/${ELASTIC_DIST}.x/apt stable main" | tee /etc/apt/sources.list.d/elastic-${ELASTIC_DIST}.x.list
# add nodejs repo
curl -sL https://deb.nodesource.com/setup_12.x | bash -
curl -sL https://deb.nodesource.com/setup_14.x | bash -
#add yarn repo
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
@ -56,16 +56,16 @@ rm packages-microsoft-prod.deb
#install kafka
PRODUCT_DIR="/var/www/${product}"
if [ "$(ls "$PRODUCT_DIR/services/kafka" 2> /dev/null)" == "" ]; then
mkdir -p ${PRODUCT_DIR}/services/
if ! cat /etc/passwd | grep -q "kafka"; then
adduser --quiet --home ${PRODUCT_DIR}/services/kafka --system kafka
mkdir -p ${PRODUCT_DIR}/services/kafka/
if ! cat /etc/passwd | grep -q "onlyoffice"; then
adduser --quiet --home ${PRODUCT_DIR} --system --group onlyoffice
fi
cd ${PRODUCT_DIR}/services/kafka
KAFKA_VERSION=$(curl https://downloads.apache.org/kafka/ | grep -Eo '3.1.[0-9]' | tail -1)
KAFKA_ARCHIVE=$(curl https://downloads.apache.org/kafka/$KAFKA_VERSION/ | grep -Eo "kafka_2.[0-9][0-9]-$KAFKA_VERSION.tgz" | tail -1)
curl https://downloads.apache.org/kafka/$KAFKA_VERSION/$KAFKA_ARCHIVE -O
tar xzf $KAFKA_ARCHIVE --strip 1 && rm -rf $KAFKA_ARCHIVE
chown -R kafka ${PRODUCT_DIR}/services/kafka/
chown -R onlyoffice ${PRODUCT_DIR}/services/kafka/
cd -
fi
@ -76,14 +76,13 @@ Requires=network.target remote-fs.target
After=network.target remote-fs.target
[Service]
Type=simple
User=kafka
User=onlyoffice
ExecStart=/bin/sh -c '${PRODUCT_DIR}/services/kafka/bin/zookeeper-server-start.sh ${PRODUCT_DIR}/services/kafka/config/zookeeper.properties > ${PRODUCT_DIR}/services/kafka/zookeeper.log 2>&1'
ExecStop=${PRODUCT_DIR}/services/kafka/bin/zookeeper-server-stop.sh
Restart=on-abnormal
[Install]
WantedBy=multi-user.target
END
systemctl start zookeeper
fi
if [ ! -e /lib/systemd/system/kafka.service ]; then
@ -93,14 +92,13 @@ Requires=zookeeper.service
After=zookeeper.service
[Service]
Type=simple
User=kafka
User=onlyoffice
ExecStart=/bin/sh -c '${PRODUCT_DIR}/services/kafka/bin/kafka-server-start.sh ${PRODUCT_DIR}/services/kafka/config/server.properties > ${PRODUCT_DIR}/services/kafka/kafka.log 2>&1'
ExecStop=${PRODUCT_DIR}/services/kafka/bin/kafka-server-stop.sh
Restart=on-abnormal
[Install]
WantedBy=multi-user.target
END
systemctl start kafka
fi
if ! dpkg -l | grep -q "mysql-server"; then
@ -154,7 +152,7 @@ apt-get install -o DPkg::options::="--force-confnew" -yq \
g++ \
make \
yarn \
dotnet-sdk-5.0 \
dotnet-sdk-6.0 \
mysql-server \
mysql-client \
postgresql \

View File

@ -63,6 +63,7 @@ MYSQL_USER=""
MYSQL_PASSWORD=""
MYSQL_ROOT_PASSWORD=""
MYSQL_HOST=""
DATABASE_MIGRATION="true"
ZOO_PORT=""
ZOO_HOST=""
@ -291,6 +292,13 @@ while [ "$1" != "" ]; do
fi
;;
-dbm | --databasemigration )
if [ "$2" != "" ]; then
DATABASE_MIGRATION=$2
shift
fi
;;
-? | -h | --help )
echo " Usage: bash $HELP_TARGET [PARAMETER] [[PARAMETER], ...]"
echo
@ -320,6 +328,7 @@ while [ "$1" != "" ]; do
echo " -ep, --externalport external appserver port (default value 8092)"
echo " -mk, --machinekey setting for core.machinekey"
echo " -ls, --local_scripts run the installation from local scripts"
echo " -dbm, --databasemigration database migration (true|false)"
echo " -?, -h, --help this help"
echo
echo " Install all the components without document server:"
@ -538,6 +547,7 @@ install_docker_compose () {
rm get-pip.py
fi
python3 -m pip install --upgrade pip
python3 -m pip install docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
@ -790,7 +800,6 @@ download_files () {
fi
svn export --force https://github.com/ONLYOFFICE/${PRODUCT}/branches/${GIT_BRANCH}/build/install/docker/ ${BASE_DIR}
svn export --force https://github.com/ONLYOFFICE/CommunityServer/branches/master/build/sql/ ${BASE_DIR}/config/ #Download SQL scripts
reconfigure STATUS ${STATUS}
}
@ -822,6 +831,7 @@ install_mysql_server () {
reconfigure MYSQL_PASSWORD ${MYSQL_PASSWORD}
reconfigure MYSQL_ROOT_PASSWORD ${MYSQL_ROOT_PASSWORD}
reconfigure MYSQL_HOST ${MYSQL_HOST}
reconfigure DATABASE_MIGRATION ${DATABASE_MIGRATION}
docker-compose -f $BASE_DIR/db.yml up -d
}

View File

@ -140,13 +140,14 @@ if rpm -q "firewalld"; then
fi
{ ${package_manager} check-update ${package_sysname}-${product}; APPSERVER_CHECK_UPDATE=$?; } || true
if [[ $APPSERVER_CHECK_UPDATE -eq $UPDATE_AVAILABLE_CODE ]]; then
APPSERVER_NEED_UPDATE="true"
fi
if [ "$APPSERVER_INSTALLED" = "false" ]; then
${package_manager} install -y ${package_sysname}-${product}
elif [ "$APPSERVER_NEED_UPDATE" = "true" ]; then
${product}-configuration.sh \
-mysqlh ${MYSQL_SERVER_HOST} \
-mysqld ${MYSQL_SERVER_DB_NAME} \
-mysqlu ${MYSQL_SERVER_USER} \
-mysqlp ${MYSQL_ROOT_PASS}
elif [[ $APPSERVER_CHECK_UPDATE -eq $UPDATE_AVAILABLE_CODE ]]; then
ENVIRONMENT="$(cat /lib/systemd/system/${product}-api.service | grep -oP 'ENVIRONMENT=\K.*')"
USER_CONNECTIONSTRING=$(json -f /etc/onlyoffice/${product}/appsettings.$ENVIRONMENT.json ConnectionStrings.default.connectionString)
MYSQL_SERVER_HOST=$(echo $USER_CONNECTIONSTRING | grep -oP 'Server=\K.*' | grep -o '^[^;]*')
@ -156,34 +157,12 @@ elif [ "$APPSERVER_NEED_UPDATE" = "true" ]; then
MYSQL_ROOT_PASS=$(echo $USER_CONNECTIONSTRING | grep -oP 'Password=\K.*' | grep -o '^[^;]*')
${package_manager} -y update ${package_sysname}-${product}
fi
if [ "${APPSERVER_INSTALLED}" = "false" ] || [ "$APPSERVER_NEED_UPDATE" = "true" ]; then
expect << EOF
set timeout -1
log_user 1
if { "${UPDATE}" == "true" } {
spawn ${product}-configuration.sh -e ${ENVIRONMENT}
} else {
spawn ${product}-configuration.sh
}
expect -re "Database host:"
send "\025$MYSQL_SERVER_HOST\r"
expect -re "Database name:"
send "\025$MYSQL_SERVER_DB_NAME\r"
expect -re "Database user:"
send "\025$MYSQL_SERVER_USER\r"
expect -re "Database password:"
send "\025$MYSQL_ROOT_PASS\r"
expect eof
EOF
APPSERVER_INSTALLED="true";
${product}-configuration.sh \
-e ${ENVIRONMENT} \
-mysqlh ${MYSQL_SERVER_HOST} \
-mysqld ${MYSQL_SERVER_DB_NAME} \
-mysqlu ${MYSQL_SERVER_USER} \
-mysqlp ${MYSQL_ROOT_PASS}
fi
echo ""

View File

@ -62,7 +62,7 @@ rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-$REV.noarch.r
rpm -ivh https://rpms.remirepo.net/enterprise/remi-release-$REV.rpm || true
#add nodejs repo
curl -sL https://rpm.nodesource.com/setup_12.x | bash - || true
curl -sL https://rpm.nodesource.com/setup_14.x | bash - || true
#add yarn
curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | tee /etc/yum.repos.d/yarn.repo || true
@ -183,7 +183,7 @@ ${package_manager} -y install epel-release \
gcc-c++ \
make \
yarn \
dotnet-sdk-5.0 \
dotnet-sdk-6.0 \
elasticsearch-${ELASTIC_VERSION} --enablerepo=elasticsearch \
mysql-server \
nginx \
@ -207,4 +207,4 @@ if [ ! -e /usr/bin/json ]; then
fi
systemctl daemon-reload
package_services="rabbitmq-server postgresql redis supervisord nginx kafka mysqld"
package_services="rabbitmq-server postgresql redis supervisord nginx mysqld"

View File

@ -0,0 +1,132 @@
import base64
import hashlib
import hmac
import json
import base64
import sys
import os
import http.client
from radicale.auth import BaseAuth
from radicale.log import logger
import platform
from urllib.parse import urlparse
if platform.system() == "Linux":
sys.path.insert(0,'/usr/lib/python3/site-packages')
import requests
from urllib import request
from urllib.parse import urlsplit
from datetime import datetime, date, time
PLUGIN_CONFIG_SCHEMA = {
"auth": {
"portal_url": {"value": "", "type": str},
"machine_key": {"value": "", "type": str}
}
}
class Auth(BaseAuth):
def __init__(self, configuration):
super().__init__(configuration.copy(PLUGIN_CONFIG_SCHEMA))
def create_auth_token(self, pkey, machine_key):
machine_key = bytes(machine_key, 'UTF-8')
now = datetime.strftime(datetime.utcnow(), "%Y%m%d%H%M%S")
message = bytes('{0}\n{1}'.format(now, pkey), 'UTF-8')
_hmac = hmac.new(machine_key, message, hashlib.sha1)
signature = str(base64.urlsafe_b64encode(_hmac.digest()), 'UTF-8')
signature = signature.replace('-', '+')
signature = signature.replace('_', '/')
token = 'ASC {0}:{1}:{2}'.format(pkey, now, signature)
logger.info('Auth token: %r', token)
return token
def get_external_login(self, environ):
self._environ = environ
portal = ""
if self._environ.get("PATH_INFO"):
if len(self._environ.get("PATH_INFO").split('/')) >= 2:
userName = self._environ.get("PATH_INFO").split('/')[1]
if userName.find('@')!=-1:
portal = userName.split('@')[2]
if self._environ.get("HTTP_X_REWRITER_URL"):
os.environ[portal + 'HTTP_X_REWRITER_URL'] = self._environ["HTTP_X_REWRITER_URL"] # hack: common value for all modules
else:
urlScheme = ""
try:
c = http.client.HTTPSConnection(portal)
c.request("GET", "/")
response = c.getresponse()
urlScheme = "https"
os.environ[portal + 'HTTP_X_REWRITER_URL'] = self._environ["HTTP_X_REWRITER_URL"]
except:
urlScheme = "http"
os.environ[portal + 'HTTP_X_REWRITER_URL'] = urlScheme + "://" + portal
return()
def login(self, login, password):
portal_url = ""
machine_key = self.configuration.get("auth", "machine_key")
auth_token = self.create_auth_token("radicale", machine_key)
portal = ""
if self._environ.get("PATH_INFO"):
if len(self._environ.get("PATH_INFO").split('/')) >= 2:
userName = self._environ.get("PATH_INFO").split('/')[1]
if userName.find('@')!=-1:
portal = userName.split('@')[2]
remote_host = ""
rewriter_url = ""
if os.environ[portal + 'HTTP_X_REWRITER_URL']:
rewriter_url = os.environ[portal + 'HTTP_X_REWRITER_URL']
parsed_uri = urlparse(rewriter_url)
if parsed_uri.netloc != '':
remote_host = parsed_uri.netloc.replace("'", "").split(':')[0]
elif parsed_uri.path != '':
remote_host = parsed_uri.path.replace("'", "").split(':')[0]
else:
logger.error("Authenticated error. Parse REWRITER_URL")
return ""
else:
logger.error("Authenticated error. not exist HTTP_X_REWRITER_URL")
return ""
try:
logger.info('Remote host: %r', remote_host)
portal_url = self.configuration.get("auth", "portal_url")
url = portal_url+"/is_caldav_authenticated"
payload = {'User': login+"@"+remote_host, 'Password': password}
headers = {'Content-type': 'application/json', 'Authorization': auth_token, 'HTTP_X_REWRITER_URL': rewriter_url}
res = requests.post(url, data=json.dumps(payload), headers=headers)
except:
logger.error("Authenticated error. API system")
res = False
try:
response = res.json()
except:
logger.error("Authenticated error.")
return ""
if res.status_code != 200:
logger.error("Error login response: %r", response)
return ""
if 'error' in response:
logger.error("Error login response: %r", response)
return ""
else:
if 'value' in response:
if response['value'] != "true":
logger.error("Error login response: %r", response)
return ""
else:
return login+"@"+remote_host

View File

@ -0,0 +1,5 @@
#!/usr/bin/env python3
from distutils.core import setup
setup(name="app_auth_plugin", version='1.0.0', packages=["app_auth_plugin"])

View File

@ -0,0 +1,67 @@
import configparser
import re
from radicale import pathutils, rights
from radicale.log import logger
from radicale import pathutils
class Rights(rights.BaseRights):
def __init__(self, configuration):
super().__init__(configuration)
self._filename = configuration.get("rights", "file")
self.full_write_file_path = ""
def authorization(self, user, path):
user = user or ""
sane_path = pathutils.strip_path(path)
# Prevent "regex injection"
escaped_user = re.escape(user)
rights_config = configparser.ConfigParser()
try:
if not rights_config.read(self._filename):
raise RuntimeError("No such file: %r" %
self._filename)
except Exception as e:
raise RuntimeError("Failed to load rights file %r: %s" %
(self._filename, e)) from e
if path=="/" or path=='':
return rights_config.get("owner-write", "permissions")
if path.find("_write.ics") != -1:
self.full_write_file_path = path
elif path.find(".ics") != -1 and path.find("_write.ics") == -1:
self.full_write_file_path = ""
for section in rights_config.sections():
try:
user_pattern = rights_config.get(section, "user")
collection_pattern = rights_config.get(section, "collection")
# Use empty format() for harmonized handling of curly braces
user_match = re.fullmatch(user_pattern.format(), user)
collection_match = user_match and re.fullmatch(
collection_pattern.format(
*map(re.escape, user_match.groups()),
user=escaped_user), sane_path)
file_match = True if self.full_write_file_path.find(path) != -1 and self.full_write_file_path != "" else False
except Exception as e:
raise RuntimeError("Error in section %r of rights file %r: "
"%s" % (section, self._filename, e)) from e
if user_match and collection_match:
if file_match and section == "allow-readonly":
logger.debug("Rule %r:%r matches %r:%r from section %r Full Access",
user, sane_path, user_pattern,
collection_pattern, section)
self.full_write_file_path = ""
return rights_config.get("admin", "permissions")
else:
logger.debug("Rule %r:%r matches %r:%r from section %r",
user, sane_path, user_pattern,
collection_pattern, section)
return rights_config.get(section, "permissions")
logger.debug("Rule %r:%r doesn't match %r:%r from section %r",
user, sane_path, user_pattern, collection_pattern,
section)
logger.info("Rights: %r:%r doesn't match any section", user, sane_path)
return ""

View File

@ -0,0 +1,5 @@
#!/usr/bin/env python3
from distutils.core import setup
setup(name="app_rights_plugin", version='1.0.0', packages=["app_rights_plugin"])

View File

@ -0,0 +1,177 @@
# This file is part of Radicale Server - Calendar Server
# Copyright © 2014 Jean-Marc Martins
# Copyright © 2012-2017 Guillaume Ayoub
# Copyright © 2017-2019 Unrud <unrud@outlook.com>
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>.
"""
Storage backend that stores data in the file system.
Uses one folder per collection and one file per collection entry.
"""
import contextlib
import os
import sys
import time
import platform
from itertools import chain
from tempfile import TemporaryDirectory
from radicale import pathutils, storage
from radicale.storage.multifilesystem.cache import CollectionCacheMixin
from radicale.storage.multifilesystem.create_collection import \
StorageCreateCollectionMixin
from app_store_plugin.delete import CollectionDeleteMixin
from radicale.storage.multifilesystem.discover import StorageDiscoverMixin
from radicale.storage.multifilesystem.get import CollectionGetMixin
from app_store_plugin.history import CollectionHistoryMixin
from radicale.storage.multifilesystem.lock import (CollectionLockMixin,
StorageLockMixin)
from radicale.storage.multifilesystem.meta import CollectionMetaMixin
from radicale.storage.multifilesystem.move import StorageMoveMixin
from app_store_plugin.sync import CollectionSyncMixin
from app_store_plugin.upload import CollectionUploadMixin
from app_store_plugin.cache import CollectionCacheMixin
from app_store_plugin import log
from radicale.storage.multifilesystem.verify import StorageVerifyMixin
PLUGIN_CONFIG_SCHEMA = {
"storage": {
"portal_url": {"value": "", "type": str}
}
}
class Collection(
CollectionCacheMixin, CollectionDeleteMixin, CollectionGetMixin,
CollectionHistoryMixin, CollectionLockMixin, CollectionMetaMixin,
CollectionSyncMixin, CollectionUploadMixin, storage.BaseCollection):
def __init__(self, storage_, path, filesystem_path=None):
self._storage = storage_
folder = self._storage._get_collection_root_folder()
# Path should already be sanitized
self._path = pathutils.strip_path(path)
self._encoding = self._storage.configuration.get("encoding", "stock")
if filesystem_path is None:
filesystem_path = pathutils.path_to_filesystem(folder, self.path)
self._filesystem_path = filesystem_path
self._etag_cache = None
# Start logging
filename = os.path.expanduser("radicale.log.config")
try:
log.start("radicale", filename)
except Exception as e:
print("ERROR: Failed to start logger: %s" % e, file=sys.stderr)
super().__init__()
@property
def path(self):
return self._path
@contextlib.contextmanager
def _atomic_write(self, path, mode="w", newline=None):
parent_dir, name = os.path.split(path)
prefix = ''
# Do not use mkstemp because it creates with permissions 0o600
with TemporaryDirectory(
prefix=".Radicale.tmp-", dir=parent_dir) as tmp_dir:
with open(prefix+os.path.join(tmp_dir, name), mode, newline=newline,
encoding=None if "b" in mode else self._encoding) as tmp:
yield tmp
tmp.flush()
self._storage._fsync(tmp)
os.replace(prefix+os.path.join(tmp_dir, name), path)
self._storage._sync_directory(parent_dir)
@property
def last_modified(self):
relevant_files = chain(
(self._filesystem_path,),
(self._props_path,) if os.path.exists(self._props_path) else (),
(os.path.join(self._filesystem_path, h) for h in self._list()))
last = max(map(os.path.getmtime, relevant_files))
return time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(last))
@property
def etag(self):
# reuse cached value if the storage is read-only
if self._storage._lock.locked == "w" or self._etag_cache is None:
self._etag_cache = super().etag
return self._etag_cache
class Storage(
StorageCreateCollectionMixin, StorageDiscoverMixin, StorageLockMixin,
StorageMoveMixin, StorageVerifyMixin, storage.BaseStorage):
_collection_class = Collection
def __init__(self, configuration):
super().__init__(configuration.copy(PLUGIN_CONFIG_SCHEMA))
folder = configuration.get("storage", "filesystem_folder")
self._makedirs_synced(folder)
def _get_collection_root_folder(self):
filesystem_folder = self.configuration.get(
"storage", "filesystem_folder")
return os.path.join(filesystem_folder, "collection-root")
def _fsync(self, f):
if self.configuration.get("storage", "_filesystem_fsync"):
try:
pathutils.fsync(f.fileno())
except OSError as e:
raise RuntimeError("Fsync'ing file %r failed: %s" %
(f.name, e)) from e
def _sync_directory(self, path):
"""Sync directory to disk.
This only works on POSIX and does nothing on other systems.
"""
if not self.configuration.get("storage", "_filesystem_fsync"):
return
if os.name == "posix":
try:
fd = os.open(path, 0)
try:
pathutils.fsync(fd)
finally:
os.close(fd)
except OSError as e:
raise RuntimeError("Fsync'ing directory %r failed: %s" %
(path, e)) from e
def _makedirs_synced(self, filesystem_path):
"""Recursively create a directory and its parents in a sync'ed way.
This method acts silently when the folder already exists.
"""
if os.path.isdir(filesystem_path):
return
parent_filesystem_path = os.path.dirname(filesystem_path)
# Prevent infinite loop
if filesystem_path != parent_filesystem_path:
# Create parent dirs recursively
self._makedirs_synced(parent_filesystem_path)
# Possible race!
os.makedirs(filesystem_path, exist_ok=True)
self._sync_directory(parent_filesystem_path)

View File

@ -0,0 +1,98 @@
import os
import pickle
import time
import platform
from hashlib import sha256
from radicale import pathutils, storage
from radicale.log import logger
class CollectionCacheMixin:
def _clean_cache(self, folder, names, max_age=None):
"""Delete all ``names`` in ``folder`` that are older than ``max_age``.
"""
age_limit = time.time() - max_age if max_age is not None else None
modified = False
for name in names:
if not pathutils.is_safe_filesystem_path_component(name):
continue
if age_limit is not None:
try:
# Race: Another process might have deleted the file.
mtime = os.path.getmtime(os.path.join(folder, name))
except FileNotFoundError:
continue
if mtime > age_limit:
continue
logger.debug("Found expired item in cache: %r", name)
# Race: Another process might have deleted or locked the
# file.
try:
os.remove(os.path.join(folder, name))
except (FileNotFoundError, PermissionError):
continue
modified = True
if modified:
self._storage._sync_directory(folder)
@staticmethod
def _item_cache_hash(raw_text):
_hash = sha256()
_hash.update(storage.CACHE_VERSION)
_hash.update(raw_text)
return _hash.hexdigest()
def _item_cache_content(self, item, cache_hash=None):
text = item.serialize()
if cache_hash is None:
cache_hash = self._item_cache_hash(text.encode(self._encoding))
return (cache_hash, item.uid, item.etag, text, item.name,
item.component_name, *item.time_range)
def _store_item_cache(self, href, item, cache_hash=None):
prefix = ''
if platform.system() == 'Windows':
prefix = '\\\\?\\'
cache_folder = os.path.join(prefix+self._filesystem_path, ".Radicale.cache",
"item")
content = self._item_cache_content(item, cache_hash)
self._storage._makedirs_synced(cache_folder)
try:
# Race: Other processes might have created and locked the
# file.
with self._atomic_write(os.path.join(cache_folder, href),
"wb") as f:
pickle.dump(content, f)
except PermissionError:
pass
return content
def _load_item_cache(self, href, input_hash):
prefix = ''
if platform.system() == 'Windows':
prefix = '\\\\?\\'
cache_folder = os.path.join(prefix+self._filesystem_path, ".Radicale.cache",
"item")
cache_hash = uid = etag = text = name = tag = start = end = None
try:
with open(os.path.join(cache_folder, href), "rb") as f:
cache_hash, *content = pickle.load(f)
if cache_hash == input_hash:
uid, etag, text, name, tag, start, end = content
except FileNotFoundError:
pass
except (pickle.UnpicklingError, ValueError) as e:
logger.warning("Failed to load item cache entry %r in %r: %s",
href, self.path, e, exc_info=True)
return cache_hash, uid, etag, text, name, tag, start, end
def _clean_item_cache(self):
prefix = ''
if platform.system() == 'Windows':
prefix = '\\\\?\\'
cache_folder = os.path.join(prefix+self._filesystem_path, ".Radicale.cache",
"item")
self._clean_cache(cache_folder, (
e.name for e in os.scandir(cache_folder) if not
os.path.isfile(os.path.join(self._filesystem_path, e.name))))

View File

@ -0,0 +1,52 @@
import os
from tempfile import TemporaryDirectory
from radicale import pathutils, storage
import requests
from urllib import request
class CollectionDeleteMixin:
def delete(self, href=None):
if href != None:
user = self.path.split("/")[0]
domain = user.split("@")[2]
self.delete_event_portals(self.path+"/"+href, domain)
if href is None:
# Delete the collection
parent_dir = os.path.dirname(self._filesystem_path)
try:
os.rmdir(self._filesystem_path)
except OSError:
with TemporaryDirectory(
prefix=".Radicale.tmp-", dir=parent_dir) as tmp:
os.rename(self._filesystem_path, os.path.join(
tmp, os.path.basename(self._filesystem_path)))
self._storage._sync_directory(parent_dir)
else:
self._storage._sync_directory(parent_dir)
else:
# Delete an item
if not pathutils.is_safe_filesystem_path_component(href):
raise pathutils.UnsafePathError(href)
path = pathutils.path_to_filesystem(self._filesystem_path, href)
if not os.path.isfile(path):
raise storage.ComponentNotFoundError(href)
os.remove(path)
self._storage._sync_directory(os.path.dirname(path))
# Track the change
self._update_history_etag(href, None)
self._clean_history()
def delete_event_portals(self, path, domain):
portal = ""
userName = path.split('/')[0]
portal = userName.split('@')[2]
rewriter_url = os.environ.get(portal + 'HTTP_X_REWRITER_URL', '')
portal_url = self._storage.configuration.get("storage", "portal_url")
machine_key = self._storage.configuration.get("auth", "machine_key")
auth_token = self.create_auth_token("radicale", machine_key)
headers = {'Authorization': auth_token, 'HTTP_X_REWRITER_URL': rewriter_url if rewriter_url.find(domain) != -1 else ""}
url = portal_url+"/caldav_delete_event?eventInfo={}".format (path)
resp = requests.get(url, headers=headers)

View File

@ -0,0 +1,73 @@
import binascii
import os
import pickle
import platform
from radicale import item as radicale_item
from radicale import pathutils
from radicale.log import logger
class CollectionHistoryMixin:
def _update_history_etag(self, href, item):
"""Updates and retrieves the history etag from the history cache.
The history cache contains a file for each current and deleted item
of the collection. These files contain the etag of the item (empty
string for deleted items) and a history etag, which is a hash over
the previous history etag and the etag separated by "/".
"""
prefix = ''
if platform.system() == 'Windows':
prefix = '\\\\?\\'
history_folder = os.path.join(prefix+self._filesystem_path,
".Radicale.cache", "history")
try:
with open(os.path.join(history_folder, href), "rb") as f:
cache_etag, history_etag = pickle.load(f)
except (FileNotFoundError, pickle.UnpicklingError, ValueError) as e:
if isinstance(e, (pickle.UnpicklingError, ValueError)):
logger.warning(
"Failed to load history cache entry %r in %r: %s",
href, self.path, e, exc_info=True)
cache_etag = ""
# Initialize with random data to prevent collisions with cleaned
# expired items.
history_etag = binascii.hexlify(os.urandom(16)).decode("ascii")
etag = item.etag if item else ""
if etag != cache_etag:
self._storage._makedirs_synced(history_folder)
history_etag = radicale_item.get_etag(
history_etag + "/" + etag).strip("\"")
try:
# Race: Other processes might have created and locked the file.
with self._atomic_write(os.path.join(history_folder, href),
"wb") as f:
pickle.dump([etag, history_etag], f)
except PermissionError:
pass
return history_etag
def _get_deleted_history_hrefs(self):
"""Returns the hrefs of all deleted items that are still in the
history cache."""
history_folder = os.path.join(self._filesystem_path,
".Radicale.cache", "history")
try:
for entry in os.scandir(history_folder):
href = entry.name
if not pathutils.is_safe_filesystem_path_component(href):
continue
if os.path.isfile(os.path.join(self._filesystem_path, href)):
continue
yield href
except FileNotFoundError:
pass
def _clean_history(self):
# Delete all expired history entries of deleted items.
history_folder = os.path.join(self._filesystem_path,
".Radicale.cache", "history")
self._clean_cache(history_folder, self._get_deleted_history_hrefs(),
max_age=self._storage.configuration.get(
"storage", "max_sync_token_age"))

View File

@ -0,0 +1,51 @@
import logging
import logging.config
import signal
import sys
def configure_from_file(logger, filename, debug):
logging.config.fileConfig(filename, disable_existing_loggers=False)
if debug:
logger.setLevel(logging.DEBUG)
for handler in logger.handlers:
handler.setLevel(logging.DEBUG)
return logger
class RemoveTracebackFilter(logging.Filter):
def filter(self, record):
record.exc_info = None
return True
def start(name="radicale", filename=None, debug=False):
"""Start the logging according to the configuration."""
logger = logging.getLogger(name)
if debug:
logger.setLevel(logging.DEBUG)
else:
logger.addFilter(RemoveTracebackFilter())
if filename:
# Configuration taken from file
try:
configure_from_file(logger, filename, debug)
except Exception as e:
raise RuntimeError("Failed to load logging configuration file %r: "
"%s" % (filename, e)) from e
# Reload config on SIGHUP (UNIX only)
if hasattr(signal, "SIGHUP"):
def handler(signum, frame):
try:
configure_from_file(logger, filename, debug)
except Exception as e:
logger.error("Failed to reload logging configuration file "
"%r: %s", filename, e, exc_info=True)
signal.signal(signal.SIGHUP, handler)
else:
# Default configuration, standard output
handler = logging.StreamHandler(sys.stderr)
handler.setFormatter(
logging.Formatter("[%(thread)x] %(levelname)s: %(message)s"))
logger.addHandler(handler)
return logger

View File

@ -0,0 +1,106 @@
import itertools
import os
import pickle
import platform
from hashlib import sha256
from radicale.log import logger
class CollectionSyncMixin:
def sync(self, old_token=None):
# The sync token has the form http://radicale.org/ns/sync/TOKEN_NAME
# where TOKEN_NAME is the sha256 hash of all history etags of present
# and past items of the collection.
def check_token_name(token_name):
if len(token_name) != 64:
return False
for c in token_name:
if c not in "0123456789abcdef":
return False
return True
old_token_name = None
if old_token:
# Extract the token name from the sync token
if not old_token.startswith("http://radicale.org/ns/sync/"):
raise ValueError("Malformed token: %r" % old_token)
old_token_name = old_token[len("http://radicale.org/ns/sync/"):]
if not check_token_name(old_token_name):
raise ValueError("Malformed token: %r" % old_token)
# Get the current state and sync-token of the collection.
state = {}
token_name_hash = sha256()
# Find the history of all existing and deleted items
for href, item in itertools.chain(
((item.href, item) for item in self.get_all()),
((href, None) for href in self._get_deleted_history_hrefs())):
history_etag = self._update_history_etag(href, item)
state[href] = history_etag
token_name_hash.update((href + "/" + history_etag).encode())
token_name = token_name_hash.hexdigest()
token = "http://radicale.org/ns/sync/%s" % token_name
if token_name == old_token_name:
# Nothing changed
return token, ()
token_folder = os.path.join(self._filesystem_path,
".Radicale.cache", "sync-token")
token_path = os.path.join(token_folder, token_name)
old_state = {}
if old_token_name:
# load the old token state
old_token_path = os.path.join(token_folder, old_token_name)
try:
# Race: Another process might have deleted the file.
with open(old_token_path, "rb") as f:
old_state = pickle.load(f)
except (FileNotFoundError, pickle.UnpicklingError,
ValueError) as e:
if isinstance(e, (pickle.UnpicklingError, ValueError)):
logger.warning(
"Failed to load stored sync token %r in %r: %s",
old_token_name, self.path, e, exc_info=True)
# Delete the damaged file
try:
os.remove(old_token_path)
except (FileNotFoundError, PermissionError):
pass
raise ValueError("Token not found: %r" % old_token)
# write the new token state or update the modification time of
# existing token state
if not os.path.exists(token_path):
self._storage._makedirs_synced(token_folder)
try:
# Race: Other processes might have created and locked the file.
prefix = ''
if platform.system() == 'Windows':
prefix = '\\\\?\\'
with self._atomic_write(prefix+token_path, "wb") as f:
pickle.dump(state, f)
except PermissionError:
pass
else:
# clean up old sync tokens and item cache
self._clean_cache(token_folder, os.listdir(token_folder),
max_age=self._storage.configuration.get(
"storage", "max_sync_token_age"))
self._clean_history()
else:
# Try to update the modification time
try:
# Race: Another process might have deleted the file.
os.utime(token_path)
except FileNotFoundError:
pass
changes = []
# Find all new, changed and deleted (that are still in the item cache)
# items
for href, history_etag in state.items():
if history_etag != old_state.get(href):
changes.append(href)
# Find all deleted items that are no longer in the item cache
for href, history_etag in old_state.items():
if href not in state:
changes.append(href)
return token, changes

View File

@ -0,0 +1,151 @@
# This file is part of Radicale Server - Calendar Server
# Copyright © 2014 Jean-Marc Martins
# Copyright © 2012-2017 Guillaume Ayoub
# Copyright © 2017-2018 Unrud <unrud@outlook.com>
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>.
import os
import pickle
from radicale import item as radicale_item
from radicale import pathutils
from radicale import logger
from datetime import datetime, date, time
import time
import base64
import hashlib
import hmac
import requests
from urllib import request
from threading import Thread
from configparser import RawConfigParser
class CollectionUploadMixin:
def upload(self, href, item):
if not pathutils.is_safe_filesystem_path_component(href):
raise pathutils.UnsafePathError(href)
try:
self._store_item_cache(href, item)
except Exception as e:
raise ValueError("Failed to store item %r in collection %r: %s" %
(href, self.path, e)) from e
path = pathutils.path_to_filesystem(self._filesystem_path, href)
with self._atomic_write(path, newline="") as fd:
fd.write(item.serialize())
# Clean the cache after the actual item is stored, or the cache entry
# will be removed again.
self._clean_item_cache()
# Track the change
user = self.path.split("/")[0]
domain = user.split("@")[2]
try:
if item.serialize().find("PRODID:-//Office//Portal//EN") == -1:
th = Thread(target=self.set_to_portals, args=(self.path + "/" + href,domain))
th.start()
except:
logger.error("Portal sending error.")
self._update_history_etag(href, item)
self._clean_history()
return self._get(href, verify_href=False)
def create_auth_token(self, pkey, machine_key):
machine_key = bytes(machine_key, 'UTF-8')
now = datetime.strftime(datetime.utcnow(), "%Y%m%d%H%M%S")
message = bytes('{0}\n{1}'.format(now, pkey), 'UTF-8')
_hmac = hmac.new(machine_key, message, hashlib.sha1)
signature = str(base64.urlsafe_b64encode(_hmac.digest()), 'UTF-8')
signature = signature.replace('-', '+')
signature = signature.replace('_', '/')
token = 'ASC {0}:{1}:{2}'.format(pkey, now, signature)
logger.debug('Auth token: %r', token)
return token
def set_to_portals(self, path, domain):
portal = ""
userName = path.split('/')[0]
portal = userName.split('@')[2]
rewriter_url = os.environ.get("localhost" + 'HTTP_X_REWRITER_URL', '')
portal_url = self._storage.configuration.get("storage", "portal_url")
machine_key = self._storage.configuration.get("auth", "machine_key")
auth_token = self.create_auth_token("radicale", machine_key)
headers = {'Authorization': auth_token, 'HTTP_X_REWRITER_URL': rewriter_url if rewriter_url.find(domain) != -1 else "" }
url = portal_url+"/change_to_storage?change={}".format (path)
resp = requests.get(url, headers=headers)
def _upload_all_nonatomic(self, items, suffix=""):
"""Upload a new set of items.
This takes a list of vobject items and
uploads them nonatomic and without existence checks.
"""
cache_folder = os.path.join(self._filesystem_path,
".Radicale.cache", "item")
self._storage._makedirs_synced(cache_folder)
hrefs = set()
for item in items:
uid = item.uid
try:
cache_content = self._item_cache_content(item)
except Exception as e:
raise ValueError(
"Failed to store item %r in temporary collection %r: %s" %
(uid, self.path, e)) from e
href_candidate_funtions = []
if os.name in ("nt", "posix"):
href_candidate_funtions.append(
lambda: uid if uid.lower().endswith(suffix.lower())
else uid + suffix)
href_candidate_funtions.extend((
lambda: radicale_item.get_etag(uid).strip('"') + suffix,
lambda: radicale_item.find_available_uid(hrefs.__contains__,
suffix)))
href = f = None
while href_candidate_funtions:
href = href_candidate_funtions.pop(0)()
if href in hrefs:
continue
if not pathutils.is_safe_filesystem_path_component(href):
if not href_candidate_funtions:
raise pathutils.UnsafePathError(href)
continue
try:
f = open(pathutils.path_to_filesystem(
self._filesystem_path, href),
"w", newline="", encoding=self._encoding)
break
except OSError as e:
if href_candidate_funtions and (
os.name == "posix" and e.errno == 22 or
os.name == "nt" and e.errno == 123):
continue
raise
with f:
f.write(item.serialize())
f.flush()
self._storage._fsync(f)
hrefs.add(href)
with open(os.path.join(cache_folder, href), "wb") as f:
pickle.dump(cache_content, f)
f.flush()
self._storage._fsync(f)
self._storage._sync_directory(cache_folder)
self._storage._sync_directory(self._filesystem_path)

View File

@ -0,0 +1,3 @@
from distutils.core import setup
setup(name="app_store_plugin", version='1.0.0', packages=["app_store_plugin"])

View File

@ -0,0 +1,13 @@
./app_store_plugin
./app_auth_plugin
./app_rights_plugin
pytz
vobject
certifi
chardet
idna
python_dateutil
six
urllib3
requests
radicale==3.0.5

View File

@ -92,7 +92,7 @@ while [ "$1" != "" ]; do
-zkp | --zookeeperport )
if [ "$2" != "" ]; then
ZOOKEEPER_HOST=$2
ZOOKEEPER_PORT=$2
shift
fi
;;
@ -208,30 +208,26 @@ install_json() {
chown onlyoffice:onlyoffice $USER_CONF
set_core_machinekey
$JSON_USERCONF "this.core={'base-domain': \"$APP_HOST\", 'machinekey': \"$CORE_MACHINEKEY\" }" \
$JSON_USERCONF "this.core={'base-domain': \"$APP_HOST\", 'machinekey': \"$CORE_MACHINEKEY\", \
'products': { 'folder': '/var/www/appserver/products', 'subfolder': 'server'} }" \
-e "this.urlshortener={ 'path': '../ASC.UrlShortener/index.js' }" -e "this.thumb={ 'path': '../ASC.Thumbnails/' }" \
-e "this.socket={ 'path': '../ASC.Socket.IO/' }" -e "this.ssoauth={ 'path': '../ASC.SsoAuth/' }" >/dev/null 2>&1
$JSON $APP_DIR/appsettings.json -e "this.core.products.subfolder='server'" >/dev/null 2>&1
$JSON $APP_DIR/appsettings.services.json -e "this.core={ 'products': { 'folder': '../../products', 'subfolder': 'server'} }" >/dev/null 2>&1
fi
}
restart_services() {
echo -n "Restarting services... "
sed -i "s/Type=.*/Type=simple/" $SYSTEMD_DIR/${PRODUCT}-calendar.service >/dev/null 2>&1 #Fix non-start of service
sed -i "s/ENVIRONMENT=.*/ENVIRONMENT=$ENVIRONMENT/" $SYSTEMD_DIR/${PRODUCT}*.service >/dev/null 2>&1
systemctl daemon-reload
for SVC in nginx ${MYSQL_PACKAGE} ${PRODUCT}-api ${PRODUCT}-api-system ${PRODUCT}-urlshortener ${PRODUCT}-thumbnails \
${PRODUCT}-socket ${PRODUCT}-studio-notify ${PRODUCT}-notify ${PRODUCT}-people-server ${PRODUCT}-files \
${PRODUCT}-files-services ${PRODUCT}-studio ${PRODUCT}-backup ${PRODUCT}-storage-encryption \
${PRODUCT}-storage-migration ${PRODUCT}-projects-server ${PRODUCT}-telegram-service ${PRODUCT}-crm \
${PRODUCT}-calendar ${PRODUCT}-mail elasticsearch kafka zookeeper
${PRODUCT}-storage-migration ${PRODUCT}-telegram-service elasticsearch $KAFKA_SERVICE $ZOOKEEPER_SERVICE
do
systemctl enable $SVC.service >/dev/null 2>&1
systemctl restart $SVC.service
systemctl enable $SVC >/dev/null 2>&1
systemctl restart $SVC
done
echo "OK"
}
@ -242,10 +238,27 @@ input_db_params(){
local def_DB_NAME=$(echo $user_connectionString | grep -oP 'Database=\K.*' | grep -o '^[^;]*')
local def_DB_USER=$(echo $user_connectionString | grep -oP 'User ID=\K.*' | grep -o '^[^;]*')
if [ -z $def_DB_HOST ] && [ -z $DB_HOST ]; then read -e -p "Database host: " -i "$DB_HOST" DB_HOST; fi
if [ -z $def_DB_NAME ] && [ -z $DB_NAME ]; then read -e -p "Database name: " -i "$DB_NAME" DB_NAME; fi
if [ -z $def_DB_USER ] && [ -z $DB_USER ]; then read -e -p "Database user: " -i "$DB_USER" DB_USER; fi
if [ -z $DB_PWD ]; then read -e -p "Database password: " -i "$DB_PWD" DB_PWD; fi
if [ -z $def_DB_HOST ] && [ -z $DB_HOST ]; then
read -e -p "Database host: " -i "$DB_HOST" DB_HOST;
else
DB_HOST=${DB_HOST:-$def_DB_HOST}
fi
if [ -z $def_DB_NAME ] && [ -z $DB_NAME ]; then
read -e -p "Database name: " -i "$DB_NAME" DB_NAME;
else
DB_NAME=${DB_NAME:-$def_DB_NAME}
fi
if [ -z $def_DB_USER ] && [ -z $DB_USER ]; then
read -e -p "Database user: " -i "$DB_USER" DB_USER;
else
DB_USER=${DB_USER:-$def_DB_USER}
fi
if [ -z $DB_PWD ]; then
read -e -p "Database password: " -i "$DB_PWD" -s DB_PWD;
fi
}
establish_mysql_conn(){
@ -267,8 +280,16 @@ establish_mysql_conn(){
#Save db settings in .json
$JSON_USERCONF "this.ConnectionStrings={'default': {'connectionString': \
\"Server=$DB_HOST;Port=$DB_PORT;Database=$DB_NAME;User ID=$DB_USER;Password=$DB_PWD;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none\"}}" >/dev/null 2>&1
\"Server=$DB_HOST;Port=$DB_PORT;Database=$DB_NAME;User ID=$DB_USER;Password=$DB_PWD;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=true;Connection Timeout=30;Maximum Pool Size=300\"}}" >/dev/null 2>&1
change_mysql_config
#Enable database migration
$JSON_USERCONF "this.migration={'enabled': \"true\"}" >/dev/null 2>&1
#Fixing appserver-backup startup error \ Adding backup_backup and backup_schedule tables
$MYSQL -D "$DB_NAME" -e 'CREATE TABLE IF NOT EXISTS `backup_backup` ( `id` char(38) NOT NULL, `tenant_id` int(11) NOT NULL, `is_scheduled` int(1) NOT NULL, `name` varchar(255) NOT NULL, `storage_type` int(11) NOT NULL, `storage_base_path` varchar(255) DEFAULT NULL, `storage_path` varchar(255) NOT NULL, `created_on` datetime NOT NULL, `expires_on` datetime NOT NULL DEFAULT "0001-01-01 00:00:00", `storage_params` TEXT NULL, `hash` char(64) NOT NULL, PRIMARY KEY (`id`), KEY `tenant_id` (`tenant_id`), KEY `expires_on` (`expires_on`), KEY `is_scheduled` (`is_scheduled`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;' >/dev/null 2>&1
$MYSQL -D "$DB_NAME" -e 'CREATE TABLE IF NOT EXISTS `backup_schedule` ( `tenant_id` int(11) NOT NULL, `backup_mail` int(11) NOT NULL DEFAULT "0", `cron` varchar(255) NOT NULL, `backups_stored` int(11) NOT NULL, `storage_type` int(11) NOT NULL, `storage_base_path` varchar(255) DEFAULT NULL, `last_backup_time` datetime NOT NULL, `storage_params` TEXT NULL, PRIMARY KEY (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;' >/dev/null 2>&1
echo "OK"
}
@ -376,47 +397,6 @@ change_mysql_config(){
systemctl restart ${MYSQL_PACKAGE} >/dev/null 2>&1
}
execute_mysql_script(){
change_mysql_config
while ! $MYSQL -e ";" >/dev/null 2>&1; do
sleep 1
done
if [ "$DB_USER" = "root" ] && [ ! "$(mysql -V | grep ' 5.5.')" ]; then
# allow connect via mysql_native_password with root and empty password
$MYSQL -D "mysql" -e "update user set plugin='mysql_native_password' where user='root';ALTER USER '${DB_USER}'@'localhost' IDENTIFIED WITH mysql_native_password BY '${DB_PWD}';" >/dev/null 2>&1
fi
#Checking the quantity of the tables created in the db
DB_TABLES_COUNT=$($MYSQL --silent --skip-column-names -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='${DB_NAME}'");
local SQL_DIR="/var/www/${PRODUCT}/sql"
if [ "${DB_TABLES_COUNT}" -eq "0" ]; then
echo -n "Installing MYSQL database... "
#Adding data to the db
sed -i -e '1 s/^/SET SQL_MODE='ALLOW_INVALID_DATES';\n/;' $SQL_DIR/onlyoffice.sql #Fix a bug related to an incorrect date
$MYSQL -e "CREATE DATABASE IF NOT EXISTS $DB_NAME CHARACTER SET utf8 COLLATE 'utf8_general_ci';" >/dev/null 2>&1
echo 'CREATE TABLE IF NOT EXISTS `Tenants` ( `id` varchar(200) NOT NULL, `Status` varchar(200) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;' >> $SQL_DIR/onlyoffice.sql #Fix non-existent tables Tenants
$MYSQL "$DB_NAME" < "$SQL_DIR/createdb.sql" >/dev/null 2>&1
$MYSQL "$DB_NAME" < "$SQL_DIR/onlyoffice.sql" >/dev/null 2>&1
$MYSQL "$DB_NAME" < "$SQL_DIR/onlyoffice.data.sql" >/dev/null 2>&1
$MYSQL "$DB_NAME" < "$SQL_DIR/onlyoffice.resources.sql" >/dev/null 2>&1
for i in $(ls $SQL_DIR/*upgrade*.sql); do
$MYSQL "$DB_NAME" < ${i} >/dev/null 2>&1
done
else
echo -n "Upgrading MySQL database... "
for i in $(ls $SQL_DIR/*upgrade*.sql); do
$MYSQL "$DB_NAME" < ${i} >/dev/null 2>&1
done
fi
echo "OK"
}
setup_nginx(){
echo -n "Configuring nginx... "
@ -443,7 +423,6 @@ setup_nginx(){
PORTS+=('5002') #ASC.People
PORTS+=('5008') #ASC.Files/client
PORTS+=('5013') #ASC.Files/editor
PORTS+=('5014') #ASC.CRM
setsebool -P httpd_can_network_connect on
;;
disabled)
@ -549,8 +528,6 @@ change_elasticsearch_config(){
if [ -d /etc/elasticsearch/ ]; then
chmod g+ws /etc/elasticsearch/
fi
systemctl start elasticsearch
}
setup_elasticsearch() {
@ -566,7 +543,8 @@ setup_elasticsearch() {
setup_kafka() {
KAFKA_SERVICE=$(systemctl list-units --no-legend "*kafka*" | cut -f1 -d' ')
KAFKA_SERVICE=$(systemctl list-unit-files --no-legend "*kafka*" | grep -oE '[^ ]+.service')
ZOOKEEPER_SERVICE=$(systemctl list-unit-files --no-legend "*zookeeper*" | grep -oE '[^ ]+.service')
if [ -n ${KAFKA_SERVICE} ]; then
@ -582,7 +560,9 @@ setup_kafka() {
sed -i "s/bootstrap.servers=.*/bootstrap.servers=${KAFKA_HOST}:${KAFKA_PORT}/g" $KAFKA_CONF/connect-standalone.properties
sed -i "s/logger.kafka.controller=.*,/logger.kafka.controller=INFO,/g" $KAFKA_CONF/log4j.properties
sed -i "s/logger.state.change.logger=.*,/logger.state.change.logger=INFO,/g" $KAFKA_CONF/log4j.properties
if ! grep -q "DefaultEventHandler" $KAFKA_CONF/log4j.properties; then
echo "log4j.logger.kafka.producer.async.DefaultEventHandler=INFO, kafkaAppender" >> $KAFKA_CONF/log4j.properties
fi
#Save kafka parameters in .json
$JSON_USERCONF "this.kafka={'BootstrapServers': \"${KAFKA_HOST}:${KAFKA_PORT}\"}" >/dev/null 2>&1
@ -607,7 +587,6 @@ install_json
if $PACKAGE_MANAGER mysql-client >/dev/null 2>&1 || $PACKAGE_MANAGER mysql-community-client >/dev/null 2>&1; then
input_db_params
establish_mysql_conn || exit $?
execute_mysql_script || exit $?
fi
if $PACKAGE_MANAGER nginx >/dev/null 2>&1; then

View File

@ -39,6 +39,7 @@ LOG_DIR="/var/log/onlyoffice/${PRODUCT}"
DOTNET_RUN="/usr/share/dotnet/dotnet"
APP_URLS="http://0.0.0.0"
ENVIRONMENT=" --ENVIRONMENT=production"
CORE=" --core:products:folder=${BASE_DIR}/products --core:products:subfolder=server"
SERVICE_NAME=(
api
@ -55,11 +56,7 @@ SERVICE_NAME=(
backup
storage-encryption
storage-migration
projects-server
telegram-service
crm
calendar
mail
ssoauth
)
@ -94,13 +91,11 @@ reassign_values (){
SERVICE_PORT="5006"
WORK_DIR="${BASE_DIR}/services/ASC.Studio.Notify/"
EXEC_FILE="ASC.Studio.Notify.dll"
CORE=" --core:products:folder=${BASE_DIR}/products --core:products:subfolder=server "
;;
notify )
SERVICE_PORT="5005"
WORK_DIR="${BASE_DIR}/services/ASC.Notify/"
EXEC_FILE="ASC.Notify.dll"
CORE=" --core:products:folder=${BASE_DIR}/products --core:products:subfolder=server "
;;
people-server )
SERVICE_PORT="5004"
@ -116,7 +111,6 @@ reassign_values (){
SERVICE_PORT="5009"
WORK_DIR="${BASE_DIR}/products/ASC.Files/service/"
EXEC_FILE="ASC.Files.Service.dll"
CORE=" --core:products:folder=${BASE_DIR}/products --core:products:subfolder=server"
;;
studio )
SERVICE_PORT="5003"
@ -127,7 +121,6 @@ reassign_values (){
SERVICE_PORT="5012"
WORK_DIR="${BASE_DIR}/services/ASC.Data.Backup/"
EXEC_FILE="ASC.Data.Backup.dll"
CORE=" --core:products:folder=${BASE_DIR}/products --core:products:subfolder=server"
;;
storage-migration )
SERVICE_PORT="5018"
@ -139,31 +132,11 @@ reassign_values (){
WORK_DIR="${BASE_DIR}/services/ASC.Data.Storage.Encryption/"
EXEC_FILE="ASC.Data.Storage.Encryption.dll"
;;
projects-server )
SERVICE_PORT="5020"
WORK_DIR="${BASE_DIR}/products/ASC.Projects/server/"
EXEC_FILE="ASC.Projects.dll"
;;
telegram-service )
SERVICE_PORT="51702"
WORK_DIR="${BASE_DIR}/services/ASC.TelegramService/"
EXEC_FILE="ASC.TelegramService.dll"
;;
crm )
SERVICE_PORT="5021"
WORK_DIR="${BASE_DIR}/products/ASC.CRM/server/"
EXEC_FILE="ASC.CRM.dll"
;;
calendar )
SERVICE_PORT="5023"
WORK_DIR="${BASE_DIR}/products/ASC.Calendar/server/"
EXEC_FILE="ASC.Calendar.dll"
;;
mail )
SERVICE_PORT="5022"
WORK_DIR="${BASE_DIR}/products/ASC.Mail/server/"
EXEC_FILE="ASC.Mail.dll"
;;
ssoauth )
SERVICE_PORT="9833"
WORK_DIR="${BASE_DIR}/services/ASC.SsoAuth.Svc/"
@ -173,7 +146,6 @@ reassign_values (){
SERVICE_NAME="$1"
EXEC_START="${DOTNET_RUN} ${WORK_DIR}${EXEC_FILE} --urls=${APP_URLS}:${SERVICE_PORT} --pathToConf=${PATH_TO_CONF} \
--'\$STORAGE_ROOT'=${STORAGE_ROOT} --log:dir=${LOG_DIR} --log:name=${SERVICE_NAME}${CORE}${ENVIRONMENT}"
CORE=""
}
write_to_file () {

View File

@ -1,3 +1,2 @@
../../../config/*.json etc/onlyoffice/appserver
../../../config/*.config etc/onlyoffice/appserver
../docker/config/*.sql var/www/appserver/sql

View File

@ -18,4 +18,4 @@ if ! cat /etc/passwd | grep -q "nginx:"; then
fi
usermod -aG onlyoffice,nginx onlyoffice
chown onlyoffice:onlyoffice /var/log/onlyoffice/appserver /var/www/appserver /etc/onlyoffice/appserver
chown -R onlyoffice:onlyoffice /var/log/onlyoffice/appserver /var/www/appserver /etc/onlyoffice/appserver

View File

@ -1,17 +1,9 @@
## COPY PUBLIC ##
../../../config/nginx/onlyoffice*.conf etc/nginx/conf.d
../../../config/nginx/includes/onlyoffice*.conf etc/nginx/includes
../../../public/* var/www/appserver/public
../../../public/images/* var/www/appserver/public/images
../../../public/offline/* var/www/appserver/public/offline
../../../public/thirdparty/* var/www/appserver/public/thirdparty
../../../products/ASC.Calendar/Client/dist/* var/www/appserver/products/ASC.Calendar/client
../../../products/ASC.CRM/Client/dist/* var/www/appserver/products/ASC.CRM/client
../../../products/ASC.Projects/Client/dist/* var/www/appserver/products/ASC.Projects/client
../../../products/ASC.Calendar/Client/dist/* var/www/appserver/products/ASC.Calendar/client
../../../products/ASC.People/Client/dist/* var/www/appserver/products/ASC.People/client
../../../products/ASC.Mail/Client/dist/* var/www/appserver/products/ASC.Mail/client
../../../products/ASC.Files/Client/dist/* var/www/appserver/products/ASC.Files/client
../../../web/ASC.Web.Editor/dist/* var/www/appserver/products/ASC.Files/editor
../../../web/ASC.Web.Client/dist/* var/www/appserver/studio/client
../../../web/ASC.Web.Login/dist/* var/www/appserver/studio/login
../../../build/deploy/public/* var/www/appserver/public
../../../build/deploy/products/ASC.People/client/* var/www/appserver/products/ASC.People/client
../../../build/deploy/products/ASC.Files/client/* var/www/appserver/products/ASC.Files/client
../../../build/deploy/products/ASC.Files/editor/* var/www/appserver/products/ASC.Files/editor
../../../build/deploy/studio/client/* var/www/appserver/studio/client
../../../build/deploy/studio/login/* var/www/appserver/studio/login

View File

@ -1,4 +1,4 @@
appserver (0.1-10) unstable; urgency=medium
appserver ({{package_header_tag_version}}) unstable; urgency=medium
* Initial Release.

View File

@ -2,34 +2,30 @@ Source: appserver
Section: web
Priority: optional
Maintainer: onlyoffice
Build-Depends: debhelper (>= 10), nodejs (>=10), dotnet-sdk-5.0, yarn
Standards-Version: 0.1-10
Build-Depends: debhelper (>= 10), nodejs (>=14), dotnet-sdk-6.0, yarn
Standards-Version: {{package_header_tag_version}}
Homepage: https://www.onlyoffice.com/
Architecture: any
Package: appserver
Architecture: any
Depends: appserver-api-system,
appserver-backup,
appserver-calendar,
appserver-crm,
appserver-storage-encryption,
appserver-files,
appserver-files-services,
appserver-mail,
appserver-storage-migration,
appserver-notify,
appserver-people-server,
appserver-projects-server,
appserver-socket,
appserver-ssoauth,
appserver-studio-notify,
appserver-telegram-service,
appserver-thumbnails,
appserver-urlshortener,
appserver-api,
appserver-studio,
appserver-proxy
Depends: appserver-api-system (= {{package_header_tag_version}}),
appserver-backup (= {{package_header_tag_version}}),
appserver-storage-encryption (= {{package_header_tag_version}}),
appserver-files (= {{package_header_tag_version}}),
appserver-files-services (= {{package_header_tag_version}}),
appserver-storage-migration (= {{package_header_tag_version}}),
appserver-notify (= {{package_header_tag_version}}),
appserver-people-server (= {{package_header_tag_version}}),
appserver-socket (= {{package_header_tag_version}}),
appserver-ssoauth (= {{package_header_tag_version}}),
appserver-studio-notify (= {{package_header_tag_version}}),
appserver-telegram-service (= {{package_header_tag_version}}),
appserver-thumbnails (= {{package_header_tag_version}}),
appserver-urlshortener (= {{package_header_tag_version}}),
appserver-api (= {{package_header_tag_version}}),
appserver-studio (= {{package_header_tag_version}}),
appserver-proxy (= {{package_header_tag_version}})
Description: Description
Package: appserver-common
@ -44,174 +40,138 @@ Description: Description
Package: appserver-api-system
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
dotnet-sdk-6.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-backup
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-calendar
Architecture: any
Depends: appserver-common,
appserver-configuration,
dotnet-sdk-5.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-crm
Architecture: any
Depends: appserver-common,
appserver-configuration,
dotnet-sdk-5.0,
dotnet-sdk-6.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-storage-encryption
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
dotnet-sdk-6.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-files
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
dotnet-sdk-6.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-files-services
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-mail
Architecture: any
Depends: appserver-common,
appserver-configuration,
dotnet-sdk-5.0,
dotnet-sdk-6.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-storage-migration
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
dotnet-sdk-6.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-notify
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
dotnet-sdk-6.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-people-server
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-projects-server
Architecture: any
Depends: appserver-common,
appserver-configuration,
dotnet-sdk-5.0,
dotnet-sdk-6.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-socket
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
nodejs (>=10),
dotnet-sdk-6.0,
nodejs (>=14),
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-studio-notify
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
dotnet-sdk-6.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-telegram-service
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
dotnet-sdk-6.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-thumbnails
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
nodejs (>=10),
dotnet-sdk-6.0,
nodejs (>=14),
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-urlshortener
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
nodejs (>=10),
dotnet-sdk-6.0,
nodejs (>=14),
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-api
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
dotnet-sdk-6.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
Package: appserver-studio
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
dotnet-sdk-6.0,
${misc:Depends},
${shlibs:Depends}
Description: Description
@ -223,10 +183,10 @@ Description: Description
Package: appserver-ssoauth
Architecture: any
Depends: appserver-common,
Depends: appserver-common (= {{package_header_tag_version}}),
appserver-configuration,
dotnet-sdk-5.0,
nodejs (>=10),
dotnet-sdk-6.0,
nodejs (>=14),
${misc:Depends},
${shlibs:Depends}
Description: Description

View File

@ -42,8 +42,8 @@ override_dh_fixperms:
dh_fixperms
override_dh_auto_install:
dh_installinit
dh_systemd_enable
dh_installinit --no-start
dh_systemd_enable --no-enable
dh_systemd_start --no-start
override_dh_strip:

View File

@ -74,4 +74,6 @@ sed -i "s!\"Threads\".*!\"Threads\": \"${ELK_THREADS}\"!g" ${PATH_TO_CONF}/elast
sed -i "s!\"subfolder\".*!\"subfolder\": \"server\",!g" ${PATH_TO_CONF}/appsettings.services.json
sed -i "s!\"BootstrapServers\".*!\"BootstrapServers\": \"${KAFKA_HOST}\"!g" ${PATH_TO_CONF}/kafka.${APP_DOTNET_ENV}.json
sed -i "s!\"path\".*!\"path\": \"../../ASC.Socket.IO\"!g" ${PATH_TO_CONF}/socket.${APP_DOTNET_ENV}.json
dotnet ${DOTNET_RUN} --urls=${URLS} --ENVIRONMENT=${APP_DOTNET_ENV} --'$STORAGE_ROOT'=${APP_STORAGE_ROOT} --pathToConf=${PATH_TO_CONF} --log:dir=${LOG_DIR} --log:name=${DOTNET_LOG_NAME} ${PARAMETERS}

View File

@ -18,33 +18,28 @@ License: AGPLv3
Source0: https://github.com/ONLYOFFICE/%{product}/archive/%GIT_BRANCH.tar.gz#/%{sourcename}.tar.gz
Source1: https://github.com/ONLYOFFICE/document-templates/archive/main/community-server.tar.gz#/document-templates-main-community-server.tar.gz
Source2: https://github.com/ONLYOFFICE/dictionaries/archive/master.tar.gz#/dictionaries-master.tar.gz
Source3: https://github.com/ONLYOFFICE/CommunityServer/archive/master.tar.gz#/CommunityServer-master.tar.gz
BuildRequires: nodejs >= 12.0
BuildRequires: nodejs >= 14.0
BuildRequires: yarn
BuildRequires: dotnet-sdk-5.0
BuildRequires: dotnet-sdk-6.0
Requires: %name-api-system
Requires: %name-calendar
Requires: %name-crm
Requires: %name-backup
Requires: %name-storage-encryption
Requires: %name-storage-migration
Requires: %name-files
Requires: %name-files-services
Requires: %name-mail
Requires: %name-notify
Requires: %name-people-server
Requires: %name-projects-server
Requires: %name-socket
Requires: %name-ssoauth
Requires: %name-studio-notify
Requires: %name-telegram-service
Requires: %name-thumbnails
Requires: %name-urlshortener
Requires: %name-api
Requires: %name-studio
Requires: %name-proxy
Requires: %name-api-system = %version-%release
Requires: %name-backup = %version-%release
Requires: %name-storage-encryption = %version-%release
Requires: %name-storage-migration = %version-%release
Requires: %name-files = %version-%release
Requires: %name-files-services = %version-%release
Requires: %name-notify = %version-%release
Requires: %name-people-server = %version-%release
Requires: %name-socket = %version-%release
Requires: %name-ssoauth = %version-%release
Requires: %name-studio-notify = %version-%release
Requires: %name-telegram-service = %version-%release
Requires: %name-thumbnails = %version-%release
Requires: %name-urlshortener = %version-%release
Requires: %name-api = %version-%release
Requires: %name-studio = %version-%release
Requires: %name-proxy = %version-%release
%description
App Server is a platform for building your own online office by connecting ONLYOFFICE modules packed as separate apps.
@ -54,10 +49,9 @@ App Server is a platform for building your own online office by connecting ONLYO
%prep
rm -rf %{_rpmdir}/%{_arch}/%{name}-*
%setup -b1 -b2 -b3 -n %{sourcename}
%setup -b1 -b2 -n %{sourcename}
mv -f %{_builddir}/document-templates-main-community-server/* %{_builddir}/%{sourcename}/products/ASC.Files/Server/DocStore/
mv -f %{_builddir}/dictionaries-master/* %{_builddir}/%{sourcename}/common/Tests/Frontend.Translations.Tests/dictionaries/
mv -f %{_builddir}/CommunityServer-master/build/sql/* %{_builddir}/%{sourcename}/build/install/docker/config/
%include build.spec

View File

@ -6,26 +6,18 @@
%{buildpath}/studio/api/
%{buildpath}/products/ASC.People/server/ASC.People.dll
%{buildpath}/products/ASC.Files/server/ASC.Files*.dll
%{buildpath}/products/ASC.CRM/server/ASC.CRM*.dll
%{buildpath}/products/ASC.Projects/server/ASC.Projects*.dll
/lib/systemd/system/%{product}-api.service
%dir %{buildpath}/studio/
%dir %{buildpath}/products/ASC.People/
%dir %{buildpath}/products/ASC.People/server/
%dir %{buildpath}/products/ASC.Files/
%dir %{buildpath}/products/ASC.Files/server/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.CRM/server/
%dir %{buildpath}/products/ASC.Projects/
%dir %{buildpath}/products/ASC.Projects/server/
%files backup
%defattr(-, onlyoffice, onlyoffice, -)
%{buildpath}/services/ASC.Data.Backup/
%{buildpath}/products/ASC.People/server/ASC.People.dll
%{buildpath}/products/ASC.Files/server/ASC.Files*.dll
%{buildpath}/products/ASC.CRM/server/ASC.CRM*.dll
%{buildpath}/products/ASC.Projects/server/ASC.Projects*.dll
/lib/systemd/system/%{product}-backup.service
%dir %{buildpath}/services/
%dir %{buildpath}/products/
@ -33,16 +25,11 @@
%dir %{buildpath}/products/ASC.People/server/
%dir %{buildpath}/products/ASC.Files/
%dir %{buildpath}/products/ASC.Files/server/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.CRM/server/
%dir %{buildpath}/products/ASC.Projects/
%dir %{buildpath}/products/ASC.Projects/server/
%files common
%defattr(-, onlyoffice, onlyoffice, -)
%config %{_sysconfdir}/onlyoffice/%{product}/
%{_var}/log/onlyoffice/%{product}/
%{buildpath}/sql/
%dir %{_sysconfdir}/onlyoffice/
%dir %{_var}/log/onlyoffice/
@ -51,26 +38,18 @@
%{buildpath}/products/ASC.Files/service/
%{buildpath}/products/ASC.Files/server/ASC.Files*.dll
%{buildpath}/products/ASC.People/server/ASC.People*.dll
%{buildpath}/products/ASC.CRM/server/ASC.CRM*.dll
%{buildpath}/products/ASC.Projects/server/ASC.Projects*.dll
/lib/systemd/system/%{product}-files-services.service
%dir %{buildpath}/products/
%dir %{buildpath}/products/ASC.People/
%dir %{buildpath}/products/ASC.People/server
%dir %{buildpath}/products/ASC.Files/
%dir %{buildpath}/products/ASC.Files/server/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.CRM/server/
%dir %{buildpath}/products/ASC.Projects/
%dir %{buildpath}/products/ASC.Projects/server/
%files notify
%defattr(-, onlyoffice, onlyoffice, -)
%{buildpath}/services/ASC.Notify/
%{buildpath}/products/ASC.People/server/ASC.People.dll
%{buildpath}/products/ASC.Files/server/ASC.Files*.dll
%{buildpath}/products/ASC.CRM/server/ASC.CRM*.dll
%{buildpath}/products/ASC.Projects/server/ASC.Projects*.dll
/lib/systemd/system/%{product}-notify.service
%dir %{buildpath}/services/
%dir %{buildpath}/products/
@ -78,26 +57,16 @@
%dir %{buildpath}/products/ASC.People/server/
%dir %{buildpath}/products/ASC.Files/
%dir %{buildpath}/products/ASC.Files/server/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.CRM/server/
%dir %{buildpath}/products/ASC.Projects/
%dir %{buildpath}/products/ASC.Projects/server/
%files files
%defattr(-, onlyoffice, onlyoffice, -)
%{buildpath}/products/ASC.Files/server/
%{buildpath}/products/ASC.People/server/ASC.People.dll
%{buildpath}/products/ASC.CRM/server/ASC.CRM*.dll
%{buildpath}/products/ASC.Projects/server/ASC.Projects*.dll
/lib/systemd/system/%{product}-files.service
%dir %{buildpath}/products/
%dir %{buildpath}/products/ASC.Files/
%dir %{buildpath}/products/ASC.People/
%dir %{buildpath}/products/ASC.People/server/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.CRM/server/
%dir %{buildpath}/products/ASC.Projects/
%dir %{buildpath}/products/ASC.Projects/server/
%files api-system
%defattr(-, onlyoffice, onlyoffice, -)
@ -115,26 +84,16 @@
%{buildpath}/products/ASC.People/client/
%{buildpath}/products/ASC.Files/client/
%{buildpath}/products/ASC.Files/editor/
%{buildpath}/products/ASC.CRM/client/
%{buildpath}/products/ASC.Projects/client/
%{buildpath}/products/ASC.Calendar/client/
%{buildpath}/products/ASC.Mail/client/
%dir %{buildpath}/studio/
%dir %{buildpath}/products/
%dir %{buildpath}/products/ASC.People/
%dir %{buildpath}/products/ASC.Files/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.Projects/
%dir %{buildpath}/products/ASC.Calendar/
%dir %{buildpath}/products/ASC.Mail/
%files studio-notify
%defattr(-, onlyoffice, onlyoffice, -)
%{buildpath}/services/ASC.Studio.Notify/
%{buildpath}/products/ASC.People/server/ASC.People.dll
%{buildpath}/products/ASC.Files/server/ASC.Files*.dll
%{buildpath}/products/ASC.CRM/server/ASC.CRM*.dll
%{buildpath}/products/ASC.Projects/server/ASC.Projects*.dll
/lib/systemd/system/%{product}-studio-notify.service
%dir %{buildpath}/services/
%dir %{buildpath}/products/
@ -142,26 +101,16 @@
%dir %{buildpath}/products/ASC.People/server/
%dir %{buildpath}/products/ASC.Files/
%dir %{buildpath}/products/ASC.Files/server/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.CRM/server/
%dir %{buildpath}/products/ASC.Projects/
%dir %{buildpath}/products/ASC.Projects/server/
%files people-server
%defattr(-, onlyoffice, onlyoffice, -)
%{buildpath}/products/ASC.People/server/
%{buildpath}/products/ASC.Files/server/ASC.Files*.dll
%{buildpath}/products/ASC.CRM/server/ASC.CRM*.dll
%{buildpath}/products/ASC.Projects/server/ASC.Projects*.dll
/lib/systemd/system/%{product}-people-server.service
%dir %{buildpath}/products/
%dir %{buildpath}/products/ASC.People/
%dir %{buildpath}/products/ASC.Files/
%dir %{buildpath}/products/ASC.Files/server/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.CRM/server/
%dir %{buildpath}/products/ASC.Projects/
%dir %{buildpath}/products/ASC.Projects/server/
%files urlshortener
%defattr(-, onlyoffice, onlyoffice, -)
@ -183,23 +132,17 @@
%{buildpath}/services/ASC.Socket.IO.Svc/
%{buildpath}/products/ASC.Files/server/ASC.Files*.dll
%{buildpath}/products/ASC.People/server/ASC.People.dll
%{buildpath}/products/ASC.CRM/server/ASC.CRM*.dll
%{buildpath}/products/ASC.Projects/server/ASC.Projects*.dll
/lib/systemd/system/%{product}-socket.service
%dir %{buildpath}/services/
%dir %{buildpath}/products/
%dir %{buildpath}/products/ASC.Files/
%dir %{buildpath}/products/ASC.People/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.Projects/
%files studio
%defattr(-, onlyoffice, onlyoffice, -)
%{buildpath}/studio/server/
%{buildpath}/products/ASC.People/server/ASC.People.dll
%{buildpath}/products/ASC.Files/server/ASC.Files*.dll
%{buildpath}/products/ASC.CRM/server/ASC.CRM*.dll
%{buildpath}/products/ASC.Projects/server/ASC.Projects*.dll
/lib/systemd/system/%{product}-studio.service
%dir %{buildpath}/studio/
%dir %{buildpath}/products/
@ -207,18 +150,12 @@
%dir %{buildpath}/products/ASC.People/server/
%dir %{buildpath}/products/ASC.Files/
%dir %{buildpath}/products/ASC.Files/server/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.CRM/server/
%dir %{buildpath}/products/ASC.Projects/
%dir %{buildpath}/products/ASC.Projects/server/
%files storage-encryption
%defattr(-, onlyoffice, onlyoffice, -)
%{buildpath}/services/ASC.Data.Storage.Encryption/
%{buildpath}/products/ASC.Files/server/ASC.Files*.dll
%{buildpath}/products/ASC.People/server/ASC.People.dll
%{buildpath}/products/ASC.CRM/server/ASC.CRM*.dll
%{buildpath}/products/ASC.Projects/server/ASC.Projects*.dll
/lib/systemd/system/%{product}-storage-encryption.service
%dir %{buildpath}/services/
%dir %{buildpath}/products/
@ -226,49 +163,23 @@
%dir %{buildpath}/products/ASC.Files/server
%dir %{buildpath}/products/ASC.People/
%dir %{buildpath}/products/ASC.People/server
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.CRM/server
%dir %{buildpath}/products/ASC.Projects/
%dir %{buildpath}/products/ASC.Projects/server
%files storage-migration
%defattr(-, onlyoffice, onlyoffice, -)
%{buildpath}/services/ASC.Data.Storage.Migration/
%{buildpath}/products/ASC.Files/server/ASC.Files*.dll
%{buildpath}/products/ASC.People/server/ASC.People.dll
%{buildpath}/products/ASC.CRM/server/ASC.CRM*.dll
%{buildpath}/products/ASC.Projects/server/ASC.Projects*.dll
/lib/systemd/system/%{product}-storage-migration.service
%dir %{buildpath}/services/
%dir %{buildpath}/products/
%dir %{buildpath}/products/ASC.Files/
%dir %{buildpath}/products/ASC.People/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.Projects/
%files projects-server
%defattr(-, onlyoffice, onlyoffice, -)
%{buildpath}/products/ASC.Projects/server/
%{buildpath}/products/ASC.Files/server/ASC.Files*.dll
%{buildpath}/products/ASC.People/server/ASC.People.dll
%{buildpath}/products/ASC.CRM/server/ASC.CRM*.dll
/lib/systemd/system/%{product}-projects-server.service
%dir %{buildpath}/products/
%dir %{buildpath}/products/ASC.Files/
%dir %{buildpath}/products/ASC.Files/server/
%dir %{buildpath}/products/ASC.People/
%dir %{buildpath}/products/ASC.People/server/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.CRM/server/
%dir %{buildpath}/products/ASC.Projects/
%files telegram-service
%defattr(-, onlyoffice, onlyoffice, -)
%{buildpath}/services/ASC.TelegramService/
%{buildpath}/products/ASC.Files/server/ASC.Files*.dll
%{buildpath}/products/ASC.People/server/ASC.People.dll
%{buildpath}/products/ASC.CRM/server/ASC.CRM*.dll
%{buildpath}/products/ASC.Projects/server/ASC.Projects*.dll
/lib/systemd/system/%{product}-telegram-service.service
%dir %{buildpath}/services/
%dir %{buildpath}/products/
@ -276,38 +187,6 @@
%dir %{buildpath}/products/ASC.Files/server/
%dir %{buildpath}/products/ASC.People/
%dir %{buildpath}/products/ASC.People/server/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.CRM/server/
%dir %{buildpath}/products/ASC.Projects/
%dir %{buildpath}/products/ASC.Projects/server/
%files crm
%defattr(-, onlyoffice, onlyoffice, -)
%{buildpath}/products/ASC.CRM/server/
%{buildpath}/products/ASC.Files/server/ASC.Files*.dll
%{buildpath}/products/ASC.People/server/ASC.People.dll
%{buildpath}/products/ASC.Projects/server/ASC.Projects*.dll
/lib/systemd/system/%{product}-crm.service
%dir %{buildpath}/products/
%dir %{buildpath}/products/ASC.CRM/
%dir %{buildpath}/products/ASC.Files/
%dir %{buildpath}/products/ASC.Files/server/
%dir %{buildpath}/products/ASC.Projects/
%dir %{buildpath}/products/ASC.Projects/server/
%files calendar
%defattr(-, onlyoffice, onlyoffice, -)
%{buildpath}/products/ASC.Calendar/server/
/lib/systemd/system/%{product}-calendar.service
%dir %{buildpath}/products/
%dir %{buildpath}/products/ASC.Calendar/
%files mail
%defattr(-, onlyoffice, onlyoffice, -)
%{buildpath}/products/ASC.Mail/server/
/lib/systemd/system/%{product}-mail.service
%dir %{buildpath}/products/
%dir %{buildpath}/products/ASC.Mail/
%files ssoauth
%defattr(-, onlyoffice, onlyoffice, -)

View File

@ -7,20 +7,12 @@ mkdir -p "%{buildroot}%{_sysconfdir}/onlyoffice/%{product}/.private/"
mkdir -p "%{buildroot}%{_sysconfdir}/onlyoffice/%{product}/data/"
mkdir -p "%{buildroot}%{_var}/log/onlyoffice/%{product}/"
mkdir -p "%{buildroot}/lib/systemd/system/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.Calendar/client/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.Calendar/server/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.CRM/client/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.CRM/server/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.Files/client/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.Files/editor/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.Files/server/DocStore/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.Files/service/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.Mail/client/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.Mail/server/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.People/client/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.People/server/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.Projects/client/"
mkdir -p "%{buildroot}%{buildpath}/products/ASC.Projects/server/"
mkdir -p "%{buildroot}%{buildpath}/public/"
mkdir -p "%{buildroot}%{buildpath}/services/ASC.Socket.IO/"
mkdir -p "%{buildroot}%{buildpath}/services/ASC.Socket.IO.Svc/"
@ -37,18 +29,13 @@ mkdir -p "%{buildroot}%{buildpath}/services/ASC.Thumbnails/"
mkdir -p "%{buildroot}%{buildpath}/services/ASC.UrlShortener/"
mkdir -p "%{buildroot}%{buildpath}/services/ASC.UrlShortener.Svc/"
mkdir -p "%{buildroot}%{buildpath}/services/ASC.Thumbnails.Svc/"
mkdir -p "%{buildroot}%{buildpath}/sql/"
mkdir -p "%{buildroot}%{buildpath}/studio/api/"
mkdir -p "%{buildroot}%{buildpath}/studio/client/"
mkdir -p "%{buildroot}%{buildpath}/studio/login/"
mkdir -p "%{buildroot}%{buildpath}/studio/server/"
cp -rf %{_builddir}/%{sourcename}/publish/products/ASC.Calendar/server/* "%{buildroot}%{buildpath}/products/ASC.Calendar/server/"
cp -rf %{_builddir}/%{sourcename}/publish/products/ASC.Mail/server/* "%{buildroot}%{buildpath}/products/ASC.Mail/server/"
cp -rf %{_builddir}/%{sourcename}/publish/products/ASC.CRM/server/* "%{buildroot}%{buildpath}/products/ASC.CRM/server/"
cp -rf %{_builddir}/%{sourcename}/publish/products/ASC.Files/server/* "%{buildroot}%{buildpath}/products/ASC.Files/server/"
cp -rf %{_builddir}/%{sourcename}/publish/services/ASC.Files.Service/service/* "%{buildroot}%{buildpath}/products/ASC.Files/service/"
cp -rf %{_builddir}/%{sourcename}/publish/products/ASC.People/server/* "%{buildroot}%{buildpath}/products/ASC.People/server/"
cp -rf %{_builddir}/%{sourcename}/publish/products/ASC.Projects/server/* "%{buildroot}%{buildpath}/products/ASC.Projects/server/"
cp -rf %{_builddir}/%{sourcename}/publish/services/ASC.ApiSystem/service/* "%{buildroot}%{buildpath}/services/ASC.ApiSystem/"
cp -rf %{_builddir}/%{sourcename}/publish/services/ASC.Data.Backup/service/* "%{buildroot}%{buildpath}/services/ASC.Data.Backup/"
cp -rf %{_builddir}/%{sourcename}/publish/services/ASC.Notify/service/* "%{buildroot}%{buildpath}/services/ASC.Notify/"
@ -67,19 +54,14 @@ cp -rf %{_builddir}/%{sourcename}/publish/services/ASC.UrlShortener.Svc/service/
cp -rf %{_builddir}/%{sourcename}/publish/services/ASC.Web.Api/service/* "%{buildroot}%{buildpath}/studio/api/"
cp -rf %{_builddir}/%{sourcename}/publish/services/ASC.Web.Studio/service/* "%{buildroot}%{buildpath}/studio/server/"
cp -rf %{_builddir}/%{sourcename}/build/install/common/systemd/modules/* "%{buildroot}/lib/systemd/system/"
cp -rf %{_builddir}/%{sourcename}/build/install/docker/config/*.sql "%{buildroot}%{buildpath}/sql/"
cp -rf %{_builddir}/%{sourcename}/build/install/common/%{product}-configuration.sh "%{buildroot}%{_bindir}/"
cp -rf %{_builddir}/%{sourcename}/config/* "%{buildroot}%{_sysconfdir}/onlyoffice/%{product}/"
cp -rf %{_builddir}/%{sourcename}/config/nginx/includes/onlyoffice*.conf "%{buildroot}%{_sysconfdir}/nginx/includes/"
cp -rf %{_builddir}/%{sourcename}/config/nginx/onlyoffice*.conf "%{buildroot}%{_sysconfdir}/nginx/conf.d/"
cp -rf %{_builddir}/%{sourcename}/products/ASC.CRM/Client/dist/* "%{buildroot}%{buildpath}/products/ASC.CRM/client/"
cp -rf %{_builddir}/%{sourcename}/products/ASC.Files/Client/dist/* "%{buildroot}%{buildpath}/products/ASC.Files/client/"
cp -rf %{_builddir}/%{sourcename}/build/deploy/products/ASC.Files/client/* "%{buildroot}%{buildpath}/products/ASC.Files/client/"
cp -rf %{_builddir}/%{sourcename}/products/ASC.Files/Server/DocStore/* "%{buildroot}%{buildpath}/products/ASC.Files/server/DocStore/"
cp -rf %{_builddir}/%{sourcename}/products/ASC.People/Client/dist/* "%{buildroot}%{buildpath}/products/ASC.People/client/"
cp -rf %{_builddir}/%{sourcename}/products/ASC.Projects/Client/dist/* "%{buildroot}%{buildpath}/products/ASC.Projects/client/"
cp -rf %{_builddir}/%{sourcename}/products/ASC.Calendar/Client/dist/* "%{buildroot}%{buildpath}/products/ASC.Calendar/client/"
cp -rf %{_builddir}/%{sourcename}/products/ASC.Mail/Client/dist/* "%{buildroot}%{buildpath}/products/ASC.Mail/client/"
cp -rf %{_builddir}/%{sourcename}/public/* "%{buildroot}%{buildpath}/public/"
cp -rf %{_builddir}/%{sourcename}/web/ASC.Web.Client/dist/* "%{buildroot}%{buildpath}/studio/client/"
cp -rf %{_builddir}/%{sourcename}/web/ASC.Web.Editor/dist/* "%{buildroot}%{buildpath}/products/ASC.Files/editor/"
cp -rf %{_builddir}/%{sourcename}/web/ASC.Web.Login/dist/* "%{buildroot}%{buildpath}/studio/login/"
cp -rf %{_builddir}/%{sourcename}/build/deploy/products/ASC.People/client/* "%{buildroot}%{buildpath}/products/ASC.People/client/"
cp -rf %{_builddir}/%{sourcename}/build/deploy/public/* "%{buildroot}%{buildpath}/public/"
cp -rf %{_builddir}/%{sourcename}/build/deploy/studio/client/* "%{buildroot}%{buildpath}/studio/client/"
cp -rf %{_builddir}/%{sourcename}/build/deploy/products/ASC.Files/editor/* "%{buildroot}%{buildpath}/products/ASC.Files/editor/"
cp -rf %{_builddir}/%{sourcename}/build/deploy/studio/login/* "%{buildroot}%{buildpath}/studio/login/"

View File

@ -1,8 +1,8 @@
%package backup
Summary: backup
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
AutoReqProv: no
%description backup
@ -14,39 +14,39 @@ Group: Applications/Internet
%package files-services
Summary: files-services
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
AutoReqProv: no
%description files-services
%package notify
Summary: notify
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
AutoReqProv: no
%description notify
%package files
Summary: files
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
AutoReqProv: no
%description files
%package api-system
Summary: api-system
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
AutoReqProv: no
%description api-system
%package proxy
Summary: proxy
Group: Applications/Internet
Requires: %name-common
Requires: %name-common = %version-%release
Requires: nginx >= 1.9.5
Requires: mysql-community-client >= 5.7.0
AutoReqProv: no
@ -55,24 +55,24 @@ AutoReqProv: no
%package studio-notify
Summary: studio-notify
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
AutoReqProv: no
%description studio-notify
%package people-server
Summary: people-server
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
AutoReqProv: no
%description people-server
%package urlshortener
Summary: urlshortener
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
Requires: nodejs >= 12.0
AutoReqProv: no
%description urlshortener
@ -80,8 +80,8 @@ AutoReqProv: no
%package socket
Summary: socket
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
Requires: nodejs >= 12.0
AutoReqProv: no
%description socket
@ -89,8 +89,8 @@ AutoReqProv: no
%package thumbnails
Summary: thumbnails
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
Requires: nodejs >= 12.0
AutoReqProv: no
%description thumbnails
@ -98,80 +98,48 @@ AutoReqProv: no
%package studio
Summary: studio
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
AutoReqProv: no
%description studio
%package crm
Summary: crm
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
AutoReqProv: no
%description crm
%package api
Summary: api
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
AutoReqProv: no
%description api
%package storage-encryption
Summary: storage-encryption
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
AutoReqProv: no
%description storage-encryption
%package storage-migration
Summary: storage-migration
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
AutoReqProv: no
%description storage-migration
%package projects-server
Summary: projects-server
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
AutoReqProv: no
%description projects-server
%package telegram-service
Summary: telegram-service
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
AutoReqProv: no
%description telegram-service
%package calendar
Summary: calendar
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
AutoReqProv: no
%description calendar
%package mail
Summary: mail
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
AutoReqProv: no
%description mail
%package ssoauth
Summary: ssoauth
Group: Applications/Internet
Requires: %name-common
Requires: dotnet-sdk-5.0
Requires: %name-common = %version-%release
Requires: dotnet-sdk-6.0
Requires: nodejs >= 12.0
AutoReqProv: no
%description ssoauth

View File

@ -27,14 +27,10 @@ powershell -Command "(gc build\deploy\nginx\onlyoffice.conf) -replace '#', '' |
xcopy config\nginx\sites-enabled\* build\deploy\nginx\sites-enabled\ /E /R /Y
REM fix paths
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-calendar.conf) -replace 'ROOTPATH', '%~dp0deploy\products\ASC.Calendar\client' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-calendar.conf"
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-crm.conf) -replace 'ROOTPATH', '%~dp0deploy\products\ASC.CRM\client' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-crm.conf"
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-editor.conf) -replace 'ROOTPATH', '%~dp0deploy\products\ASC.Files\editor' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-editor.conf"
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-files.conf) -replace 'ROOTPATH', '%~dp0deploy\products\ASC.Files\client' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-files.conf"
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-login.conf) -replace 'ROOTPATH', '%~dp0deploy\studio\login' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-login.conf"
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-mail.conf) -replace 'ROOTPATH', '%~dp0deploy\products\ASC.Mail\client' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-mail.conf"
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-people.conf) -replace 'ROOTPATH', '%~dp0deploy\products\ASC.People\client' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-people.conf"
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-projects.conf) -replace 'ROOTPATH', '%~dp0deploy\products\ASC.Projects\client' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-projects.conf"
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-studio.conf) -replace 'ROOTPATH', '%~dp0deploy\studio\client' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-studio.conf"
REM restart nginx

View File

@ -0,0 +1,67 @@
@echo off
PUSHD %~dp0
call runasadmin.bat "%~dpnx0"
if %errorlevel% == 0 (
PUSHD %~dp0..
echo "mode="
REM call yarn wipe
call yarn install
call yarn build:test.translation:personal
REM call yarn wipe
call yarn deploy:personal
REM copy nginx configurations to deploy folder
xcopy config\nginx\onlyoffice.conf build\deploy\nginx\ /E /R /Y
powershell -Command "(gc build\deploy\nginx\onlyoffice.conf) -replace '#', '' | Out-File -encoding ASCII build\deploy\nginx\onlyoffice.conf"
xcopy config\nginx\sites-enabled\* build\deploy\nginx\sites-enabled\ /E /R /Y
REM fix paths
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-editor.conf) -replace 'ROOTPATH', '%~dp0deploy\products\ASC.Files\editor' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-editor.conf"
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-files.conf) -replace 'ROOTPATH', '%~dp0deploy\products\ASC.Files\client' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-files.conf"
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-login.conf) -replace 'ROOTPATH', '%~dp0deploy\studio\login' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-login.conf"
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-people.conf) -replace 'ROOTPATH', '%~dp0deploy\products\ASC.People\client' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-people.conf"
powershell -Command "(gc build\deploy\nginx\sites-enabled\onlyoffice-studio.conf) -replace 'ROOTPATH', '%~dp0deploy\studio\client' -replace '\\', '/' | Out-File -encoding ASCII build\deploy\nginx\sites-enabled\onlyoffice-studio.conf"
REM restart nginx
echo service nginx stop
call sc stop nginx > nul
REM sleep 5 seconds
call ping 127.0.0.1 -n 6 > nul
echo service nginx start
call sc start nginx > nul
REM sleep 5 seconds
call ping 127.0.0.1 -n 6 > nul
call yarn e2e.test:translation:personal
exit
if NOT %errorlevel% == 0 (
echo Couldn't restarte Onlyoffice%%~nf service
)
)
echo.
POPD
if "%1"=="nopause" goto start
pause
:start

View File

@ -0,0 +1,4 @@
@echo off
PUSHD %~dp0..\..
set servicepath=%cd%\common\ASC.Migration\bin\Debug\ASC.Migration.exe urls=http://0.0.0.0:5034 $STORAGE_ROOT=%cd%\Data pathToConf=%cd%\config log:dir=%cd%\Logs log:name=migration core:products:folder=%cd%\products

10
build/run/Radicale.xml Normal file
View File

@ -0,0 +1,10 @@
<service>
<id>OnlyofficeRadicale</id>
<name>ONLYOFFICE Radicale</name>
<startmode>manual</startmode>
<executable>python</executable>
<arguments>-m radicale --config %BASE%/../../config/radicale.config</arguments>
<log mode="none"/>
<delayedAutoStart>true</delayedAutoStart>
<onfailure action="restart" delay="5 sec" />
</service>

10
build/run/WebDav.xml Normal file
View File

@ -0,0 +1,10 @@
<service>
<id>OnlyofficeWebDav</id>
<name>ONLYOFFICE WebDav Server</name>
<startmode>manual</startmode>
<executable>node</executable>
<arguments>../../common/ASC.WebDav/server/webDavServer.js</arguments>
<log mode="none"/>
<delayedAutoStart>true</delayedAutoStart>
<onfailure action="restart" delay="5 sec" />
</service>

1
build/scripts/webdav.bat Normal file
View File

@ -0,0 +1 @@
yarn install --cwd %~dp0../../common/ASC.WebDav/ --frozen-lockfile

View File

@ -121,5 +121,4 @@ global using RabbitMQ.Client;
global using StackExchange.Redis.Extensions.Core.Configuration;
global using StackExchange.Redis.Extensions.Newtonsoft;
global using ILogger = Microsoft.Extensions.Logging.ILogger;
global using LogLevel = Microsoft.Extensions.Logging.LogLevel;

View File

@ -30,6 +30,9 @@ public static partial class CommonLogger
[LoggerMessage(Level = LogLevel.Error)]
public static partial void ErrorWithException(this ILogger logger, Exception exception);
[LoggerMessage(Level = LogLevel.Error)]
public static partial void ErrorWithException(this ILogger logger, string message, Exception exception);
[LoggerMessage(Level = LogLevel.Error)]
public static partial void Error(this ILogger logger, string message);
@ -44,4 +47,7 @@ public static partial class CommonLogger
[LoggerMessage(Level = LogLevel.Warning)]
public static partial void WarningWithException(this ILogger logger, Exception exception);
[LoggerMessage(Level = LogLevel.Warning)]
public static partial void WarningWithException(this ILogger logger, string message, Exception exception);
}

View File

@ -174,6 +174,11 @@ public static class HttpRequestExtensions
|| !string.IsNullOrEmpty(request.Headers[HeaderNames.UserAgent]) && request.Headers[HeaderNames.UserAgent].ToString().Contains("SailfishOS"));
}
public static bool MobileApp(this HttpRequest request)
{
return !string.IsNullOrEmpty(request.Headers[HeaderNames.UserAgent]) && (request.Headers[HeaderNames.UserAgent].Contains("iOS") || request.Headers[HeaderNames.UserAgent].Contains("Android"));
}
public static string GetUserHostAddress(this HttpRequest request)
{
return request.HttpContext.Features.Get<IHttpConnectionFeature>()?.RemoteIpAddress.ToString();

View File

@ -439,6 +439,7 @@ public static class MimeMapping
AddMimeMapping(".otp", "application/vnd.oasis.opendocument.presentation-template");
AddMimeMapping(".ots", "application/vnd.oasis.opendocument.spreadsheet-template");
AddMimeMapping(".ott", "application/vnd.oasis.opendocument.text-template");
AddMimeMapping(".oxps", "application/oxps");
AddMimeMapping(".p", "text/x-pascal");
AddMimeMapping(".p10", "application/pkcs10");
AddMimeMapping(".p10", "application/x-pkcs10");

View File

@ -152,7 +152,7 @@ public class BaseCommonLinkUtility
var mapped = tenant.MappedDomain.ToLowerInvariant();
if (!mapped.Contains(Uri.SchemeDelimiter))
{
mapped = Uri.UriSchemeHttp + Uri.SchemeDelimiter + mapped;
mapped = result.Scheme + Uri.SchemeDelimiter + mapped;
}
result = new UriBuilder(mapped);
}

View File

@ -84,9 +84,9 @@ public class BillingClient
return payments;
}
public IDictionary<string, Tuple<Uri, Uri>> GetPaymentUrls(string portalId, string[] products, string affiliateId = null, string campaign = null, string currency = null, string language = null, string customerId = null, string quantity = null)
public IDictionary<string, Uri> GetPaymentUrls(string portalId, string[] products, string affiliateId = null, string campaign = null, string currency = null, string language = null, string customerId = null, string quantity = null)
{
var urls = new Dictionary<string, Tuple<Uri, Uri>>();
var urls = new Dictionary<string, Uri>();
var additionalParameters = new List<Tuple<string, string>>() { Tuple.Create("PaymentSystemId", AvangatePaymentSystemId.ToString()) };
if (!string.IsNullOrEmpty(affiliateId))
@ -124,49 +124,60 @@ public class BillingClient
var result = Request("GetPaymentUrl", portalId, parameters);
var paymentUrls = JsonSerializer.Deserialize<Dictionary<string, string>>(result);
var upgradeUrls = new Dictionary<string, string>();
if (!string.IsNullOrEmpty(portalId)
//TODO: remove
&& false)
{
try
{
//max 100 products
result = Request("GetPaymentUpgradeUrl", portalId, parameters);
upgradeUrls = JsonSerializer.Deserialize<Dictionary<string, string>>(result);
}
catch (BillingNotFoundException)
{
}
}
foreach (var p in products)
{
string url;
var paymentUrl = (Uri)null;
var upgradeUrl = (Uri)null;
if (paymentUrls.TryGetValue(p, out url))
{
url = ToUrl(url);
if (!string.IsNullOrEmpty(url))
if (paymentUrls.TryGetValue(p, out url) && !string.IsNullOrEmpty(url = ToUrl(url)))
{
paymentUrl = new Uri(url);
}
}
if (upgradeUrls.TryGetValue(p, out url))
{
url = ToUrl(url);
if (!string.IsNullOrEmpty(url))
{
upgradeUrl = new Uri(url);
}
}
urls[p] = Tuple.Create(paymentUrl, upgradeUrl);
urls[p] = paymentUrl;
}
return urls;
}
public string GetPaymentUrl(string portalId, string[] products, string affiliateId = null, string campaign = null, string currency = null, string language = null, string customerId = null, string quantity = null)
{
var additionalParameters = new List<Tuple<string, string>>() { Tuple.Create("PaymentSystemId", AvangatePaymentSystemId.ToString()) };
if (!string.IsNullOrEmpty(affiliateId))
{
additionalParameters.Add(Tuple.Create("AffiliateId", affiliateId));
}
if (!string.IsNullOrEmpty(campaign))
{
additionalParameters.Add(Tuple.Create("campaign", campaign));
}
if (!string.IsNullOrEmpty(currency))
{
additionalParameters.Add(Tuple.Create("Currency", currency));
}
if (!string.IsNullOrEmpty(language))
{
additionalParameters.Add(Tuple.Create("Language", language));
}
if (!string.IsNullOrEmpty(customerId))
{
additionalParameters.Add(Tuple.Create("CustomerID", customerId));
}
if (!string.IsNullOrEmpty(quantity))
{
additionalParameters.Add(Tuple.Create("Quantity", quantity));
}
var parameters = products
.Distinct()
.Select(p => Tuple.Create("ProductId", p))
.Concat(additionalParameters)
.ToArray();
var result = Request("GetSinglePaymentUrl", portalId, parameters);
var paymentUrl = JsonConvert.DeserializeObject<string>(result);
return paymentUrl;
}
public IDictionary<string, Dictionary<string, decimal>> GetProductPriceInfo(params string[] productIds)
{
ArgumentNullException.ThrowIfNull(productIds);

View File

@ -1,331 +0,0 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using ConfigurationManager = System.Configuration.ConfigurationManager;
using ConfigurationSection = System.Configuration.ConfigurationSection;
namespace ASC.Core.Common.Billing;
[Singletone]
public class CouponManager : IDisposable
{
private IEnumerable<AvangateProduct> _products;
private readonly IHttpClientFactory _clientFactory;
private readonly IEnumerable<string> _groups;
private readonly int _percent;
private readonly int _schedule;
private readonly string _vendorCode;
private readonly byte[] _secret;
private readonly Uri _baseAddress;
private readonly string _apiVersion;
private readonly SemaphoreSlim _semaphoreSlim;
private readonly ILogger<CouponManager> _logger;
public CouponManager(ILogger<CouponManager> logger, IHttpClientFactory clientFactory)
{
_semaphoreSlim = new SemaphoreSlim(1, 1);
_logger = logger;
_clientFactory = clientFactory;
try
{
var cfg = (AvangateCfgSectionHandler)ConfigurationManager.GetSection("avangate");
_secret = Encoding.UTF8.GetBytes(cfg.Secret);
_vendorCode = cfg.Vendor;
_percent = cfg.Percent;
_schedule = cfg.Schedule;
_baseAddress = new Uri(cfg.BaseAddress);
_apiVersion = "/rest/" + cfg.ApiVersion.TrimStart('/');
_groups = (cfg.Groups ?? "").Split(',', '|', ' ');
}
catch (Exception e)
{
_secret = Encoding.UTF8.GetBytes("");
_vendorCode = "";
_percent = AvangateCfgSectionHandler.DefaultPercent;
_schedule = AvangateCfgSectionHandler.DefaultShedule;
_baseAddress = new Uri(AvangateCfgSectionHandler.DefaultAdress);
_apiVersion = AvangateCfgSectionHandler.DefaultApiVersion;
_groups = new List<string>();
_logger.CriticalCouponManager(e);
}
}
public string CreateCoupon(TenantManager tenantManager)
{
return CreatePromotionAsync(tenantManager).Result;
}
private async Task<string> CreatePromotionAsync(TenantManager tenantManager)
{
try
{
using var httpClient = PrepaireClient();
using var content = new StringContent(await Promotion.GeneratePromotion(_logger, this, tenantManager, _percent, _schedule), Encoding.Default, "application/json");
using var response = await httpClient.PostAsync($"{_apiVersion}/promotions/", content);
if (!response.IsSuccessStatusCode)
{
throw new Exception(response.ReasonPhrase);
}
var result = await response.Content.ReadAsStringAsync();
await Task.Delay(1000 - DateTime.UtcNow.Millisecond); // otherwise authorize exception
var createdPromotion = JsonConvert.DeserializeObject<Promotion>(result);
return createdPromotion.Coupon.Code;
}
catch (Exception ex)
{
_logger.ErrorWithException(ex);
throw;
}
}
internal Task<IEnumerable<AvangateProduct>> GetProducts()
{
return _products != null ? Task.FromResult(_products) : InternalGetProducts();
}
private async Task<IEnumerable<AvangateProduct>> InternalGetProducts()
{
await _semaphoreSlim.WaitAsync();
if (_products != null)
{
_semaphoreSlim.Release();
return _products;
}
try
{
using var httpClient = PrepaireClient();
using var response = await httpClient.GetAsync($"{_apiVersion}/products/?Limit=1000&Enabled=true");
if (!response.IsSuccessStatusCode)
{
throw new Exception(response.ReasonPhrase);
}
var result = await response.Content.ReadAsStringAsync();
_logger.Debug(result);
var products = JsonConvert.DeserializeObject<List<AvangateProduct>>(result);
products = products.Where(r => r.ProductGroup != null && _groups.Contains(r.ProductGroup.Code)).ToList();
return _products = products;
}
catch (Exception ex)
{
_logger.ErrorWithException(ex);
throw;
}
finally
{
_semaphoreSlim.Release();
}
}
private HttpClient PrepaireClient()
{
const string applicationJson = "application/json";
var httpClient = _clientFactory.CreateClient();
httpClient.BaseAddress = _baseAddress;
httpClient.Timeout = TimeSpan.FromMinutes(3);
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("accept", applicationJson);
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", applicationJson);
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("X-Avangate-Authentication", CreateAuthHeader());
return httpClient;
}
private string CreateAuthHeader()
{
using var hmac = new HMACMD5(_secret);
var date = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss");
var hash = _vendorCode.Length + _vendorCode + date.Length + date;
var data = hmac.ComputeHash(Encoding.UTF8.GetBytes(hash));
var sBuilder = new StringBuilder();
foreach (var t in data)
{
sBuilder.Append(t.ToString("x2"));
}
var stringBuilder = new StringBuilder();
stringBuilder.Append($"code='{_vendorCode}' ");
stringBuilder.Append($"date='{date}' ");
stringBuilder.Append($"hash='{sBuilder}'");
return stringBuilder.ToString();
}
public void Dispose()
{
if (_semaphoreSlim != null)
{
_semaphoreSlim.Dispose();
}
}
}
class Promotion
{
public string Code { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
public bool Enabled { get; set; }
public int MaximumOrdersNumber { get; set; }
public bool InstantDiscount { get; set; }
public string ChannelType { get; set; }
public string ApplyRecurring { get; set; }
public Coupon Coupon { get; set; }
public Discount Discount { get; set; }
public IEnumerable<CouponProduct> Products { get; set; }
public int PublishToAffiliatesNetwork { get; set; }
public int AutoApply { get; set; }
public static async Task<string> GeneratePromotion(ILogger log, CouponManager couponManager, TenantManager tenantManager, int percent, int schedule)
{
try
{
var tenant = tenantManager.GetCurrentTenant();
var startDate = DateTime.UtcNow.Date;
var endDate = startDate.AddDays(schedule);
var code = tenant.Alias;
var promotion = new Promotion
{
Type = "REGULAR",
Enabled = true,
MaximumOrdersNumber = 1,
InstantDiscount = false,
ChannelType = "ECOMMERCE",
ApplyRecurring = "NONE",
PublishToAffiliatesNetwork = 0,
AutoApply = 0,
StartDate = startDate.ToString("yyyy-MM-dd"),
EndDate = endDate.ToString("yyyy-MM-dd"),
Name = string.Format("{0} {1}% off", code, percent),
Coupon = new Coupon { Type = "SINGLE", Code = code },
Discount = new Discount { Type = "PERCENT", Value = percent },
Products = (await couponManager.GetProducts()).Select(r => new CouponProduct { Code = r.ProductCode })
};
return JsonConvert.SerializeObject(promotion);
}
catch (Exception ex)
{
log.ErrorWithException(ex);
throw;
}
}
}
class Coupon
{
public string Type { get; set; }
public string Code { get; set; }
}
class Discount
{
public string Type { get; set; }
public int Value { get; set; }
}
class AvangateProduct
{
public string ProductCode { get; set; }
public string ProductName { get; set; }
public AvangateProductGroup ProductGroup { get; set; }
}
class AvangateProductGroup
{
public string Name { get; set; }
public string Code { get; set; }
}
class CouponProduct
{
public string Code { get; set; }
}
class AvangateCfgSectionHandler : ConfigurationSection
{
public const string DefaultAdress = "https://api.avangate.com/";
public const string DefaultApiVersion = "4.0";
public const int DefaultPercent = 5;
public const int DefaultShedule = 10;
[ConfigurationProperty("secret")]
public string Secret => (string)this["secret"];
[ConfigurationProperty("vendor")]
public string Vendor
{
get => (string)this["vendor"];
set => this["vendor"] = value;
}
[ConfigurationProperty("percent", DefaultValue = DefaultPercent)]
public int Percent
{
get => Convert.ToInt32(this["percent"]);
set => this["percent"] = value;
}
[ConfigurationProperty("schedule", DefaultValue = DefaultShedule)]
public int Schedule
{
get => Convert.ToInt32(this["schedule"]);
set => this["schedule"] = value;
}
[ConfigurationProperty("groups")]
public string Groups => (string)this["groups"];
[ConfigurationProperty("address", DefaultValue = DefaultAdress)]
public string BaseAddress
{
get => (string)this["address"];
set => this["address"] = value;
}
[ConfigurationProperty("apiVersion", DefaultValue = DefaultApiVersion)]
public string ApiVersion
{
get => (string)this["apiVersion"];
set => this["apiVersion"] = value;
}
}

View File

@ -34,6 +34,7 @@ public interface ITariffService
string GetButton(int tariffId, string partnerId);
Tariff GetTariff(int tenantId, bool withRequestToPaymentSystem = true);
Uri GetShoppingUri(int? tenant, int quotaId, string affiliateId, string currency = null, string language = null, string customerId = null, string quantity = null);
Uri GetShoppingUri(string[] productIds, string affiliateId = null, string currency = null, string language = null, string customerId = null, string quantity = null);
void ClearCache(int tenantId);
void DeleteDefaultBillingInfo();
void SaveButton(int tariffId, string partnerId, string buttonUrl);

View File

@ -33,30 +33,14 @@ namespace ASC.Core.Billing;
public class License
{
public string OriginalLicense { get; set; }
[JsonPropertyName("affiliate_id")]
public string AffiliateId { get; set; }
//[Obsolete]
public bool WhiteLabel { get; set; }
public bool Customization { get; set; }
public bool Branding { get; set; }
public bool SSBranding { get; set; }
[JsonPropertyName("end_date")]
public DateTime DueDate { get; set; }
[JsonPropertyName("portal_count")]
public int PortalCount { get; set; }
public bool Trial { get; set; }
[JsonPropertyName("user_quota")]
public int ActiveUsers { get; set; }
[JsonPropertyName("customer_id")]
public string CustomerId { get; set; }
public string Signature { get; set; }
public bool? DiscEncryption { get; set; }
[JsonPropertyName("users_count")]
public int DSUsersCount { get; set; }
@ -67,6 +51,9 @@ public class License
[JsonPropertyName("connections")]
public int DSConnections { get; set; }
[JsonPropertyName("signature")]
public string Signature { get; set; }
public static License Parse(string licenseString)
{
if (string.IsNullOrEmpty(licenseString))

View File

@ -44,11 +44,11 @@ public class LicenseReader
private readonly PaymentManager _paymentManager;
private readonly CoreSettings _coreSettings;
private readonly ILogger<LicenseReader> _logger;
private readonly Users.Constants _constants;
public readonly string LicensePath;
private readonly string _licensePathTemp;
public const string CustomerIdKey = "CustomerId";
public const int MaxUserCount = 10000;
public LicenseReader(
UserManager userManager,
@ -56,7 +56,8 @@ public class LicenseReader
PaymentManager paymentManager,
CoreSettings coreSettings,
LicenseReaderConfig licenseReaderConfig,
ILogger<LicenseReader> logger)
ILogger<LicenseReader> logger,
Users.Constants constants)
{
_userManager = userManager;
_tenantManager = tenantManager;
@ -65,6 +66,7 @@ public class LicenseReader
LicensePath = licenseReaderConfig.LicensePath;
_licensePathTemp = LicensePath + ".tmp";
_logger = logger;
_constants = constants;
}
public string CustomerId
@ -186,31 +188,6 @@ public class LicenseReader
throw new BillingNotConfiguredException("License not correct", license.OriginalLicense);
}
if (license.DueDate.Date < VersionReleaseDate)
{
throw new LicenseExpiredException("License expired", license.OriginalLicense);
}
if (license.ActiveUsers.Equals(default) || license.ActiveUsers < 1)
{
license.ActiveUsers = MaxUserCount;
}
if (license.ActiveUsers < _userManager.GetUsers(EmployeeStatus.Default, EmployeeType.User).Length)
{
throw new LicenseQuotaException("License quota", license.OriginalLicense);
}
if (license.PortalCount <= 0)
{
license.PortalCount = _tenantManager.GetTenantQuota(Tenant.DefaultTenant).CountPortals;
}
var activePortals = _tenantManager.GetTenants().Count;
if (activePortals > 1 && license.PortalCount < activePortals)
{
throw new LicensePortalException("License portal count", license.OriginalLicense);
}
return license.DueDate.Date;
}
@ -224,40 +201,16 @@ public class LicenseReader
var quota = new TenantQuota(-1000)
{
ActiveUsers = license.ActiveUsers,
ActiveUsers = _constants.MaxEveryoneCount,
MaxFileSize = defaultQuota.MaxFileSize,
MaxTotalSize = defaultQuota.MaxTotalSize,
Name = "license",
DocsEdition = true,
HasDomain = true,
Audit = true,
ControlPanel = true,
HealthCheck = true,
Ldap = true,
Sso = true,
Customization = license.Customization,
WhiteLabel = license.WhiteLabel || license.Customization,
Branding = license.Branding,
SSBranding = license.SSBranding,
Update = true,
Support = true,
Trial = license.Trial,
CountPortals = license.PortalCount,
DiscEncryption = true,
PrivacyRoom = true,
Restore = true,
ContentSearch = true
Trial = license.Trial
};
if (defaultQuota.Name != "overdue" && !defaultQuota.Trial)
{
quota.WhiteLabel |= defaultQuota.WhiteLabel;
quota.Branding |= defaultQuota.Branding;
quota.SSBranding |= defaultQuota.SSBranding;
quota.CountPortals = Math.Max(defaultQuota.CountPortals, quota.CountPortals);
}
_tenantManager.SaveTenantQuota(quota);
var tariff = new Tariff
@ -267,13 +220,6 @@ public class LicenseReader
};
_paymentManager.SetTariff(-1, tariff);
if (!string.IsNullOrEmpty(license.AffiliateId))
{
var tenant = _tenantManager.GetCurrentTenant();
tenant.AffiliateId = license.AffiliateId;
_tenantManager.SaveTenant(tenant);
}
}
private void LogError(Exception error)
@ -294,61 +240,4 @@ public class LicenseReader
}
}
}
private static readonly DateTime _date = DateTime.MinValue;
public DateTime VersionReleaseDate
{
get
{
// release sign is not longer requered
return _date;
//if (_date != DateTime.MinValue) return _date;
//_date = DateTime.MaxValue;
//try
//{
// var versionDate = Configuration["version:release:date"];
// var sign = Configuration["version:release:sign"];
// if (!sign.StartsWith("ASC "))
// {
// throw new Exception("sign without ASC");
// }
// var splitted = sign.Substring(4).Split(':');
// var pkey = splitted[0];
// if (pkey != versionDate)
// {
// throw new Exception("sign with different date");
// }
// var date = splitted[1];
// var orighash = splitted[2];
//var skey = MachinePseudoKeys.GetMachineConstant();
//using (var hasher = new HMACSHA1(skey))
// {
// var data = string.Join("\n", date, pkey);
// var hash = hasher.ComputeHash(Encoding.UTF8.GetBytes(data));
// if (WebEncoders.Base64UrlEncode(hash) != orighash && Convert.ToBase64String(hash) != orighash)
// {
// throw new Exception("incorrect hash");
// }
// }
// var year = int.Parse(versionDate.Substring(0, 4));
// var month = int.Parse(versionDate.Substring(4, 2));
// var day = int.Parse(versionDate.Substring(6, 2));
// _date = new DateTime(year, month, day);
//}
//catch (Exception ex)
//{
// Log.Error("VersionReleaseDate", ex);
//}
//return _date;
}
}
}

View File

@ -37,7 +37,8 @@ public class PaymentInfo
public string LName { get; set; }
public string Email { get; set; }
public DateTime PaymentDate { get; set; }
public Decimal Price { get; set; }
public decimal Price { get; set; }
public int Qty { get; set; }
public string PaymentCurrency { get; set; }
public string PaymentMethod { get; set; }
public int QuotaId { get; set; }

View File

@ -246,6 +246,28 @@ public class TariffService : ITariffService
}
catch (BillingNotFoundException)
{
var q = QuotaService.GetTenantQuota(tariff.QuotaId);
if (q != null
&& !q.Trial
&& !q.Free
&& !q.NonProfit
&& !q.Open
&& !q.Custom)
{
var asynctariff = Tariff.CreateDefault();
asynctariff.DueDate = DateTime.Today.AddDays(-1);
asynctariff.Prolongable = false;
asynctariff.Autorenewal = false;
asynctariff.State = TariffState.NotPaid;
if (SaveBillingInfo(tenantId, asynctariff))
{
asynctariff = CalculateTariff(tenantId, asynctariff);
ClearCache(tenantId);
Cache.Insert(key, asynctariff, DateTime.UtcNow.Add(GetCacheExpiration()));
}
}
}
catch (Exception error)
{
@ -351,9 +373,9 @@ public class TariffService : ITariffService
? GetBillingUrlCacheKey(tenant.Value)
: string.Format($"notenant{(!string.IsNullOrEmpty(affiliateId) ? "_" + affiliateId : "")}");
key += quota.Visible ? "" : "0";
if (Cache.Get<Dictionary<string, Tuple<Uri, Uri>>>(key) is not IDictionary<string, Tuple<Uri, Uri>> urls)
if (Cache.Get<Dictionary<string, Uri>>(key) is not IDictionary<string, Uri> urls)
{
urls = new Dictionary<string, Tuple<Uri, Uri>>();
urls = new Dictionary<string, Uri>();
if (_billingClient.Configured)
{
try
@ -386,29 +408,64 @@ public class TariffService : ITariffService
ResetCacheExpiration();
if (!string.IsNullOrEmpty(quota.AvangateId) && urls.TryGetValue(quota.AvangateId, out var tuple))
if (!string.IsNullOrEmpty(quota.AvangateId) && urls.TryGetValue(quota.AvangateId, out var url))
{
var result = tuple.Item2;
if (result == null)
{
result = tuple.Item1;
}
else
{
var tariff = tenant.HasValue ? GetTariff(tenant.Value) : null;
if (tariff == null || tariff.QuotaId == quotaId || tariff.State >= TariffState.Delay)
{
result = tuple.Item1;
}
}
if (result == null)
if (url == null)
{
return null;
}
result = new Uri(result.ToString()
url = new Uri(url.ToString()
.Replace("__Currency__", HttpUtility.UrlEncode(currency ?? ""))
.Replace("__Language__", HttpUtility.UrlEncode((language ?? "").ToLower()))
.Replace("__CustomerID__", HttpUtility.UrlEncode(customerId ?? ""))
.Replace("__Quantity__", HttpUtility.UrlEncode(quantity ?? "")));
return url;
}
return null;
}
public Uri GetShoppingUri(string[] productIds, string affiliateId = null, string currency = null, string language = null, string customerId = null, string quantity = null)
{
var key = "shopingurl" + string.Join("_", productIds) + (!string.IsNullOrEmpty(affiliateId) ? "_" + affiliateId : "");
var url = Cache.Get<string>(key);
if (url == null)
{
url = string.Empty;
if (_billingClient.Configured)
{
try
{
var client = GetBillingClient();
url =
client.GetPaymentUrl(
null,
productIds,
affiliateId,
null,
!string.IsNullOrEmpty(currency) ? "__Currency__" : null,
!string.IsNullOrEmpty(language) ? "__Language__" : null,
!string.IsNullOrEmpty(customerId) ? "__CustomerID__" : null,
!string.IsNullOrEmpty(quantity) ? "__Quantity__" : null
);
}
catch (Exception error)
{
Logger.ErrorWithException(error);
}
}
Cache.Insert(key, url, DateTime.UtcNow.Add(TimeSpan.FromMinutes(10)));
}
ResetCacheExpiration();
if (string.IsNullOrEmpty(url))
{
return null;
}
var result = new Uri(url.ToString()
.Replace("__Currency__", HttpUtility.UrlEncode(currency ?? ""))
.Replace("__Language__", HttpUtility.UrlEncode((language ?? "").ToLower()))
.Replace("__CustomerID__", HttpUtility.UrlEncode(customerId ?? ""))
@ -416,9 +473,6 @@ public class TariffService : ITariffService
return result;
}
return null;
}
public IDictionary<string, Dictionary<string, decimal>> GetProductPriceInfo(params string[] productIds)
{
ArgumentNullException.ThrowIfNull(productIds);
@ -617,7 +671,6 @@ public class TariffService : ITariffService
defaultQuota.Name = "overdue";
defaultQuota.Features = q.Features;
defaultQuota.Support = false;
QuotaService.SaveTenantQuota(defaultQuota);
}

View File

@ -99,7 +99,7 @@ class CachedAzService : IAzService
public AzRecord SaveAce(int tenant, AzRecord r)
{
r = _service.SaveAce(tenant, r);
_cacheNotify.Publish(r, CacheNotifyAction.InsertOrUpdate);
_cacheNotify.Publish((AzRecordCache)r, CacheNotifyAction.InsertOrUpdate);
return r;
}
@ -107,6 +107,6 @@ class CachedAzService : IAzService
public void RemoveAce(int tenant, AzRecord r)
{
_service.RemoveAce(tenant, r);
_cacheNotify.Publish(r, CacheNotifyAction.Remove);
_cacheNotify.Publish((AzRecordCache)r, CacheNotifyAction.Remove);
}
}

View File

@ -430,4 +430,9 @@ public class CachedUserService : IUserService, ICachedService
return Service.GetUser(tenant, id, exp);
}
public IEnumerable<string> GetDavUserEmails(int tenant)
{
return Service.GetDavUserEmails(tenant);
}
}

View File

@ -83,6 +83,17 @@ public class PaymentManager
return _tariffService.GetShoppingUri(null, quotaId, affiliateId, currency, language, customerId, quantity);
}
public Uri GetShoppingUri(string productId, string currency = null, string language = null, string customerId = null, string quantity = null, string affiliateId = null)
{
return _tariffService.GetShoppingUri(new[] { productId }, affiliateId, currency, language, customerId, quantity);
}
// used in www
public Uri GetShoppingUri(string[] productIds, string currency = null, string language = null, string customerId = null, string quantity = null, string affiliateId = null)
{
return _tariffService.GetShoppingUri(productIds, affiliateId, currency, language, customerId, quantity);
}
public void ActivateKey(string key)
{
ArgumentNullOrEmptyException.ThrowIfNullOrEmpty(key);

View File

@ -328,6 +328,7 @@ public class TenantManager
{
currentQuota.ActiveUsers = tariff.Quantity;
currentQuota.MaxTotalSize *= currentQuota.ActiveUsers;
currentQuota.Price *= currentQuota.ActiveUsers;
}
return currentQuota;

View File

@ -55,6 +55,12 @@ public class UserManager
private readonly PermissionContext _permissionContext;
private readonly UserManagerConstants _userManagerConstants;
private readonly CoreBaseSettings _coreBaseSettings;
private readonly CoreSettings _coreSettings;
private readonly InstanceCrypto _instanceCrypto;
private readonly RadicaleClient _radicaleClient;
private readonly CardDavAddressbook _cardDavAddressbook;
private readonly ILogger<UserManager> _log;
private readonly ICache _cache;
private readonly Constants _constants;
private Tenant _tenant;
@ -70,13 +76,25 @@ public class UserManager
TenantManager tenantManager,
PermissionContext permissionContext,
UserManagerConstants userManagerConstants,
CoreBaseSettings coreBaseSettings)
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
InstanceCrypto instanceCrypto,
RadicaleClient radicaleClient,
CardDavAddressbook cardDavAddressbook,
ILogger<UserManager> log,
ICache cache)
{
_userService = service;
_tenantManager = tenantManager;
_permissionContext = permissionContext;
_userManagerConstants = userManagerConstants;
_coreBaseSettings = coreBaseSettings;
_coreSettings = coreSettings;
_instanceCrypto = instanceCrypto;
_radicaleClient = radicaleClient;
_cardDavAddressbook = cardDavAddressbook;
_log = log;
_cache = cache;
_constants = _userManagerConstants.Constants;
}
@ -86,8 +104,14 @@ public class UserManager
PermissionContext permissionContext,
UserManagerConstants userManagerConstants,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
InstanceCrypto instanceCrypto,
RadicaleClient radicaleClient,
CardDavAddressbook cardDavAddressbook,
ILogger<UserManager> log,
ICache cache,
IHttpContextAccessor httpContextAccessor)
: this(service, tenantManager, permissionContext, userManagerConstants, coreBaseSettings)
: this(service, tenantManager, permissionContext, userManagerConstants, coreBaseSettings, coreSettings, instanceCrypto, radicaleClient, cardDavAddressbook, log, cache)
{
_accessor = httpContextAccessor;
}
@ -279,7 +303,7 @@ public class UserManager
return findUsers.ToArray();
}
public UserInfo SaveUserInfo(UserInfo u, bool isVisitor = false)
public UserInfo SaveUserInfo(UserInfo u, bool isVisitor = false, bool syncCardDav = false)
{
if (IsSystemUser(u.Id))
{
@ -329,10 +353,77 @@ public class UserManager
throw new InvalidOperationException("Can not disable tenant owner.");
}
var oldUserData = _userService.GetUserByUserName(_tenantManager.GetCurrentTenant().Id, u.UserName);
var newUser = _userService.SaveUser(_tenantManager.GetCurrentTenant().Id, u);
if (syncCardDav)
{
var tenant = _tenantManager.GetCurrentTenant();
var myUri = (_accessor?.HttpContext != null) ? _accessor.HttpContext.Request.GetUrlRewriter().ToString() :
(_cache.Get<string>("REWRITE_URL" + tenant.Id) != null) ?
new Uri(_cache.Get<string>("REWRITE_URL" + tenant.Id)).ToString() : tenant.GetTenantDomain(_coreSettings);
var rootAuthorization = _cardDavAddressbook.GetSystemAuthorization();
var allUserEmails = GetDavUserEmails().ToList();
if (oldUserData != null && oldUserData.Status != newUser.Status && newUser.Status == EmployeeStatus.Terminated)
{
var userAuthorization = oldUserData.Email.ToLower() + ":" + _instanceCrypto.Encrypt(oldUserData.Email);
var requestUrlBook = _cardDavAddressbook.GetRadicaleUrl(myUri, newUser.Email.ToLower(), true, true);
var collection = _cardDavAddressbook.GetCollection(requestUrlBook, userAuthorization, myUri.ToString()).Result;
if (collection.Completed && collection.StatusCode != 404)
{
_cardDavAddressbook.Delete(myUri, newUser.Id, newUser.Email, tenant.Id).Wait();//TODO
}
foreach (var email in allUserEmails)
{
var requestUrlItem = _cardDavAddressbook.GetRadicaleUrl(myUri.ToString(), email.ToLower(), true, true, itemID: newUser.Id.ToString());
try
{
var davItemRequest = new DavRequest()
{
Url = requestUrlItem,
Authorization = rootAuthorization,
Header = myUri
};
_radicaleClient.RemoveAsync(davItemRequest).ConfigureAwait(false);
}
catch (Exception ex)
{
_log.ErrorWithException(ex);
}
}
}
else
{
try
{
var cardDavUser = new CardDavItem(u.Id, u.FirstName, u.LastName, u.UserName, u.BirthDate, u.Sex, u.Title, u.Email, u.ContactsList, u.MobilePhone);
try
{
_cardDavAddressbook.UpdateItemForAllAddBooks(allUserEmails, myUri, cardDavUser, _tenantManager.GetCurrentTenant().Id, oldUserData != null && oldUserData.Email != newUser.Email ? oldUserData.Email : null).Wait(); // todo
}
catch (Exception ex)
{
_log.ErrorWithException(ex);
}
}
catch (Exception ex)
{
_log.ErrorWithException(ex);
}
}
}
return newUser;
}
public IEnumerable<string> GetDavUserEmails()
{
return _userService.GetDavUserEmails(_tenantManager.GetCurrentTenant().Id);
}
public void DeleteUser(Guid id)
{
@ -347,7 +438,59 @@ public class UserManager
throw new InvalidOperationException("Can not remove tenant owner.");
}
var delUser = GetUsers(id);
_userService.RemoveUser(Tenant.Id, id);
var tenant = _tenantManager.GetCurrentTenant();
try
{
var curreMail = delUser.Email.ToLower();
var currentAccountPaswd = _instanceCrypto.Encrypt(curreMail);
var userAuthorization = curreMail + ":" + currentAccountPaswd;
var rootAuthorization = _cardDavAddressbook.GetSystemAuthorization();
var myUri = (_accessor?.HttpContext != null) ? _accessor.HttpContext.Request.GetUrlRewriter().ToString() :
(_cache.Get<string>("REWRITE_URL" + tenant.Id) != null) ?
new Uri(_cache.Get<string>("REWRITE_URL" + tenant.Id)).ToString() : tenant.GetTenantDomain(_coreSettings);
var davUsersEmails = GetDavUserEmails();
var requestUrlBook = _cardDavAddressbook.GetRadicaleUrl(myUri, delUser.Email.ToLower(), true, true);
var addBookCollection = _cardDavAddressbook.GetCollection(requestUrlBook, userAuthorization, myUri.ToString()).Result;
if (addBookCollection.Completed && addBookCollection.StatusCode != 404)
{
var davbookRequest = new DavRequest()
{
Url = requestUrlBook,
Authorization = rootAuthorization,
Header = myUri
};
_radicaleClient.RemoveAsync(davbookRequest).ConfigureAwait(false);
}
foreach (var email in davUsersEmails)
{
var requestUrlItem = _cardDavAddressbook.GetRadicaleUrl(myUri.ToString(), email.ToLower(), true, true, itemID: delUser.Id.ToString());
try
{
var davItemRequest = new DavRequest()
{
Url = requestUrlItem,
Authorization = rootAuthorization,
Header = myUri
};
_radicaleClient.RemoveAsync(davItemRequest).ConfigureAwait(false);
}
catch (Exception ex)
{
_log.ErrorWithException(ex);
}
}
}
catch (Exception ex)
{
_log.ErrorWithException(ex);
}
}
public void SaveUserPhoto(Guid id, byte[] photo)
@ -467,6 +610,15 @@ public class UserManager
_userService.SaveUserGroupRef(Tenant.Id, new UserGroupRef(userId, groupId, UserGroupRefType.Contains));
ResetGroupCache(userId);
var user = GetUsers(userId);
if (groupId == Constants.GroupVisitor.ID)
{
var tenant = _tenantManager.GetCurrentTenant();
var myUri = (_accessor?.HttpContext != null) ? _accessor.HttpContext.Request.GetUrlRewriter().ToString() :
(_cache.Get<string>("REWRITE_URL" + tenant.Id) != null) ?
new Uri(_cache.Get<string>("REWRITE_URL" + tenant.Id)).ToString() : tenant.GetTenantDomain(_coreSettings);
_cardDavAddressbook.Delete(myUri, user.Id, user.Email, tenant.Id).Wait(); //todo
}
}
public void RemoveUserFromGroup(Guid userId, Guid groupId)
@ -563,7 +715,7 @@ public class UserManager
if (group == null)
{
group = ToGroup(Constants.BuildinGroups.FirstOrDefault(r => r.ID == groupID));
group = ToGroup(Constants.BuildinGroups.FirstOrDefault(r => r.ID == groupID) ?? Constants.LostGroupInfo);
}
return new GroupInfo

View File

@ -30,6 +30,8 @@ namespace ASC.Core;
public class SecurityContext
{
private readonly ILogger<SecurityContext> _logger;
private readonly DbLoginEventsManager _dbLoginEventsManager;
public IAccount CurrentAccount => _authContext.CurrentAccount;
public bool IsAuthenticated => _authContext.IsAuthenticated;
@ -50,10 +52,12 @@ public class SecurityContext
UserFormatter userFormatter,
CookieStorage cookieStorage,
TenantCookieSettingsHelper tenantCookieSettingsHelper,
ILogger<SecurityContext> logger
ILogger<SecurityContext> logger,
DbLoginEventsManager dbLoginEventsManager
)
{
_logger = logger;
_dbLoginEventsManager = dbLoginEventsManager;
_userManager = userManager;
_authentication = authentication;
_authContext = authContext;
@ -72,14 +76,15 @@ public class SecurityContext
UserFormatter userFormatter,
CookieStorage cookieStorage,
TenantCookieSettingsHelper tenantCookieSettingsHelper,
ILogger<SecurityContext> logger
) : this(userManager, authentication, authContext, tenantManager, userFormatter, cookieStorage, tenantCookieSettingsHelper, logger)
ILogger<SecurityContext> logger,
DbLoginEventsManager dbLoginEventsManager
) : this(userManager, authentication, authContext, tenantManager, userFormatter, cookieStorage, tenantCookieSettingsHelper, logger, dbLoginEventsManager)
{
_httpContextAccessor = httpContextAccessor;
}
public string AuthenticateMe(string login, string passwordHash)
public string AuthenticateMe(string login, string passwordHash, Func<int> funcLoginEvent = null)
{
ArgumentNullException.ThrowIfNull(login);
ArgumentNullException.ThrowIfNull(passwordHash);
@ -87,7 +92,7 @@ public class SecurityContext
var tenantid = _tenantManager.GetCurrentTenant().Id;
var u = _userManager.GetUsersByPasswordHash(tenantid, login, passwordHash);
return AuthenticateMe(new UserAccount(u, tenantid, _userFormatter));
return AuthenticateMe(new UserAccount(u, tenantid, _userFormatter), funcLoginEvent);
}
public bool AuthenticateMe(string cookie)
@ -110,7 +115,7 @@ public class SecurityContext
}
_logger.InformationEmptyBearer(ipFrom, address);
}
else if (_cookieStorage.DecryptCookie(cookie, out var tenant, out var userid, out var indexTenant, out var expire, out var indexUser))
else if (_cookieStorage.DecryptCookie(cookie, out var tenant, out var userid, out var indexTenant, out var expire, out var indexUser, out var loginEventId))
{
if (tenant != _tenantManager.GetCurrentTenant().Id)
{
@ -136,8 +141,13 @@ public class SecurityContext
return false;
}
AuthenticateMeWithoutCookie(new UserAccount(new UserInfo { Id = userid }, tenant, _userFormatter));
var settingLoginEvents = _dbLoginEventsManager.GetLoginEventIds(tenant, userid).Result; // remove Result
if (loginEventId != 0 && !settingLoginEvents.Contains(loginEventId))
{
return false;
}
AuthenticateMeWithoutCookie(new UserAccount(new UserInfo { Id = userid }, tenant, _userFormatter));
return true;
}
catch (InvalidCredentialException ice)
@ -174,7 +184,13 @@ public class SecurityContext
return false;
}
public string AuthenticateMe(IAccount account, List<Claim> additionalClaims = null)
public string AuthenticateMe(Guid userId, Func<int> funcLoginEvent = null, List<Claim> additionalClaims = null)
{
var account = _authentication.GetAccountByID(_tenantManager.GetCurrentTenant().Id, userId);
return AuthenticateMe(account, funcLoginEvent, additionalClaims);
}
public string AuthenticateMe(IAccount account, Func<int> funcLoginEvent = null, List<Claim> additionalClaims = null)
{
AuthenticateMeWithoutCookie(account, additionalClaims);
@ -182,7 +198,13 @@ public class SecurityContext
if (account is IUserAccount)
{
cookie = _cookieStorage.EncryptCookie(_tenantManager.GetCurrentTenant().Id, account.ID);
var loginEventId = 0;
if (funcLoginEvent != null)
{
loginEventId = funcLoginEvent();
}
cookie = _cookieStorage.EncryptCookie(_tenantManager.GetCurrentTenant().Id, account.ID, loginEventId);
}
return cookie;
@ -251,13 +273,6 @@ public class SecurityContext
_authContext.Principal = new CustomClaimsPrincipal(new ClaimsIdentity(account, claims), account);
}
public string AuthenticateMe(Guid userId, List<Claim> additionalClaims = null)
{
var account = _authentication.GetAccountByID(_tenantManager.GetCurrentTenant().Id, userId);
return AuthenticateMe(account, additionalClaims);
}
public void AuthenticateMeWithoutCookie(Guid userId, List<Claim> additionalClaims = null)
{
var account = _authentication.GetAccountByID(_tenantManager.GetCurrentTenant().Id, userId);

View File

@ -59,6 +59,7 @@ public interface IUserService
UserInfo SaveUser(int tenant, UserInfo user);
void RemoveGroup(int tenant, Guid id);
void RemoveUser(int tenant, Guid id);
IEnumerable<string> GetDavUserEmails(int tenant);
void RemoveUserGroupRef(int tenant, Guid userId, Guid groupId, UserGroupRefType refType);
void SetUserPasswordHash(int tenant, Guid id, string passwordHash);
void SetUserPhoto(int tenant, Guid id, byte[] photo);

View File

@ -24,8 +24,6 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using AutoMapper.QueryableExtensions;
namespace ASC.Core.Data;
[Scope]

View File

@ -0,0 +1,184 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Core.Data;
[Scope]
public class DbLoginEventsManager
{
private const string GuidLoginEvent = "F4D8BBF6-EB63-4781-B55E-5885EAB3D759";
private static readonly TimeSpan _expirationTimeout = TimeSpan.FromMinutes(5);
private static readonly List<int> _loginActions = new List<int>
{
(int)MessageAction.LoginSuccess,
(int)MessageAction.LoginSuccessViaSocialAccount,
(int)MessageAction.LoginSuccessViaSms,
(int)MessageAction.LoginSuccessViaApi,
(int)MessageAction.LoginSuccessViaSocialApp,
(int)MessageAction.LoginSuccessViaApiSms,
(int)MessageAction.LoginSuccessViaSSO,
(int)MessageAction.LoginSuccessViaApiSocialAccount,
(int)MessageAction.LoginSuccesViaTfaApp,
(int)MessageAction.LoginSuccessViaApiTfa
};
private readonly ICache _cache;
private readonly TenantManager _tenantManager;
private readonly AuthContext _authContext;
private readonly IMapper _mapper;
private MessagesContext LoginEventContext => _lazyLoginEventContext.Value;
private readonly Lazy<MessagesContext> _lazyLoginEventContext;
public DbLoginEventsManager(
ICache cache,
TenantManager tenantManager,
AuthContext authContext,
DbContextManager<MessagesContext> dbContextManager,
TenantUtil tenantUtil,
IMapper mapper)
{
_cache = cache;
_tenantManager = tenantManager;
_authContext = authContext;
_mapper = mapper;
_lazyLoginEventContext = new Lazy<MessagesContext>(() => dbContextManager.Value);
}
public async Task<List<int>> GetLoginEventIds(int tenantId, Guid userId)
{
var commonKey = GetCacheKey(tenantId, userId);
var cacheKeys = _cache.Get<List<int>>(commonKey);
if (cacheKeys != null)
{
return cacheKeys;
}
var date = DateTime.UtcNow.AddYears(-1);
var resultList = await LoginEventContext.LoginEvents
.Where(r => r.TenantId == tenantId && r.UserId == userId && _loginActions.Contains(r.Action) && r.Date >= date && r.Active)
.Select(r => r.Id)
.ToListAsync();
if (resultList != null)
{
_cache.Insert(commonKey, resultList, _expirationTimeout);
}
return resultList;
}
public async Task<List<BaseEvent>> GetLoginEvents(int tenantId, Guid userId)
{
var date = DateTime.UtcNow.AddYears(-1);
var loginInfo = await LoginEventContext.LoginEvents
.Where(r => r.TenantId == tenantId && r.UserId == userId && _loginActions.Contains(r.Action) && r.Date >= date && r.Active)
.OrderByDescending(r => r.Id)
.ToListAsync();
return _mapper.Map<List<LoginEvent>, List<BaseEvent>>(loginInfo);
}
public async Task LogOutEvent(int loginEventId)
{
var events = await LoginEventContext.LoginEvents
.Where(r => r.Id == loginEventId)
.ToListAsync();
foreach (var e in events)
{
e.Active = false;
}
await LoginEventContext.SaveChangesAsync();
ResetCache();
}
public async Task LogOutAllActiveConnections(int tenantId, Guid userId)
{
var events = await LoginEventContext.LoginEvents
.Where(r => r.TenantId == tenantId && r.UserId == userId && r.Active)
.ToListAsync();
foreach (var e in events)
{
e.Active = false;
}
await LoginEventContext.SaveChangesAsync();
ResetCache(tenantId, userId);
}
public async Task LogOutAllActiveConnectionsForTenant(int tenantId)
{
var events = await LoginEventContext.LoginEvents
.Where(r => r.TenantId == tenantId && r.Active)
.ToListAsync();
foreach (var e in events)
{
e.Active = false;
}
await LoginEventContext.SaveChangesAsync();
}
public async Task LogOutAllActiveConnectionsExceptThis(int loginEventId, int tenantId, Guid userId)
{
var events = await LoginEventContext.LoginEvents
.Where(r => r.TenantId == tenantId && r.UserId == userId && r.Id != loginEventId && r.Active)
.ToListAsync();
foreach (var e in events)
{
e.Active = false;
}
await LoginEventContext.SaveChangesAsync();
ResetCache(tenantId, userId);
}
public void ResetCache()
{
var tenantId = _tenantManager.GetCurrentTenant().Id;
var userId = _authContext.CurrentAccount.ID;
ResetCache(tenantId, userId);
}
public void ResetCache(int tenantId, Guid userId)
{
var key = GetCacheKey(tenantId, userId);
_cache.Remove(key);
}
private string GetCacheKey(int tenantId, Guid userId)
{
return string.Join("", GuidLoginEvent, tenantId, userId);
}
}

View File

@ -24,8 +24,6 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using AutoMapper.QueryableExtensions;
namespace ASC.Core.Data;
[Scope]

View File

@ -24,8 +24,6 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using AutoMapper.QueryableExtensions;
namespace ASC.Core.Data;
[Scope]
@ -147,34 +145,15 @@ public class DbTenantService : ITenantService
if (Guid.TryParse(login, out var userId))
{
var pwdHash = GetPasswordHash(userId, passwordHash);
var oldHash = Hasher.Base64Hash(passwordHash, HashAlg.SHA256);
var q = query()
.Where(r => r.User.Id == userId)
.Where(r => r.UserSecurity.PwdHash == pwdHash || r.UserSecurity.PwdHash == oldHash) //todo: remove old scheme
.Where(r => r.UserSecurity.PwdHash == pwdHash)
;
return q.ProjectTo<Tenant>(_mapper.ConfigurationProvider).ToList();
}
else
{
var oldHash = Hasher.Base64Hash(passwordHash, HashAlg.SHA256);
var q =
query()
.Where(r => r.UserSecurity.PwdHash == oldHash);
if (login.Contains('@'))
{
q = q.Where(r => r.User.Email == login);
}
else if (Guid.TryParse(login, out var uId))
{
q = q.Where(r => r.User.Id == uId);
}
//old password
var result = q.ProjectTo<Tenant>(_mapper.ConfigurationProvider).ToList();
var usersQuery = UserDbContext.Users
.Where(r => r.Email == login)
.Where(r => r.Status == EmployeeStatus.Active)
@ -184,13 +163,10 @@ public class DbTenantService : ITenantService
var passwordHashs = usersQuery.Select(r => GetPasswordHash(r, passwordHash)).ToList();
q = query()
var q = query()
.Where(r => passwordHashs.Any(p => r.UserSecurity.PwdHash == p) && r.DbTenant.Status == TenantStatus.Active);
//new password
result = result.Concat(q.ProjectTo<Tenant>(_mapper.ConfigurationProvider)).ToList();
return result.Distinct();
return q.ProjectTo<Tenant>(_mapper.ConfigurationProvider).ToList();
}
}
@ -459,7 +435,7 @@ public class DbTenantService : ITenantService
// cut number suffix
while (true)
{
if (6 < domain.Length && char.IsNumber(domain, domain.Length - 1))
if (TenantDomainValidator.MinLength < domain.Length && char.IsNumber(domain, domain.Length - 1))
{
domain = domain[0..^1];
}

View File

@ -24,8 +24,6 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using AutoMapper.QueryableExtensions;
namespace ASC.Core.Data;
[Scope]
@ -119,10 +117,7 @@ public class EFUserService : IUserService
if (Guid.TryParse(login, out var userId))
{
RegeneratePassword(tenant, userId);
var pwdHash = GetPasswordHash(userId, passwordHash);
var oldHash = Hasher.Base64Hash(passwordHash, HashAlg.SHA256);
var q = GetUserQuery(tenant)
.Where(r => !r.Removed)
@ -132,7 +127,7 @@ public class EFUserService : IUserService
User = user,
UserSecurity = security
})
.Where(r => r.UserSecurity.PwdHash == pwdHash || r.UserSecurity.PwdHash == oldHash) //todo: remove old scheme
.Where(r => r.UserSecurity.PwdHash == pwdHash)
;
if (tenant != Tenant.DefaultTenant)
@ -151,31 +146,20 @@ public class EFUserService : IUserService
.Where(r => r.Email == login);
var users = q.ProjectTo<UserInfo>(_mapper.ConfigurationProvider).ToList();
UserInfo result = null;
foreach (var user in users)
{
RegeneratePassword(tenant, user.Id);
var pwdHash = GetPasswordHash(user.Id, passwordHash);
var oldHash = Hasher.Base64Hash(passwordHash, HashAlg.SHA256);
var any = UserDbContext.UserSecurity
.Any(r => r.UserId == user.Id && (r.PwdHash == pwdHash || r.PwdHash == oldHash));//todo: remove old scheme
.Any(r => r.UserId == user.Id && (r.PwdHash == pwdHash));
if (any)
{
if (tenant != Tenant.DefaultTenant)
{
return user;
}
//need for regenerate all passwords only
//todo: remove with old scheme
result = user;
}
}
return result;
return null;
}
}
@ -188,31 +172,6 @@ public class EFUserService : IUserService
return q.ProjectTo<UserInfo>(_mapper.ConfigurationProvider).ToList();
}
//todo: remove
private void RegeneratePassword(int tenant, Guid userId)
{
var q = UserDbContext.UserSecurity
.Where(r => r.UserId == userId);
if (tenant != Tenant.DefaultTenant)
{
q = q.Where(r => r.Tenant == tenant);
}
var h2 = q.Select(r => new { r.Tenant, r.PwdHashSha512 })
.Take(1)
.FirstOrDefault();
if (h2 == null || string.IsNullOrEmpty(h2.PwdHashSha512))
{
return;
}
var password = Crypto.GetV(h2.PwdHashSha512, 1, false);
var passwordHash = _passwordHasher.GetClientPassword(password);
SetUserPasswordHash(h2.Tenant, userId, passwordHash);
}
public UserGroupRef GetUserGroupRef(int tenant, Guid groupId, UserGroupRefType refType)
{
IQueryable<UserGroup> q = UserDbContext.UserGroups;
@ -550,7 +509,6 @@ public class EFUserService : IUserService
Tenant = tenant,
UserId = id,
PwdHash = h1,
PwdHashSha512 = null,//todo: remove
LastModified = DateTime.UtcNow
};
@ -745,6 +703,16 @@ public class EFUserService : IUserService
}
}
public IEnumerable<string> GetDavUserEmails(int tenant)
{
return (from usersDav in UserDbContext.UsersDav
join users in UserDbContext.Users on new { tenant = usersDav.TenantId, userId = usersDav.UserId } equals new { tenant = users.Tenant, userId = users.Id }
where usersDav.TenantId == tenant
select users.Email)
.Distinct()
.ToList();
}
protected string GetPasswordHash(Guid userId, string password)
{
return Hasher.Base64Hash(password + userId + Encoding.UTF8.GetString(MachinePseudoKeys.GetMachineConstant()), HashAlg.SHA512);

View File

@ -39,6 +39,7 @@ public class UserDbContext : BaseDbContext
public DbSet<UserGroup> UserGroups { get; set; }
public DbSet<Subscription> Subscriptions { get; set; }
public DbSet<DbSubscriptionMethod> SubscriptionMethods { get; set; }
public DbSet<UserDav> UsersDav { get; set; }
protected override Dictionary<Provider, Func<BaseDbContext>> ProviderContext
{
@ -63,7 +64,8 @@ public class UserDbContext : BaseDbContext
.AddUserPhoto()
.AddDbGroup()
.AddUserGroup()
.AddSubscription();
.AddSubscription()
.AddUserDav();
}
}

View File

@ -30,6 +30,7 @@ public class MySqlWebstudioDbContext : WebstudioDbContext { }
public class PostgreSqlWebstudioDbContext : WebstudioDbContext { }
public class WebstudioDbContext : BaseDbContext
{
public DbSet<DbTenant> Tenants { get; set; }
public DbSet<DbWebstudioSettings> WebstudioSettings { get; set; }
public DbSet<DbWebstudioUserVisit> WebstudioUserVisit { get; set; }
public DbSet<DbWebstudioIndex> WebstudioIndex { get; set; }
@ -52,7 +53,9 @@ public class WebstudioDbContext : BaseDbContext
.From(modelBuilder, _provider)
.AddWebstudioSettings()
.AddWebstudioUserVisit()
.AddDbWebstudioIndex();
.AddDbWebstudioIndex()
.AddDbTenant()
.AddDbFunction();
}
}

View File

@ -103,17 +103,18 @@ public static class AclExtension
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|1e04460243b54d7982f3fd6208a11960", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|6743007c6f954d208c88a8601ce5e76d", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|e67be73df9ae4ce18fec1880cb518cb4", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|ea942538e68e49079394035336ee0ba8", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|ea942538e68e49079394035336ee0ba8", AceType = AceType.Deny },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|32d24cb57ece46069c9419216ba42086", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|bf88953e3c434850a3fbb1e43ad53a3e", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|2a9230378b2d487b9a225ac0918acf3f", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|f4d98afdd336433287783c6945c81ea0", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|28b10049dd204f54b986873bc14ccfc7", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|3cfd481b46f24a4ab55cb8c0c9def02c", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|6a598c7491ae437da5f4ad339bd11bb2", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|742cf945cbbc4a5782d61600a12cf8ca", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|853b6eb973ee438d9b098ffeedf36234", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|46cfa73af32046cf8d5bcd82e1d67f26", AceType = 0 }
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|28b10049dd204f54b986873bc14ccfc7", AceType = AceType.Deny },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|3cfd481b46f24a4ab55cb8c0c9def02c", AceType = AceType.Deny },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|6a598c7491ae437da5f4ad339bd11bb2", AceType = AceType.Deny },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|742cf945cbbc4a5782d61600a12cf8ca", AceType = AceType.Deny },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|853b6eb973ee438d9b098ffeedf36234", AceType = AceType.Deny },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|46cfa73af32046cf8d5bcd82e1d67f26", AceType = 0 },
new Acl { Tenant = -1, Subject = Guid.Parse("c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e"), Action = Guid.Parse("77777777-32ae-425f-99b5-83176061d1ae"), Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|37620ae5c40b45ce855a39dd7d76a1fa", AceType = 0 }
);
return modelBuilder;

View File

@ -0,0 +1,86 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Core.Common.EF;
public class UserDav : BaseEntity
{
public int TenantId { get; set; }
public Guid UserId { get; set; }
public override object[] GetKeys()
{
return new object[] { TenantId, UserId };
}
}
public static class UserDavExtension
{
public static ModelBuilderWrapper AddUserDav(this ModelBuilderWrapper modelBuilder)
{
modelBuilder
.Add(MySqlAddUserDav, Provider.MySql)
.Add(PgSqlAddUserDav, Provider.PostgreSql);
return modelBuilder;
}
public static void MySqlAddUserDav(this ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserDav>(entity =>
{
entity.HasKey(e => new { e.TenantId, e.UserId })
.HasName("PRIMARY");
entity.ToTable("core_userdav");
entity.Property(e => e.TenantId).HasColumnName("tenant_id");
entity.Property(e => e.UserId)
.HasColumnName("userid")
.HasColumnType("varchar(38)")
.HasCharSet("utf8")
.UseCollation("utf8_general_ci");
});
}
public static void PgSqlAddUserDav(this ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserDav>(entity =>
{
entity.HasKey(e => new { e.TenantId, e.UserId })
.HasName("core_userdav_pkey");
entity.ToTable("core_userdav", "onlyoffice");
entity.Property(e => e.TenantId).HasColumnName("tenant_id");
entity.Property(e => e.UserId)
.HasColumnName("userid")
.HasMaxLength(38);
});
}
}

View File

@ -31,7 +31,6 @@ public class UserSecurity : BaseEntity
public int Tenant { get; set; }
public Guid UserId { get; set; }
public string PwdHash { get; set; }
public string PwdHashSha512 { get; set; }
public DateTime? LastModified { get; set; }
public override object[] GetKeys()
{
@ -90,12 +89,6 @@ public static class UserSecurityExtension
.HasCharSet("utf8")
.UseCollation("utf8_general_ci");
entity.Property(e => e.PwdHashSha512)
.HasColumnName("pwdhashsha512")
.HasColumnType("varchar(512)")
.HasCharSet("utf8")
.UseCollation("utf8_general_ci");
entity.Property(e => e.Tenant).HasColumnName("tenant");
});
}
@ -125,11 +118,6 @@ public static class UserSecurityExtension
.HasMaxLength(512)
.HasDefaultValueSql("NULL");
entity.Property(e => e.PwdHashSha512)
.HasColumnName("pwdhashsha512")
.HasMaxLength(512)
.HasDefaultValueSql("NULL");
entity.Property(e => e.Tenant).HasColumnName("tenant");
});
}

View File

@ -35,6 +35,7 @@ global using System.Globalization;
global using System.Linq;
global using System.Linq.Expressions;
global using System.Net;
global using System.Net.Http.Headers;
global using System.Reflection;
global using System.Resources;
global using System.Runtime.Serialization;
@ -56,15 +57,19 @@ global using Amazon.Runtime;
global using Amazon.SimpleEmail;
global using Amazon.SimpleEmail.Model;
global using ASC.AuditTrail.Models;
global using ASC.Collections;
global using ASC.Common;
global using ASC.Common.Caching;
global using ASC.Common.Data;
global using ASC.Common.Log;
global using ASC.Common.Logging;
global using ASC.Common.Mapping;
global using ASC.Common.Module;
global using ASC.Common.Notify.Engine;
global using ASC.Common.Notify.Patterns;
global using ASC.Common.Radicale;
global using ASC.Common.Radicale.Core;
global using ASC.Common.Security;
global using ASC.Common.Security.Authentication;
global using ASC.Common.Security.Authorizing;
@ -74,7 +79,6 @@ global using ASC.Core;
global using ASC.Core.Billing;
global using ASC.Core.Caching;
global using ASC.Core.Common;
global using ASC.Core.Common.Billing;
global using ASC.Core.Common.Configuration;
global using ASC.Core.Common.EF;
global using ASC.Core.Common.EF.Context;
@ -104,6 +108,10 @@ global using ASC.Core.Users;
global using ASC.EventBus.Abstractions;
global using ASC.EventBus.Events;
global using ASC.Geolocation;
global using ASC.MessagingSystem.Core;
global using ASC.MessagingSystem.Data;
global using ASC.MessagingSystem.Mapping;
global using ASC.MessagingSystem.Models;
global using ASC.Notify;
global using ASC.Notify.Channels;
global using ASC.Notify.Cron;
@ -119,6 +127,7 @@ global using ASC.Web.Studio.Utility;
global using Autofac;
global using AutoMapper;
global using AutoMapper.QueryableExtensions;
global using MailKit.Security;
@ -149,4 +158,3 @@ global using ProtoBuf;
global using Telegram.Bot;
global using static ASC.Security.Cryptography.EmailValidationKeyProvider;
global using ASC.Common.Data;

View File

@ -242,7 +242,7 @@ public class HostedSolution
var expires = tenantSettings.IsDefault() ? DateTime.UtcNow.AddYears(1) : DateTime.UtcNow.AddMinutes(tenantSettings.LifeTime);
var userSettings = SettingsManager.LoadSettingsFor<TenantCookieSettings>(tenantId, user.Id);
return cookieStorage.EncryptCookie(tenantId, user.Id, tenantSettings.Index, expires, userSettings.Index);
return cookieStorage.EncryptCookie(tenantId, user.Id, tenantSettings.Index, expires, userSettings.Index, 0);
}
public Tariff GetTariff(int tenant, bool withRequestToPaymentSystem = true)

View File

@ -27,6 +27,9 @@
namespace ASC.Core.Common.Log;
internal static partial class CookieStorageLogger
{
[LoggerMessage(Level = LogLevel.Error, Message = "Authenticate error: cookie {cookie}, tenant {tenant}, userid {userid}, indexTenant {indexTenant}, expire {expire}")]
public static partial void AuthenticateError(this ILogger<CookieStorage> logger, string cookie, int tenant, Guid userId, int indexTenant, string expire, Exception exception);
[LoggerMessage(Level = LogLevel.Error, Message = "Authenticate error: cookie {cookie}, tenant {tenant}, userid {userid}, indexTenant {indexTenant}, expire {expire}, loginEvent {loginEvent}")]
public static partial void AuthenticateError(this ILogger<CookieStorage> logger, string cookie, int tenant, Guid userId, int indexTenant, string expire, int loginEvent, Exception exception);
[LoggerMessage(Level = LogLevel.Error, Message = "Failed to get login event ID from cookie: cookie {cookie}, loginEvent {loginEventId}")]
public static partial void ErrorLoginEvent(this ILogger<CookieStorage> logger, string cookie, int loginEventId, Exception exception);
}

View File

@ -26,7 +26,7 @@
namespace ASC.AuditTrail.Models;
public class BaseEvent
public class BaseEvent : IMapFrom<LoginEvent>
{
public int Id { get; set; }
public int TenantId { get; set; }
@ -35,7 +35,7 @@ public class BaseEvent
public IList<string> Description { get; set; }
[Event("IpCol")]
public string Ip { get; set; }
public string IP { get; set; }
[Event("BrowserCol")]
public string Browser { get; set; }
@ -54,22 +54,11 @@ public class BaseEvent
[Event("ActionCol")]
public string ActionText { get; set; }
}
internal class BaseEventMap<T> : ClassMap<T> where T : BaseEvent
{
public BaseEventMap()
public virtual void Mapping(Profile profile)
{
var eventType = typeof(T);
var eventProps = eventType
.GetProperties()
.Where(r => r.GetCustomAttribute<EventAttribute>() != null)
.OrderBy(r => r.GetCustomAttribute<EventAttribute>().Order);
foreach (var prop in eventProps)
{
var attr = prop.GetCustomAttribute<EventAttribute>().Resource;
Map(eventType, prop).Name(AuditReportResource.ResourceManager.GetString(attr));
}
profile.CreateMap<LoginEvent, BaseEvent>()
.ConvertUsing<BaseEventTypeConverter>();
}
}

View File

@ -0,0 +1,46 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.MessagingSystem.Mapping;
[Scope]
public class BaseEventTypeConverter : ITypeConverter<LoginEvent, BaseEvent>
{
private readonly TenantUtil _tenantUtil;
public BaseEventTypeConverter(TenantUtil tenantUtil)
{
_tenantUtil = tenantUtil;
}
public BaseEvent Convert(LoginEvent source, BaseEvent destination, ResolutionContext context)
{
var baseEvent = context.Mapper.Map<LoginEvent, BaseEvent>(source);
baseEvent.IP = source.Ip.Split(':').Length > 1 ? source.Ip.Split(':')[0] : source.Ip;
baseEvent.Date = _tenantUtil.DateTimeFromUtc(source.Date);
return baseEvent;
}
}

View File

@ -24,7 +24,7 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.AuditTrail.Attributes;
namespace ASC.AuditTrail;
[AttributeUsage(AttributeTargets.Property)]
public class EventAttribute : Attribute

View File

@ -24,6 +24,7 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.MessagingSystem.Models;
public class EventMessage
@ -41,4 +42,5 @@ public class EventMessage
public IList<string> Description { get; set; }
public MessageTarget Target { get; set; }
public string UAHeader { get; set; }
public bool Active { get; set; }
}

View File

@ -24,11 +24,14 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.MessagingSystem.Models;
public class LoginEvent : MessageEvent, IMapFrom<EventMessage>
{
public string Login { get; set; }
public bool Active { get; set; }
public void Mapping(Profile profile)
{
@ -104,7 +107,11 @@ public static class LoginEventsExtension
.HasCharSet("utf8")
.UseCollation("utf8_general_ci");
entity.Property(e => e.TenantId).HasColumnName("tenant_id");
entity.Property(e => e.TenantId)
.HasColumnName("tenant_id");
entity.Property(e => e.Active)
.HasColumnName("active");
entity.Property(e => e.UserId)
.IsRequired()
@ -164,6 +171,8 @@ public static class LoginEventsExtension
entity.Property(e => e.TenantId).HasColumnName("tenant_id");
entity.Property(e => e.Active).HasColumnName("active");
entity.Property(e => e.UserId)
.IsRequired()
.HasColumnName("user_id")

View File

@ -1,4 +1,4 @@
// (c) Copyright Ascensio System SIA 2010-2022
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
@ -24,6 +24,23 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
/*
*
* (c) Copyright Ascensio System Limited 2010-2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace ASC.MessagingSystem.Core;
public enum MessageAction
@ -109,6 +126,7 @@ public enum MessageAction
SubtaskUpdated = 2035,
SubtaskUpdatedStatus = 2036,
SubtaskDeleted = 2037,
SubtaskMoved = 2058,
DiscussionCreated = 2038,
DiscussionUpdated = 2039,
@ -379,6 +397,10 @@ public enum MessageAction
UserDataReassigns = 4030,
UserDataRemoving = 4031,
UserLogoutActiveConnections = 4034,
UserLogoutActiveConnection = 4035,
UserLogoutActiveConnectionsForUser = 4036,
#endregion
#region Documents
@ -394,10 +416,15 @@ public enum MessageAction
FileLocked = 5006,
FileUnlocked = 5007,
FileUpdatedAccess = 5008,
FileUpdatedAccessFor = 5068,
FileSendAccessLink = 5036, // not used
FileOpenedForChange = 5054,
FileRemovedFromList = 5058,
FileExternalLinkAccessUpdated = 5060,
FileDownloaded = 5009,
FileDownloadedAs = 5010,
FileRevisionDownloaded = 5062,
FileUploaded = 5011,
FileImported = 5012,
@ -407,18 +434,23 @@ public enum MessageAction
FileMoved = 5015,
FileMovedWithOverwriting = 5016,
FileMovedToTrash = 5017,
FileDeleted = 5018, // not used
FileDeleted = 5018,
FolderCreated = 5019,
FolderRenamed = 5020,
FolderUpdatedAccess = 5021,
FolderUpdatedAccessFor = 5066,
FolderCopied = 5022,
FolderCopiedWithOverwriting = 5023,
FolderMoved = 5024,
FolderMovedFrom = 5067,
FolderMovedWithOverwriting = 5025,
FolderMovedToTrash = 5026,
FolderDeleted = 5027, // not used
FolderDeleted = 5027,
FolderRemovedFromList = 5059,
FolderDownloaded = 5057,
ThirdPartyCreated = 5028,
ThirdPartyUpdated = 5029,
@ -426,9 +458,10 @@ public enum MessageAction
DocumentsThirdPartySettingsUpdated = 5031,
DocumentsOverwritingSettingsUpdated = 5032,
DocumentsForcesave = 5049, // last
DocumentsForcesave = 5049,
DocumentsStoreForcesave = 5048,
DocumentsUploadingFormatsSettingsUpdated = 5033,
DocumentsExternalShareSettingsUpdated = 5069, // last
FileConverted = 5035,
@ -437,6 +470,15 @@ public enum MessageAction
DocumentSignComplete = 5046,
DocumentSendToSign = 5045,
FileMarkedAsFavorite = 5055,
FileRemovedFromFavorite = 5056,
FileMarkedAsRead = 5063,
FileReaded = 5064,
TrashEmptied = 5061,
FolderMarkedAsRead = 5065,
#endregion
#region Settings

View File

@ -41,48 +41,31 @@ public class MessageTarget
public MessageTarget Create<T>(T value)
{
try
{
var res = new List<string>();
if (value is System.Collections.IEnumerable ids)
{
res.AddRange(from object id in ids select id.ToString());
}
else
var res = new List<string>(1);
if (value != null)
{
res.Add(value.ToString());
}
return new MessageTarget(_option)
{
_items = res.Distinct()
_items = res
};
}
catch (Exception e)
{
_logger.ErrorEventMessageTarget(e);
return null;
}
}
public MessageTarget Create(IEnumerable<string> value)
{
try
var res = new MessageTarget(_option)
{
return new MessageTarget(_option)
{
_items = value.Distinct()
_items = new List<string>()
};
}
catch (Exception e)
{
_logger.ErrorEventMessageTarget(e);
return null;
if (value != null)
{
res._items = value.Select(r => r.ToString()).ToList();
}
return res;
}
public MessageTarget Parse(string value)
@ -104,7 +87,7 @@ public class MessageTarget
_items = items
};
}
public IEnumerable<string> GetItems() { return _items.ToList(); }
public override string ToString()
{
return string.Join(",", _items);

View File

@ -100,7 +100,7 @@ namespace ASC.Core.Common.Migrations.MySql.CoreDbContextMySql
Tenant = -1,
ActiveUsers = 10000,
AvangateId = "0",
Features = "domain,audit,controlpanel,healthcheck,ldap,sso,whitelabel,branding,ssbranding,update,support,portals:10000,discencryption,privacyroom,restore",
Features = "update",
MaxFileSize = 102400L,
MaxTotalSize = 10995116277760L,
Name = "default",

View File

@ -116,7 +116,7 @@ public partial class CoreDbContextMySql : Migration
migrationBuilder.InsertData(
table: "tenants_quota",
columns: new[] { "tenant", "active_users", "avangate_id", "description", "features", "max_file_size", "max_total_size", "name", "price", "visible" },
values: new object[] { -1, 10000, "0", null, "domain,audit,controlpanel,healthcheck,ldap,sso,whitelabel,branding,ssbranding,update,support,portals:10000,discencryption,privacyroom,restore", 102400L, 10995116277760L, "default", 0.00m, false });
values: new object[] { -1, 10000, "0", null, "update", 102400L, 10995116277760L, "default", 0.00m, false });
migrationBuilder.CreateIndex(
name: "last_modified",

View File

@ -99,7 +99,7 @@ namespace ASC.Core.Common.Migrations.MySql.CoreDbContextMySql
Tenant = -1,
ActiveUsers = 10000,
AvangateId = "0",
Features = "domain,audit,controlpanel,healthcheck,ldap,sso,whitelabel,branding,ssbranding,update,support,portals:10000,discencryption,privacyroom,restore",
Features = "update",
MaxFileSize = 102400L,
MaxTotalSize = 10995116277760L,
Name = "default",

View File

@ -53,166 +53,354 @@ public partial class FilesDbContextMySql : Migration
columns: new[] { "input", "output" },
values: new object[,]
{
{ ".csv", ".ods" },
{ ".pps", ".odp" },
{ ".pps", ".pdf" },
{ ".pps", ".pptx" },
{ ".ppsm", ".odp" },
{ ".ppsm", ".pdf" },
{ ".ppsm", ".pptx" },
{ ".potx", ".pptx" },
{ ".ppsx", ".odp" },
{ ".ppsx", ".pptx" },
{ ".ppt", ".odp" },
{ ".ppt", ".pdf" },
{ ".ppt", ".pptx" },
{ ".pptm", ".odp" },
{ ".pptm", ".pdf" },
{ ".ppsx", ".pdf" },
{ ".potx", ".pdf" },
{ ".potx", ".odp" },
{ ".potm", ".pptx" },
{ ".ots", ".xlsx" },
{ ".odt", ".docx" },
{ ".odt", ".pdf" },
{ ".odt", ".rtf" },
{ ".odt", ".txt" },
{ ".ott", ".docx" },
{ ".ott", ".odt" },
{ ".ott", ".pdf" },
{ ".ott", ".rtf" },
{ ".ott", ".txt" },
{ ".pot", ".odp" },
{ ".pot", ".pdf" },
{ ".pot", ".pptx" },
{ ".potm", ".odp" },
{ ".potm", ".pdf" },
{ ".pptm", ".pptx" },
{ ".ots", ".pdf" },
{ ".pptt", ".odp" },
{ ".pptt", ".pptx" },
{ ".xlst", ".xlsx" },
{ ".xlst", ".csv" },
{ ".xlst", ".ods" },
{ ".xlt", ".csv" },
{ ".xlt", ".ods" },
{ ".xlt", ".pdf" },
{ ".xlst", ".pdf" },
{ ".xlt", ".xlsx" },
{ ".xltm", ".ods" },
{ ".xltm", ".pdf" },
{ ".xltm", ".xlsx" },
{ ".xltx", ".pdf" },
{ ".xltx", ".csv" },
{ ".xltx", ".ods" },
{ ".xltm", ".csv" },
{ ".xlsm", ".xlsx" },
{ ".xlsm", ".ods" },
{ ".xlsm", ".pdf" },
{ ".pptx", ".odp" },
{ ".pptx", ".pdf" },
{ ".rtf", ".odp" },
{ ".rtf", ".pdf" },
{ ".rtf", ".docx" },
{ ".rtf", ".txt" },
{ ".txt", ".pdf" },
{ ".txt", ".docx" },
{ ".txt", ".odp" },
{ ".txt", ".rtx" },
{ ".xls", ".csv" },
{ ".xls", ".ods" },
{ ".xls", ".pdf" },
{ ".xls", ".xlsx" },
{ ".xlsm", ".csv" },
{ ".pptt", ".pdf" },
{ ".ots", ".ods" },
{ ".ots", ".csv" },
{ ".ods", ".xlsx" },
{ ".dot", ".pdf" },
{ ".dot", ".rtf" },
{ ".dot", ".txt" },
{ ".dotm", ".docx" },
{ ".dotm", ".odt" },
{ ".dotm", ".pdf" },
{ ".dot", ".odt" },
{ ".dotm", ".rtf" },
{ ".dotx", ".docx" },
{ ".dotx", ".odt" },
{ ".dotx", ".pdf" },
{ ".dotx", ".rtf" },
{ ".dotx", ".txt" },
{ ".epub", ".docx" },
{ ".dotm", ".txt" },
{ ".dot", ".docx" },
{ ".docx", ".txt" },
{ ".docx", ".rtf" },
{ ".docx", ".docxf" },
{ ".docxf",".docx" },
{ ".docxf",".dotx" },
{ ".docxf",".epub" },
{ ".docxf",".fb2" },
{ ".docxf",".html" },
{ ".docxf",".odt" },
{ ".docxf",".oform" },
{ ".docxf",".ott" },
{ ".docxf",".pdf" },
{ ".docxf",".rtf" },
{ ".docxf",".txt" },
{ ".csv", ".pdf" },
{ ".csv", ".xlsx" },
{ ".doc", ".docx" },
{ ".doc", ".odt" },
{ ".doc", ".pdf" },
{ ".doc", ".rtf" },
{ ".doc", ".txt" },
{ ".docm", ".docx" },
{ ".docm", ".odt" },
{ ".docm", ".pdf" },
{ ".docm", ".rtf" },
{ ".docm", ".txt" },
{ ".doct", ".docx" },
{ ".docx", ".odt" },
{ ".docx", ".pdf" },
{ ".epub", ".odt" },
{ ".epub", ".pdf" },
{ ".epub", ".rtf" },
{ ".epub", ".txt" },
{ ".html", ".pdf" },
{ ".html", ".rtf" },
{ ".html", ".txt" },
{ ".mht", ".docx" },
{ ".mht", ".odt" },
{ ".mht", ".pdf" },
{ ".mht", ".rtf" },
{ ".mht", ".txt" },
{ ".odp", ".pdf" },
{ ".odp", ".pptx" },
{ ".otp", ".odp" },
{ ".otp", ".pdf" },
{ ".otp", ".pptx" },
{ ".ods", ".csv" },
{ ".ods", ".pdf" },
{ ".html", ".odt" },
{ ".xltx", ".xlsx" },
{ ".html", ".docx" },
{ ".fodt", ".rtf" },
{ ".fb2", ".docx" },
{ ".fb2", ".odt" },
{ ".fb2", ".pdf" },
{ ".fb2", ".rtf" },
{ ".fb2", ".txt" },
{ ".fodp", ".odp" },
{ ".fodp", ".pdf" },
{ ".fodp", ".pptx" },
{ ".fods", ".csv" },
{ ".fods", ".ods" },
{ ".fods", ".pdf" },
{ ".fods", ".xlsx" },
{ ".fodt", ".docx" },
{ ".fodt", ".odt" },
{ ".fodt", ".pdf" },
{ ".fodt", ".txt" },
{ ".xps", ".pdf" }
{".csv", ".ods"},
{".csv", ".ots"},
{".csv", ".pdf"},
{".csv", ".xlsm"},
{".csv", ".xlsx"},
{".csv", ".xltm"},
{".csv", ".xltx"},
{".doc", ".docm"},
{".doc", ".docx"},
{".doc", ".dotm"},
{".doc", ".dotx"},
{".doc", ".epub"},
{".doc", ".fb2"},
{".doc", ".html"},
{".doc", ".odt"},
{".doc", ".ott"},
{".doc", ".pdf"},
{".doc", ".rtf"},
{".doc", ".txt"},
{".docm", ".docx"},
{".docm", ".dotm"},
{".docm", ".dotx"},
{".docm", ".epub"},
{".docm", ".fb2"},
{".docm", ".html"},
{".docm", ".odt"},
{".docm", ".ott"},
{".docm", ".pdf"},
{".docm", ".rtf"},
{".docm", ".txt"},
{".doct", ".docx"},
{".docx", ".docm"},
{".docx", ".docxf"},
{".docx", ".dotm"},
{".docx", ".dotx"},
{".docx", ".epub"},
{".docx", ".fb2"},
{".docx", ".html"},
{".docx", ".odt"},
{".docx", ".ott"},
{".docx", ".pdf"},
{".docx", ".rtf"},
{".docx", ".txt"},
{".docxf", ".docx"},
{".docxf", ".dotx"},
{".docxf", ".epub"},
{".docxf", ".fb2"},
{".docxf", ".html"},
{".docxf", ".odt"},
{".docxf", ".oform"},
{".docxf", ".ott"},
{".docxf", ".pdf"},
{".docxf", ".rtf"},
{".docxf", ".txt"},
{".dot", ".docm"},
{".dot", ".docx"},
{".dot", ".dotm"},
{".dot", ".dotx"},
{".dot", ".epub"},
{".dot", ".fb2"},
{".dot", ".html"},
{".dot", ".odt"},
{".dot", ".ott"},
{".dot", ".pdf"},
{".dot", ".rtf"},
{".dot", ".txt"},
{".dotm", ".docm"},
{".dotm", ".docx"},
{".dotm", ".dotx"},
{".dotm", ".epub"},
{".dotm", ".fb2"},
{".dotm", ".html"},
{".dotm", ".odt"},
{".dotm", ".ott"},
{".dotm", ".pdf"},
{".dotm", ".rtf"},
{".dotm", ".txt"},
{".dotx", ".docm"},
{".dotx", ".docx"},
{".dotx", ".dotm"},
{".dotx", ".epub"},
{".dotx", ".fb2"},
{".dotx", ".html"},
{".dotx", ".odt"},
{".dotx", ".ott"},
{".dotx", ".pdf"},
{".dotx", ".rtf"},
{".dotx", ".txt"},
{".epub", ".docm"},
{".epub", ".docx"},
{".epub", ".dotm"},
{".epub", ".dotx"},
{".epub", ".fb2"},
{".epub", ".html"},
{".epub", ".odt"},
{".epub", ".ott"},
{".epub", ".pdf"},
{".epub", ".rtf"},
{".epub", ".txt"},
{".fb2", ".docm"},
{".fb2", ".docx"},
{".fb2", ".dotm"},
{".fb2", ".dotx"},
{".fb2", ".epub"},
{".fb2", ".html"},
{".fb2", ".odt"},
{".fb2", ".ott"},
{".fb2", ".pdf"},
{".fb2", ".rtf"},
{".fb2", ".txt"},
{".fodp", ".odp"},
{".fodp", ".otp"},
{".fodp", ".pdf"},
{".fodp", ".potm"},
{".fodp", ".potx"},
{".fodp", ".pptm"},
{".fodp", ".pptx"},
{".fods", ".csv"},
{".fods", ".ods"},
{".fods", ".ots"},
{".fods", ".pdf"},
{".fods", ".xlsm"},
{".fods", ".xlsx"},
{".fods", ".xltm"},
{".fods", ".xltx"},
{".fodt", ".docm"},
{".fodt", ".docx"},
{".fodt", ".dotm"},
{".fodt", ".dotx"},
{".fodt", ".epub"},
{".fodt", ".fb2"},
{".fodt", ".html"},
{".fodt", ".odt"},
{".fodt", ".ott"},
{".fodt", ".pdf"},
{".fodt", ".rtf"},
{".fodt", ".txt"},
{".html", ".docm"},
{".html", ".docx"},
{".html", ".dotm"},
{".html", ".dotx"},
{".html", ".epub"},
{".html", ".fb2"},
{".html", ".odt"},
{".html", ".ott"},
{".html", ".pdf"},
{".html", ".rtf"},
{".html", ".txt"},
{".mht", ".docm"},
{".mht", ".docx"},
{".mht", ".dotm"},
{".mht", ".dotx"},
{".mht", ".epub"},
{".mht", ".fb2"},
{".mht", ".odt"},
{".mht", ".ott"},
{".mht", ".pdf"},
{".mht", ".rtf"},
{".mht", ".txt"},
{".odp", ".otp"},
{".odp", ".pdf"},
{".odp", ".potm"},
{".odp", ".potx"},
{".odp", ".pptm"},
{".odp", ".pptx"},
{".otp", ".odp"},
{".otp", ".pdf"},
{".otp", ".potm"},
{".otp", ".potx"},
{".otp", ".pptm"},
{".otp", ".pptx"},
{".ods", ".csv"},
{".ods", ".ots"},
{".ods", ".pdf"},
{".ods", ".xlsm"},
{".ods", ".xlsx"},
{".ods", ".xltm"},
{".ods", ".xltx"},
{".ots", ".csv"},
{".ots", ".ods"},
{".ots", ".pdf"},
{".ots", ".xlsm"},
{".ots", ".xlsx"},
{".ots", ".xltm"},
{".ots", ".xltx"},
{".odt", ".docm"},
{".odt", ".docx"},
{".odt", ".dotm"},
{".odt", ".dotx"},
{".odt", ".epub"},
{".odt", ".fb2"},
{".odt", ".html"},
{".odt", ".ott"},
{".odt", ".pdf"},
{".odt", ".rtf"},
{".odt", ".txt"},
{".ott", ".docm"},
{".ott", ".docx"},
{".ott", ".dotm"},
{".ott", ".dotx"},
{".ott", ".epub"},
{".ott", ".fb2"},
{".ott", ".html"},
{".ott", ".odt"},
{".ott", ".pdf"},
{".ott", ".rtf"},
{".ott", ".txt"},
{".oxps", ".pdf"},
{".pot", ".odp"},
{".pot", ".otp"},
{".pot", ".pdf"},
{".pot", ".pptm"},
{".pot", ".pptx"},
{".pot", ".potm"},
{".pot", ".potx"},
{".potm", ".odp"},
{".potm", ".otp"},
{".potm", ".pdf"},
{".potm", ".potx"},
{".potm", ".pptm"},
{".potm", ".pptx"},
{".potx", ".odp"},
{".potx", ".otp"},
{".potx", ".pdf"},
{".potx", ".potm"},
{".potx", ".pptm"},
{".potx", ".pptx"},
{".pps", ".odp"},
{".pps", ".otp"},
{".pps", ".pdf"},
{".pps", ".potm"},
{".pps", ".potx"},
{".pps", ".pptm"},
{".pps", ".pptx"},
{".ppsm", ".odp"},
{".ppsm", ".otp"},
{".ppsm", ".pdf"},
{".ppsm", ".potm"},
{".ppsm", ".potx"},
{".ppsm", ".pptm"},
{".ppsm", ".pptx"},
{".ppsx", ".odp"},
{".ppsx", ".otp"},
{".ppsx", ".pdf"},
{".ppsx", ".potm"},
{".ppsx", ".potx"},
{".ppsx", ".pptm"},
{".ppsx", ".pptx"},
{".ppt", ".odp"},
{".ppt", ".otp"},
{".ppt", ".pdf"},
{".ppt", ".potm"},
{".ppt", ".potx"},
{".ppt", ".pptm"},
{".ppt", ".pptx"},
{".pptm", ".odp"},
{".pptm", ".otp"},
{".pptm", ".pdf"},
{".pptm", ".potm"},
{".pptm", ".potx"},
{".pptm", ".pptx"},
{".pptt", ".pptx"},
{".pptx", ".odp"},
{".pptx", ".otp"},
{".pptx", ".pdf"},
{".pptx", ".potm"},
{".pptx", ".potx"},
{".pptx", ".pptm"},
{".rtf", ".docm"},
{".rtf", ".docx"},
{".rtf", ".dotm"},
{".rtf", ".dotx"},
{".rtf", ".epub"},
{".rtf", ".fb2"},
{".rtf", ".html"},
{".rtf", ".odt"},
{".rtf", ".ott"},
{".rtf", ".pdf"},
{".rtf", ".txt"},
{".txt", ".docm"},
{".txt", ".docx"},
{".txt", ".dotm"},
{".txt", ".dotx"},
{".txt", ".epub"},
{".txt", ".fb2"},
{".txt", ".html"},
{".txt", ".odt"},
{".txt", ".ott"},
{".txt", ".pdf"},
{".txt", ".rtf"},
{".xls", ".csv"},
{".xls", ".ods"},
{".xls", ".ots"},
{".xls", ".pdf"},
{".xls", ".xlsm"},
{".xls", ".xlsx"},
{".xls", ".xltm"},
{".xls", ".xltx"},
{".xlsm", ".csv"},
{".xlsm", ".ods"},
{".xlsm", ".ots"},
{".xlsm", ".pdf"},
{".xlsm", ".xlsx"},
{".xlsm", ".xltm"},
{".xlsm", ".xltx"},
{".xlst", ".xlsx"},
{".xlsx", ".csv"},
{".xlsx", ".ods"},
{".xlsx", ".ots"},
{".xlsx", ".pdf"},
{".xlsx", ".xlsm"},
{".xlsx", ".xltm"},
{".xlsx", ".xltx"},
{".xlt", ".csv"},
{".xlt", ".ods"},
{".xlt", ".ots"},
{".xlt", ".pdf"},
{".xlt", ".xlsm"},
{".xlt", ".xlsx"},
{".xlt", ".xltm"},
{".xlt", ".xltx"},
{".xltm", ".csv"},
{".xltm", ".ods"},
{".xltm", ".ots"},
{".xltm", ".pdf"},
{".xltm", ".xlsm"},
{".xltm", ".xlsx"},
{".xltm", ".xltx"},
{".xltx", ".csv"},
{".xltx", ".ods"},
{".xltx", ".ots"},
{".xltx", ".pdf"},
{".xltx", ".xlsm"},
{".xltx", ".xlsx"},
{".xltx", ".xltm"},
{".xml", ".docm"},
{".xml", ".docx"},
{".xml", ".dotm"},
{".xml", ".dotx"},
{".xml", ".epub"},
{".xml", ".fb2"},
{".xml", ".html"},
{".xml", ".odt"},
{".xml", ".ott"},
{".xml", ".pdf"},
{".xml", ".rtf"},
{".xml", ".txt"},
{".xps", ".pdf"}
});
}

View File

@ -327,6 +327,10 @@ namespace ASC.MessagingSystem.Migrations.MySql.MessagesContextMySql
.HasColumnType("int")
.HasColumnName("tenant_id");
b.Property<int>("Active")
.HasColumnType("int")
.HasColumnName("active");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("char(38)")

View File

@ -81,6 +81,7 @@ public partial class MessagesContextMySql : Migration
.Annotation("MySql:CharSet", "utf8"),
date = table.Column<DateTime>(type: "datetime", nullable: false),
tenant_id = table.Column<int>(type: "int", nullable: false),
active = table.Column<int>(type: "int", nullable: false),
user_id = table.Column<string>(type: "char(38)", nullable: false, collation: "utf8_general_ci")
.Annotation("MySql:CharSet", "utf8"),
page = table.Column<string>(type: "varchar(300)", nullable: true, collation: "utf8_general_ci")

View File

@ -67,6 +67,10 @@ namespace ASC.MessagingSystem.Migrations.MySql.MessagesContextMySql
.HasColumnType("int")
.HasColumnName("tenant_id");
b.Property<int>("Active")
.HasColumnType("int")
.HasColumnName("active");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("char(38)")

View File

@ -495,7 +495,7 @@ namespace ASC.Core.Common.Migrations.MySql.UserDbContextMySql
Subject = "c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e",
Action = "77777777-32ae-425f-99b5-83176061d1ae",
Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|ea942538e68e49079394035336ee0ba8",
AceType = 0
AceType = AceType.Deny
},
new
{
@ -535,7 +535,7 @@ namespace ASC.Core.Common.Migrations.MySql.UserDbContextMySql
Subject = "c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e",
Action = "77777777-32ae-425f-99b5-83176061d1ae",
Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|28b10049dd204f54b986873bc14ccfc7",
AceType = 0
AceType = AceType.Deny
},
new
{
@ -543,7 +543,7 @@ namespace ASC.Core.Common.Migrations.MySql.UserDbContextMySql
Subject = "c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e",
Action = "77777777-32ae-425f-99b5-83176061d1ae",
Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|3cfd481b46f24a4ab55cb8c0c9def02c",
AceType = 0
AceType = AceType.Deny
},
new
{
@ -551,7 +551,7 @@ namespace ASC.Core.Common.Migrations.MySql.UserDbContextMySql
Subject = "c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e",
Action = "77777777-32ae-425f-99b5-83176061d1ae",
Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|6a598c7491ae437da5f4ad339bd11bb2",
AceType = 0
AceType = AceType.Deny
},
new
{
@ -559,7 +559,7 @@ namespace ASC.Core.Common.Migrations.MySql.UserDbContextMySql
Subject = "c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e",
Action = "77777777-32ae-425f-99b5-83176061d1ae",
Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|742cf945cbbc4a5782d61600a12cf8ca",
AceType = 0
AceType = AceType.Deny
},
new
{
@ -567,7 +567,7 @@ namespace ASC.Core.Common.Migrations.MySql.UserDbContextMySql
Subject = "c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e",
Action = "77777777-32ae-425f-99b5-83176061d1ae",
Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|853b6eb973ee438d9b098ffeedf36234",
AceType = 0
AceType = AceType.Deny
},
new
{
@ -576,6 +576,14 @@ namespace ASC.Core.Common.Migrations.MySql.UserDbContextMySql
Action = "77777777-32ae-425f-99b5-83176061d1ae",
Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|46cfa73af32046cf8d5bcd82e1d67f26",
AceType = 0
},
new
{
Tenant = -1,
Subject = "c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e",
Action = "77777777-32ae-425f-99b5-83176061d1ae",
Object = "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|37620ae5c40b45ce855a39dd7d76a1fa",
AceType = 0
});
});
@ -1425,12 +1433,6 @@ namespace ASC.Core.Common.Migrations.MySql.UserDbContextMySql
.UseCollation("utf8_general_ci")
.HasAnnotation("MySql:CharSet", "utf8");
b.Property<string>("PwdHashSha512")
.HasColumnType("varchar(512)")
.HasColumnName("pwdhashsha512")
.UseCollation("utf8_general_ci")
.HasAnnotation("MySql:CharSet", "utf8");
b.Property<int>("Tenant")
.HasColumnType("int")
.HasColumnName("tenant");

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