mirror of
https://gitlab.linphone.org/BC/public/flexisip-account-manager.git
synced 2026-01-17 18:08:06 +00:00
Compare commits
45 commits
2.1.0-alph
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a1f3471d6 | ||
|
|
85c939b4da | ||
|
|
f883c3cee7 | ||
|
|
06dc357524 | ||
|
|
4d601c4a9c | ||
|
|
09d386a303 | ||
|
|
0740bd0425 | ||
|
|
d179d0f6df | ||
|
|
25ddd330c1 | ||
|
|
e0f33da4ac | ||
|
|
ae4a651f2a | ||
|
|
593d7ce5c0 | ||
|
|
6fcde1b467 | ||
|
|
789c27f654 | ||
|
|
a53910d364 | ||
|
|
abcc9c1c7b | ||
|
|
26aaab2f07 | ||
|
|
fe265a972d | ||
|
|
98d9d76225 | ||
|
|
9aeeb0fa73 | ||
|
|
57e09cc4de | ||
|
|
ed28e8fe55 | ||
|
|
689140a553 | ||
|
|
7221d55ff8 | ||
|
|
0e3b0d36d8 | ||
|
|
10cdbb4b6a | ||
|
|
b0de4841f6 | ||
|
|
a98c8764d5 | ||
|
|
fbf47fc9c9 | ||
|
|
6240f81f14 | ||
|
|
edbe49d404 | ||
|
|
d8f0c47d8f | ||
|
|
6770e198d9 | ||
|
|
5f22c8c862 | ||
|
|
a56de2e93a | ||
|
|
38f0120ecc | ||
|
|
abe67c9734 | ||
|
|
ee2c9fed8f | ||
|
|
e0a9b75923 | ||
|
|
2a3634d461 | ||
|
|
2ec4f488b6 | ||
|
|
60df61d508 | ||
|
|
9a9b8ab34e | ||
|
|
a876a8cf82 | ||
|
|
a5eeb06055 |
221 changed files with 5965 additions and 3537 deletions
|
|
@ -14,6 +14,14 @@ rocky9-deploy:
|
|||
- rocky9-package
|
||||
- rocky9-test
|
||||
|
||||
rocky10-deploy:
|
||||
extends: .deploy
|
||||
script:
|
||||
- ./deploy_packages.sh rockylinux 10
|
||||
needs:
|
||||
- rocky10-package
|
||||
- rocky10-test
|
||||
|
||||
debian12-deploy:
|
||||
extends: .deploy
|
||||
script:
|
||||
|
|
@ -22,26 +30,24 @@ debian12-deploy:
|
|||
- debian12-package
|
||||
- debian12-test
|
||||
|
||||
debian13-deploy:
|
||||
extends: .deploy
|
||||
script:
|
||||
- ./deploy_packages.sh debian trixie
|
||||
needs:
|
||||
- debian13-package
|
||||
- debian13-test
|
||||
|
||||
remi-rocky8-deploy:
|
||||
extends: .deploy
|
||||
rules:
|
||||
- changes:
|
||||
- .gitlab-ci.yml
|
||||
#rules:
|
||||
#- changes:
|
||||
# - .gitlab-ci.yml
|
||||
script:
|
||||
- ./deploy_packages.sh rockylinux 8
|
||||
needs:
|
||||
- remi-rocky8-package
|
||||
|
||||
remi-rocky9-deploy:
|
||||
extends: .deploy
|
||||
rules:
|
||||
- changes:
|
||||
- .gitlab-ci.yml
|
||||
script:
|
||||
- ./deploy_packages.sh rockylinux 9
|
||||
needs:
|
||||
- remi-rocky9-package
|
||||
|
||||
.deploy:
|
||||
stage: deploy
|
||||
tags: ["docker"]
|
||||
|
|
|
|||
|
|
@ -4,12 +4,6 @@ rocky8-package:
|
|||
extends: .package
|
||||
image: gitlab.linphone.org:4567/bc/public/docker/rocky8-php:$ROCKY_8_IMAGE_VERSION
|
||||
script:
|
||||
# We install this dependency only for the pipeline
|
||||
- dnf -y install https://rpms.remirepo.net/enterprise/remi-release-8.rpm
|
||||
- dnf -y module reset php
|
||||
- dnf -y module enable php:remi-8.2
|
||||
- dnf -y update php\*
|
||||
- dnf -y install php-sodium
|
||||
- make package-el8
|
||||
|
||||
rocky9-package:
|
||||
|
|
@ -18,20 +12,28 @@ rocky9-package:
|
|||
extends: .package
|
||||
image: gitlab.linphone.org:4567/bc/public/docker/rocky9-php:$ROCKY_9_IMAGE_VERSION
|
||||
script:
|
||||
# We install this dependency only for the pipeline
|
||||
- dnf -y install https://rpms.remirepo.net/enterprise/remi-release-9.rpm
|
||||
- dnf -y module reset php
|
||||
- dnf -y module enable php:remi-8.2
|
||||
- dnf -y update php\*
|
||||
- dnf -y install php-sodium
|
||||
- make package-el9
|
||||
|
||||
rocky10-package:
|
||||
needs:
|
||||
- prepare-package
|
||||
extends: .package
|
||||
image: gitlab.linphone.org:4567/bc/public/docker/rocky10-php:$ROCKY_10_IMAGE_VERSION
|
||||
script:
|
||||
- make package-el10
|
||||
|
||||
debian12-package:
|
||||
needs:
|
||||
- prepare-package
|
||||
extends: .debian_package
|
||||
image: gitlab.linphone.org:4567/bc/public/docker/debian12-php:$DEBIAN_12_IMAGE_VERSION
|
||||
|
||||
debian13-package:
|
||||
needs:
|
||||
- prepare-package
|
||||
extends: .debian_package
|
||||
image: gitlab.linphone.org:4567/bc/public/docker/debian13-php:$DEBIAN_13_IMAGE_VERSION
|
||||
|
||||
.debian_package:
|
||||
extends: .package
|
||||
script:
|
||||
|
|
@ -46,21 +48,12 @@ remi-rocky8-package:
|
|||
- dnf -y module reset redis
|
||||
- dnf -y install @redis:6
|
||||
|
||||
remi-rocky9-package:
|
||||
image: gitlab.linphone.org:4567/bc/public/docker/rocky9-php:$ROCKY_9_IMAGE_VERSION
|
||||
extends: .remi-rocky-package
|
||||
variables:
|
||||
ROCKY_RELEASE: 9
|
||||
before_script:
|
||||
- dnf -y module reset redis
|
||||
- dnf -y install @redis:7
|
||||
|
||||
.remi-rocky-package:
|
||||
extends: .package
|
||||
rules:
|
||||
- if: $CI_COMMIT_REF_NAME =~ /^release/ || $CI_COMMIT_REF_NAME == "master"
|
||||
- changes:
|
||||
- .gitlab-ci.yml
|
||||
#- changes:
|
||||
# - .gitlab-ci.yml
|
||||
script:
|
||||
# Remi
|
||||
- mkdir -p $CI_PROJECT_DIR/build
|
||||
|
|
|
|||
|
|
@ -10,12 +10,18 @@ rocky9-test:
|
|||
needs:
|
||||
- rocky9-package
|
||||
|
||||
rocky10-test:
|
||||
extends: .rocky-test
|
||||
image: gitlab.linphone.org:4567/bc/public/docker/rocky10-php:$ROCKY_10_IMAGE_VERSION
|
||||
needs:
|
||||
- rocky10-package
|
||||
|
||||
.rocky-test:
|
||||
extends: .test
|
||||
script:
|
||||
- yum -y localinstall build/*.rpm
|
||||
- cd /opt/belledonne-communications/share/flexisip-account-manager/flexiapi
|
||||
- composer install --ignore-platform-req=ext-sodium # Rocky 8 use the external library
|
||||
- composer install --ignore-platform-req=ext-sodium # Rocky 8 and 9 use the external library
|
||||
- vendor/bin/phpcs
|
||||
- vendor/bin/phpmd . ansi phpmd.xml
|
||||
- php artisan key:generate
|
||||
|
|
@ -27,10 +33,16 @@ debian12-test:
|
|||
needs:
|
||||
- debian12-package
|
||||
|
||||
debian13-test:
|
||||
extends: .debian-test
|
||||
image: gitlab.linphone.org:4567/bc/public/docker/debian13-php:$DEBIAN_13_IMAGE_VERSION
|
||||
needs:
|
||||
- debian13-package
|
||||
|
||||
.debian-test:
|
||||
extends: .test
|
||||
script:
|
||||
- apt update
|
||||
#- apt update
|
||||
- apt install -y ./build/*.deb
|
||||
- cd /opt/belledonne-communications/share/flexisip-account-manager/flexiapi
|
||||
- composer install
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
variables:
|
||||
ROCKY_8_IMAGE_VERSION: 20250702_171834_update_rocky8_dockerhub
|
||||
ROCKY_9_IMAGE_VERSION: 20250702_171314_update_rocky9_dockerhub
|
||||
DEBIAN_12_IMAGE_VERSION: 20241204_162237_update_download_linphone_org
|
||||
ROCKY_10_IMAGE_VERSION: 20250908_164454_rocky10_first
|
||||
DEBIAN_12_IMAGE_VERSION: 20250908_154742_refresh_dependencies
|
||||
DEBIAN_13_IMAGE_VERSION: 20251204_115628_update_packages
|
||||
PHP_REDIS_REMI_VERSION: php-pecl-redis6-6.1.0-1
|
||||
PHP_IGBINARY_REMI_VERSION: php-pecl-igbinary-3.2.16-2
|
||||
PHP_MSGPACK_REMI_VERSION: php-pecl-msgpack-2.2.0-3
|
||||
|
|
|
|||
356
CHANGELOG.md
356
CHANGELOG.md
|
|
@ -1,231 +1,157 @@
|
|||
# Flexisip Account Manager Changelog
|
||||
# Releases
|
||||
|
||||
v2.0
|
||||
----
|
||||
- Fix FLEXIAPI-205 Remove the deprecated endpoints, compatibility code documentation and tests. Drop the confirmation_key accounts column and activation_expirations table
|
||||
- Fix FLEXIAPI-206 Upgrade to Laravel 10, PHP 8.1 minimum and bump all the related dependencies, drop Debian 11 Bullseye
|
||||
- Fix FLEXIAPI-220 Migrate SIP Domains to Spaces
|
||||
- Fix GH-15 Add password import from CSV
|
||||
- Fix FLEXIAPI-242 Add stricter validation for the AccountCreationToken Push Notification endpoint
|
||||
- Fix FLEXIAPI-241 Add a /push-notification endpoint to send custom push notifications to the Flexisip Pusher
|
||||
- Fix FLEXIAPI-244 Remove faulty middleware
|
||||
- Fix FLEXIAPI-250 Allow Spaces to be declared without a subdomain
|
||||
- Fix FLEXIAPI-252 Update the hCaptcha Laravel library, use file instead of cookies to store the session to prevent empty errors bags
|
||||
- Fix FLEXIAPI-254 Allow no data on POST requests to not trigger the ValidateJSON middleware
|
||||
- Fix FLEXIAPI-255 Create a INSTALL.md tutorial and log FlexisipPusherConnector errors
|
||||
- Fix FLEXIAPI-257 Return a more coherent message when search API endpoints returns a 404
|
||||
- Fix FLEXIAPI-260 Return 404 and not 403 if the contact is already in the list or missing when removing it
|
||||
- Fix FLEXIAPI-262 Bypass the JWT auth if we have an API Key
|
||||
- Fix FLEXIAPI-264 Add -k|api_key_ip parameter to accounts:create-admin-account to set/clear the related API Key restriction
|
||||
- Fix FLEXIAPI-256 Publish an empty string while deleting a device on Redis to force the refresh on the other clients
|
||||
- Fix FLEXIAPI-268 Allow pn-param in Apple format for the push notifications endpoints
|
||||
- Fix FLEXIAPI-269 Update the IsNotPhoneNumber rule to use a better phone number validator
|
||||
- Fix FLEXIAPI-258 Move DotEnv instance configurations in the Spaces table
|
||||
- Fix FLEXIAPI-270 Call the static $apnsTypes attribute in FlexisipPusherConnector
|
||||
- Fix FLEXIAPI-271 Handle properly reversed attributes in objects
|
||||
- Fix FLEXIAPI-237 Add internationalisation support in the app
|
||||
- Fix FLEXIAPI-261 Remove the TURN part in the XML provisioning (and only keep the API endpoint)
|
||||
- Fix FLEXIAPI-275 Add names in Spaces
|
||||
- Fix FLEXIAPI-278 Complete and reorganize the Markdown documentation
|
||||
- Fix FLEXIAPI-233 Add External Accounts (new version)
|
||||
- Fix FLEXIAPI-277 Restrict authorized ini keys that can be set to prevent conflict with the existing ones set in the UI
|
||||
- Fix FLEXIAPI-272 Add Space based email server integration
|
||||
- Fix FLEXIAPI-284 Add configurable admin API Keys
|
||||
- Fix FLEXIAPI-232 Add provisioning email + important redesign of the contacts page
|
||||
- Fix FLEXIAPI-287 Refactor the emails templates
|
||||
- Fix FLEXIAPI-286 Send an account_recovery_token using a push notification and protect the account recovery using phone page with the account_recovery_token
|
||||
- Fix FLEXIAPI-293 Remove the (long) outdated general documentation
|
||||
- Fix FLEXIAPI-224 Add a console script to send Space Expiration emails
|
||||
- Fix FLEXIAPI-297 Fix PrId and CallId validations
|
||||
- Fix FLEXIAPI-305 Add specific error page for Space Expiration
|
||||
- Fix FLEXIAPI-169 Added missing selinux label to log files and storage directory
|
||||
- Fix FLEXIAPI-313 Fix the admin device deletion link, recover the missing...
|
||||
- Fix FLEXIAPI-318 Fix email recovery validation
|
||||
- Fix FLEXIAPI-319 Fix the admin device deletion link, recover the missing method
|
||||
- Fix FLEXIAPI-321 Disable the account creation button when the Space is full for admins
|
||||
- Fix FLEXIAPI-322 Api Keys documentation
|
||||
- Fix FLEXIAPI-328 Set realm on Space creation, limit the update if some accounts are present
|
||||
- Fix FLEXIAPI-325 Add endpoints to send the password reset and provisioning emails
|
||||
- Fix FLEXIAPI-332 Check if the first line was untouched and that the number of columns is exact on each lines
|
||||
- Fix FLEXIAPI-329 Use correct routes for accounts devices
|
||||
- Fix FLEXIAPI-330 Remove the ConfirmedRegistration email and related code
|
||||
- Fix FLEXIAPI-324 Add an app setup wizard page
|
||||
- Fix FLEXIAPI-335 Safari rendering issues with font icons
|
||||
- Fix FLEXIAPI-336 Fix broken ph icons
|
||||
- Fix FLEXIAPI-333 Remove HTML buttons because they cannot be rendered in "old" Outlook versions
|
||||
- Fix FLEXIAPI-337 Generate the provisioning URLs based on the user space
|
||||
- Fix FLEXIAPI-326 Rework email templates and translations
|
||||
- Fix FLEXIAPI-340 Fix the space resolution when getting the realm on Accounts
|
||||
- Fix FLEXIAPI-341 Allow realm to be empty when creating a Space
|
||||
- Fix FLEXIAPI-342 Enforce password change when the External Account domain is changed
|
||||
- Fix FLEXIAPI-346 Complete the supporting text for the provisioning ini field
|
||||
- Fix FLEXIAPI-350 Fix wrongly assigned variables in some views
|
||||
- Fix FLEXIAPI-351 Fix import of CSV generated on Windows
|
||||
- Fix FLEXIAPI-352 Add missing errors box in the password change form
|
||||
- Fix FLEXIAPI-356 Cleanup and reorganize the pipeline to mutualize some things and save time
|
||||
- Fix FLEXIAPI-355 Add withoutGlobalScope() to the Account ContactVcardList resolver
|
||||
- Fix FLEXIAPI-354 Fix contact deletion
|
||||
- Fix FLEXIAPI-360 Add rules on some jobs to only run them in the Gitlab pipeline when needed
|
||||
- Fix FLEXIAPI-362 Return an empty object and not an empty array in the vcards-storage index endpoint to prevent some parsing issues in the clients
|
||||
- Fix FLEXIAPI-312 Add Redis publish event when updating the externalAccount to ping the Flexisip B2BUA
|
||||
- Fix FLEXIAPI-363 Send the Redis publish event when the externalAccount is deleted to ping the Flexisip B2BUA
|
||||
- Fix FLEXIAPI-364 Fix a faulty redirection in the ExternalAccount controller
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
v1.6
|
||||
----
|
||||
- Fix FLEXIAPI-192 Add DotEnv configuration to allow the expiration of tokens and codes in the app
|
||||
- Fix FLEXIAPI-196 Add a phone validation system by country code with configuration panels and related tests and documentation
|
||||
- Fix FLEXIAPI-203 Implement domain based Linphone configuration, add documentation, complete API endpoints, complete provisioning XML
|
||||
- Fix FLEXIAPI-208 Add SMS templates documentation
|
||||
- Fix FLEXIAPI-211 Add a JSON validation middleware + test
|
||||
- Fix FLEXIAPI-212 Add CoTURN credentials support in the provisioning
|
||||
- Fix FLEXIAPI-213 Add TURN credentials support in the API as defined in draft-uberti-behave-turn-rest-00
|
||||
- Fix FLEXIAPI-216 Implement the RFC 8898 partially... for HTTP
|
||||
- Fix FLEXIAPI-239 Ensure to return the correct error codes as stated in the RFC6750 section 3.1
|
||||
- Fix FLEXIAPI-238 Replace Material Icons with Phosphor
|
||||
- Fix FLEXIAPI-240 Update the Docker images
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
||||
|
||||
v1.5
|
||||
---
|
||||
- Fix FLEXIAPI-202 Add account parameter to the redirection in the destroy admin route
|
||||
- Fix FLEXIAPI-195 Fix LiblinphoneTesterAccoutSeeder to fit with the latest Account related changes
|
||||
- Fix FLEXIAPI-193 Typo
|
||||
- Fix FLEXIAPI-192 Clear and upgrade properly the account dictionary entries if the entries are already existing
|
||||
- Fix FLEXIAPI-191 Add quotes for the pn-prid parameter in FlexisipPusherConnector
|
||||
- Fix FLEXIAPI-186 Ensure that empty objects are serialized in JSON as objects and not empty arrays
|
||||
- Fix FLEXIAPI-185 Return null if the account dictionary is empty in the API
|
||||
- Fix FLEXIAPI-184 Append phone_change_code and email_change_code to the admin /accounts/<id> endpoint if they are available
|
||||
- Fix FLEXIAPI-183 Complete the account hooks on the dictionnary actions
|
||||
- Fix FLEXIAPI-182 Replace APP_SUPER_ADMINS_SIP_DOMAINS with a proper spaces table, API endpoints, UI panels, console command, tests and documentation
|
||||
- Fix FLEXIAPI-181 Replace APP_ADMINS_MANAGE_MULTI_DOMAINS with APP_SUPER_ADMINS_SIP_DOMAINS
|
||||
- Fix FLEXIAPI-180 Fix the token and activation flow for the provisioning with token endpoint when the header is missing
|
||||
- Fix FLEXIAPI-179 Add Localization support as a Middleware that handles Accept-Language HTTP header
|
||||
- Fix FLEXIAPI-178 Show the unused code in the Activity tab of the accounts in the admin panel
|
||||
- Fix FLEXIAPI-177 Complete vcards-storage and devices related endpoints with their User/Admin ones
|
||||
- Fix FLEXIAPI-176 Improve logs for the deprecated endpoints and AccountCreationToken related serialization
|
||||
- Fix FLEXIAPI-175 and FLEXISIP-231 Rewrite the Redis contacts parser to handle properly SIP uris (thanks @thibault.lemaire !)
|
||||
- Fix FLEXIAPI-174 Check if the phone is valid before trying to recover it (deprecated endpoint)
|
||||
- Fix FLEXIAPI-173 Wrong route in validateEmail (deprecated)
|
||||
- Fix FLEXIAPI-171 Fix README documentation for CreateAdminAccount
|
||||
- Fix FLEXIAPI-170 Fix undefined variable apiKey in CreateAdminAccount
|
||||
- Fix FLEXIAPI-168 Add POST /accounts/me/email to confirm the email change
|
||||
- Fix FLEXIAPI-167 Add the handling of a custom identifier for the JWT tokens on top of the email one
|
||||
- Fix FLEXIAPI-166 Reimplement the deprecated email validation URL
|
||||
- Fix FLEXIAPI-165 Remove for now text/vcard header constraint
|
||||
- Fix FLEXIAPI-164 Add vcards-storage endpoints
|
||||
- Fix FLEXIAPI-163 Complete AccountService hooks
|
||||
- Fix FLEXIAPI-162 Drop the aliases table and migrate the data to the phone column
|
||||
- Fix FLEXIAPI-161 Complete the Dictionary tests to cover the collection accessor
|
||||
- Fix FLEXIAPI-159 Add the account_creation_tokens/consume endpoint
|
||||
- Fix FLEXIAPI-158 Restrict the phone number change API endpoint to return 403 if the account doesn't have a validated Account Creation Token
|
||||
- Fix FLEXIAPI-156 Disable the Phone change web form when PHONE_AUTHENTICATION is disabled
|
||||
- Fix FLEXIAPI-155 Add a new accountServiceAccountUpdatedHook and accountServiceAccountDeletedHook
|
||||
- Fix FLEXIAPI-153 Add phone and email to be changed in the Activity panel
|
||||
- Fix FLEXIAPI-152 API Key usage clarification
|
||||
- Fix FLEXIAPI-151 Migrate to hCaptcha
|
||||
- Fix FLEXIAPI-150 Use the same account_id parameter for both API and Web routes
|
||||
- Fix FLEXIAPI-149 Add a toggle to disable phone check on username for admin endpoints and forms
|
||||
- Fix FLEXIAPI-148 Reuse AccountService in the POST /api/accounts admin endpoint
|
||||
- FIX FLEXIAPI-146 Allow users to manage their own devices
|
||||
- Fix FLEXIAPI-145 Put back the 'code' parameter as an alias for the 'confirmation_key' for the activateEmail and activatePhone endpoints
|
||||
- Fix FLEXIAPI-144 Introduce APP_FLEXISIP_PUSHER_FIREBASE_KEYSMAP as a replacement for APP_FLEXISIP_PUSHER_FIREBASE_KEY
|
||||
- Fix FLEXIAPI-143 JWT Authentication layer on the API
|
||||
- Fix FLEXIAPI-142 PUT /accounts endpoint doesn't allow overiding values anymore
|
||||
- Fix FLEXIAPI-140 Fix the display_name attribute in the Vcard4 render
|
||||
- Fix FLEXIAPI-139 Refactor the email and phone API documentation
|
||||
- Fix FLEXIAPI-138 Add ip and user_agent columns to all the tokens and code tables, fill the values when required and display them in the admin
|
||||
- Fix FLEXIAPI-136 Refactor the Web Panel toggle mechanism and move it to a proper Middleware
|
||||
- Fix FLEXIAPI-135 Merge the admins table in the accounts table
|
||||
- Fix FLEXIAPI-134 Add a system to detect and block abusive accounts
|
||||
- Fix FLEXIAPI-133 Use the correct breadcrumb on create and fix a password
|
||||
- Fix FLEXIAPI-132 Refactor the Provisioning to remove proxy_default_values
|
||||
- Fix #143 Ensure that the ProvisioningToken model behave likes all the other Consommable
|
||||
- Fix #141 Add a new hook system for the Account Service
|
||||
- Fix #138 Add a dictionary attached to the accounts
|
||||
- Fix #137 Migrate the icons from Material Icons to Material Symbols
|
||||
- Fix #135 Refactor the password algorithms code
|
||||
- Fix #134 Create an Activity view in the Admin > Accounts panel
|
||||
- Fix #133 Make the MySQL connection unstrict
|
||||
- Fix #132 Move the provisioning_tokens and recovery_codes to dedicated table
|
||||
- Fix #130 Drop the group column in the Accounts table
|
||||
## [2.1]
|
||||
|
||||
v1.4.9
|
||||
------
|
||||
- Complete the missing changelog
|
||||
### Added
|
||||
|
||||
v1.4.8
|
||||
------
|
||||
- Fix FLEXIAPI-166 Reimplement the deprecated email validation URL
|
||||
- Fix FLEXIAPI-140 Select the display_name attribute from the database to inject...
|
||||
- **Add CardDav servers** They can be configured in the administration panels and the API.
|
||||
- **Rockylinux 10 support** Packages are now available in the official repository
|
||||
- **Artisan cleanup script for statistics** Add an artisan console script to clear statistics after n days `app:clear-statistics {days} {--apply}`
|
||||
- **Add Voicemail features and related API endpoints** to integrate with `flexisip-voicemail`
|
||||
|
||||
v1.4.7
|
||||
------
|
||||
- Fix FLEXIAPI-175 and FLEXISIP-231 Rewrite the Redis contacts parser to handle properly SIP uris (thanks @thibault.lemaire !)
|
||||
### Changed
|
||||
|
||||
v1.4.6
|
||||
------
|
||||
- Fix FLEXIAPI-142 PUT /accounts endpoint doesn't allow overiding values anymore
|
||||
- Fix typos and dependencies
|
||||
- **Contacts Lists** The Contacts Lists are now handled per Space. During the migration, if there is only one Space present, existing Contacts Lists are automatically attached to it, otherwise the first Super Space available is used. If they are then attached to the wrong Space you'll have to change directly their `space_id` value in the `contacts_lists` database table.
|
||||
- **PHP 8.2 minimum** Laravel and its dependencies were upgraded to version 11 as well.
|
||||
- **Logout the user when the password is correctly changed**
|
||||
|
||||
v1.4.5
|
||||
------
|
||||
- Fix FLEXIAPI-132 Refactor the Provisioning to remove proxy_default_values
|
||||
## [2.0]
|
||||
|
||||
v1.4.4
|
||||
------
|
||||
- Fix FLEXIAPI-136 Refactor the Web Panel toggle mechanism and move it to a proper Middleware
|
||||
### Added
|
||||
|
||||
v1.4.3
|
||||
------
|
||||
- Fix FLEXIAPI-133 Use the correct breadcrumb on create and fix a password update related issue on update
|
||||
- **Spaces:** A new way to manage your SIP domains and hosts. A Space is defined by a unique SIP Domain and Host pair.
|
||||
- **New mandatory DotEnv variable** `APP_ROOT_HOST`, replaces `APP_URL` and `APP_SIP_DOMAIN` that are now configured using the new dedicated Artisan script. It defines the root hostname where all the Spaces will be configured. All the Spaces will be as subdomains of `APP_ROOT_HOST` except one that can be equal to `APP_ROOT_HOST`. Example: if `APP_ROOT_HOST=myhost.com` the Spaces hosts will be `myhost.com`, `alpha.myhost.com` , `beta.myhost.com`...
|
||||
- **New DotEnv variable:** `APP_ACCOUNT_RECOVERY_TOKEN_EXPIRATION_MINUTES=0` Number of minutes before expiring the recovery tokens
|
||||
- **New Artisan script** `php artisan spaces:create-update {sip_domain} {host} {name} {--super}`, replaces `php artisan sip_domains:create-update {sip_domain} {--super}`. Can create a Space or update a Space Host base on its Space SIP Domain.
|
||||
- **Push Notification endpoint** Add a /push-notification endpoint to send custom push notifications to the Flexisip Pusher
|
||||
- **Add internationalisation support in the app** The web panels are now available in French and English
|
||||
- **Add External Accounts** In the API and web panels, allowing users to setup an external account that can be used in another service like the B2BUA
|
||||
- **Add configurable admin API Keys** Allowing admins to setup non-expiring services API Keys
|
||||
- **Add provisioning email** A user can now receive a custom generated email with all the provisioning related information
|
||||
- **Add API endpoints to send the password reset and provisioning emails**
|
||||
- **Add an app setup wizard page** Static web page inviting the users to download the app if it is not installed yet
|
||||
|
||||
v1.4.2
|
||||
------
|
||||
- Fix #135 Refactor the password algorithms code
|
||||
### Changed
|
||||
|
||||
v1.4.1
|
||||
------
|
||||
- Fix #133 Make the MySQL connection unstrict
|
||||
- **Complete and reorganize the Markdown documentation**
|
||||
- **Refactor the emails templates** All the emails were modernized and are now generated in HTML
|
||||
|
||||
v1.4
|
||||
----
|
||||
- Redesign and refactoring of the main UI and panel flows
|
||||
- Complete the statistics and add a specific API to get usage statistics from FlexiAPI
|
||||
- Removal of XMLRPC
|
||||
- Add RockyLinux 9 support
|
||||
- Add Debian 12 to CI
|
||||
- Fix #122 Add a new console command CreateFirstAdmin
|
||||
- Fix #121 Only apply throttling to redeemed tokens
|
||||
- Fix #123 Define a proper documentation for the provisioning flow
|
||||
- Fix #124 Return 404 when the account is already provisioned or the provisioning_token not valid
|
||||
- Fix #125 Remove the External Accounts feature
|
||||
- Fix #19 Set all the ERROR confirmation_key to null in the accounts table
|
||||
### Removed
|
||||
|
||||
v1.3
|
||||
----
|
||||
- Fix #90 Deploy packages from release branches as well
|
||||
- Fix #58 Fix the packaging process to use git describe as a reference
|
||||
- Fix #58 Move the generated packages in the build directory, and fix the release and version format in the .spec
|
||||
- Fix #58 Refactor and cleanup the .gitlab-ci file
|
||||
- Move the minimum PHP version to 8.0
|
||||
- Fix #47 Move the docker to an external repository
|
||||
- Fix #83 Add php-redis-remi package
|
||||
- Fix #85 Also package php-pecl-igbinary and php-pecl-msgpack from remi
|
||||
- Fix #84 Remove CentOS7 from the pipeline
|
||||
- Fix #80 Inject provisioning link and QRCode in the default email with a password_reset parameter
|
||||
- Fix #79 Add a refresh_password parameter to the provisioning URLs
|
||||
- Fix #78 Add a APP_ACCOUNTS_EMAIL_UNIQUE environnement setting
|
||||
- Fix #30 Remove APP_EVERYONE_IS_ADMIN
|
||||
- **Remove the deprecated endpoints** The endpoints inherited from XMLRPC are now completely removed, the following variable can be removed:
|
||||
- APP_DANGEROUS_ENDPOINTS
|
||||
- APP_PROJECT_URL
|
||||
- **Removing and moving DotEnv instance environnement variables to the Spaces** The following DotEnv variables were removed. You can now configure them directly in the designated spaces after the migration.
|
||||
- INSTANCE_COPYRIGHT
|
||||
- INSTANCE_INTRO_REGISTRATION
|
||||
- INSTANCE_CONFIRMED_REGISTRATION_TEXT
|
||||
- INSTANCE_CUSTOM_THEME
|
||||
- WEB_PANEL
|
||||
- PUBLIC_REGISTRATION
|
||||
- PHONE_AUTHENTICATION
|
||||
- DEVICES_MANAGEMENT
|
||||
- INTERCOM_FEATURES
|
||||
- NEWSLETTER_REGISTRATION_ADDRESS
|
||||
- ACCOUNT_PROXY_REGISTRAR_ADDRESS
|
||||
- ACCOUNT_TRANSPORT_PROTOCOL_TEXT
|
||||
- ACCOUNT_REALM
|
||||
- ACCOUNT_PROVISIONING_RC_FILE
|
||||
- ACCOUNT_PROVISIONING_OVERWRITE_ALL
|
||||
- ACCOUNT_PROVISIONING_USE_X_LINPHONE_PROVISIONING_HEADER
|
||||
- **Enforce the session and cache in the configuration** The following variables can be removed from your DotEnv file as well:
|
||||
- SESSION_DRIVER
|
||||
- CACHE_DRIVER
|
||||
|
||||
v1.2
|
||||
----
|
||||
### Migrate from [1.6]
|
||||
|
||||
- Introduce FlexiAPI built on Laravel to replace XMLRPC
|
||||
- Deprecates XMLRPC (will be removed in the 2.0 release)
|
||||
- Create a REST API to manage the accounts, related features and provisioning
|
||||
- Create a user web panel for their account management, currently in testing phase (unstable)
|
||||
- Create an admin web panel to manage accounts and related features
|
||||
- Allow accounts to be exported as ExternalAccounts and imported in another Flexisip Account Manager instance
|
||||
- Add various artisan console commands to maintain the data (cleaning up, importing, exporting, seeding)
|
||||
- Add unit tests for the FlexiAIP REST API
|
||||
- Rebuild the existing database using the Laravel migration scripts
|
||||
1. Deploy the new version and migrate the database.
|
||||
|
||||
```
|
||||
php artisan migrate
|
||||
```
|
||||
|
||||
2. Set `APP_ROOT_HOST` in `.env` or as an environnement variable. And remove `APP_URL` and `APP_SIP_DOMAIN`
|
||||
|
||||
```
|
||||
APP_ROOT_HOST=myhost.com
|
||||
```
|
||||
|
||||
3. The migration script will automatically copy the `sip_domain` into `host` in the `spaces` table. You then have to "fix" the hosts and set them to equal or be subdomains of `APP_ROOT_HOST`.
|
||||
|
||||
```
|
||||
php artisan spaces:create-update my.sip myhost.com "My Super Space" --super # You can set some Spaces as SuperSpaces, the admin will be able to manage the other spaces
|
||||
php artisan spaces:create-update alpha.sip alpha.myhost.com "Alpha Space"
|
||||
php artisan spaces:create-update beta.sip beta.myhost.com "Beta Space"
|
||||
...
|
||||
```
|
||||
|
||||
4. Configure your web server to point the `APP_ROOT_HOST` and subdomains to the app. See the related documentation in [`INSTALL.md` file](INSTALL.md#31-mandatory-app_root_host-variable).
|
||||
|
||||
5. Configure your Spaces.
|
||||
|
||||
6. (Optional) Import the old instance DotEnv environnement variables into a space.
|
||||
|
||||
7. Remove the instance based environnement variables (see **Changed** above) and configure them directly in the spaces using the API or Web Panel.
|
||||
|
||||
⚠️ Be careful, during this import only the project DotEnv file variables will be imported, other environnement (eg. set in Apache, nginx or Docker) will be ignored.
|
||||
|
||||
⚠️ The content of the `ACCOUNT_PROVISIONING_RC_FILE` will not be imported. You will have to extract the sections and lines that you want to use manually using the dedicated form or the API.
|
||||
|
||||
```
|
||||
php artisan spaces:import-configuration-from-dot-env {sip_domain}
|
||||
```
|
||||
|
||||
You can find more details regarding those steps in the [`INSTALL.md`](INSTALL.md) and [`README.md`](README.md) files.
|
||||
|
||||
|
||||
## [1.6] - 2024-12-30
|
||||
|
||||
### Added
|
||||
|
||||
- **Allow the expiration of tokens and codes in the DotEnv configuration**
|
||||
- **New DotEnv variables:** check all the new `*_EXPIRATION_MINUTES` for each token and code in `.env.example`
|
||||
- **Phone validation system by country code:** all the provided phone numbers are now properly validated and some countries can be forbidden
|
||||
- **SIP Domain management:** the account domains are now managed in a set of panels and API endpoints, this is the base of the upcoming space administration system
|
||||
- **JSON validation in the API:** the provised JSON is now validated and returns an error if an issue is detected
|
||||
- **CoTURN credentials support:** TURN credentials can now be generated and return through the provisioning feature
|
||||
- **RFC 8898 Support**
|
||||
|
||||
## Changed
|
||||
|
||||
- **Replace Material Icons with Phosphor**
|
||||
|
||||
## Deprecated
|
||||
|
||||
- **Last major version supporting the deprecated endpoints of the API**
|
||||
|
||||
### Migrate from [1.5]
|
||||
|
||||
Nothing specific to do
|
||||
|
||||
## [1.5] - 2024-08-29
|
||||
|
||||
### Added
|
||||
|
||||
- **Account activity view:** new panel, available behind the Activity tab, will allow any admin to follow the activity of the accounts they manage.
|
||||
- **Detect and block abusive accounts:** This activity tracking is coming with a related tool that is measuring the accounts activity and automatically block them if it detects some unusual behaviors on the service. An account can also directly be blocked and unblocked from the setting panel. Two new setting variables will allow you to fine tune those behaviors triggers.
|
||||
- **New DotEnv variable:** `BLOCKING_TIME_PERIOD_CHECK=30` Time span on which the blocking service will proceed, in minutes
|
||||
- **New DotEnv variable:** `BLOCKING_AMOUNT_EVENTS_AUTHORIZED_DURING_PERIOD=5` Amount of account events authorized during this period
|
||||
- **OAuth JWT Authentication:** OAuth support with the handling of JWE tokens issues by a third party service such as Keycloack.
|
||||
- **New DotEnv variable:** `JWT_RSA_PUBLIC_KEY_PEM=`
|
||||
- **New DotEnv variable:** `JWT_SIP_IDENTIFIER=sip_identifier`
|
||||
- **Super-domains and super-admins support:** Introduce SIP domains management. The app accounts are now divided by their domains with their own respective administrators that can only see and manage their own domain accounts and settings. On top of that it is possible to configure a SIP domain as a "super-domain" and then allow its admins to become "super-admins". Those super-admins will then be able to manage all the accounts handled by the instance and create/edit/delete the other SIP domains. Add new endpoints and a new super-admin role in the API to manage the SIP domains. SIP domains can also be created and updated directly from the console using a new artisan script (documented in the README);
|
||||
- **New Artisan script:** `php artisan sip_domains:create-update {domain} {--super}`
|
||||
- **Account Dictionary:** Each account can now handle a specific dictionary, configurable by the API or directly the web panel. This dictionary allows developers to store arbitrary `key -> value pairs` on each accounts.
|
||||
- **Vcard storage:** Attach custom vCards on a dedicated account using new endpoints in the API. The published vCard are validated before being stored.
|
||||
|
||||
### Changed
|
||||
|
||||
- **User management of their own devices:** Allowing users will be able to manage its own devices. Specific API endpoints were also added to manage them directly from the clients.
|
||||
- **Migration to hCaptcha:** Migrate from Google Recaptcha to hCaptcha in this release.
|
||||
- **New DotEnv variable:** HCAPTCHA_SECRET=secret-key
|
||||
- **New DotEnv variable:** HCAPTCHA_SITEKEY=site-key
|
||||
- **Localization support:** The API is now accepting the `Accept-Language` header and adapt its internal localization to the client/browser one. For the moment only French and English are supported but more languages could be added in the future.
|
||||
|
|
|
|||
10
INSTALL.md
10
INSTALL.md
|
|
@ -24,6 +24,14 @@ FlexiAPI is packaged for Debian and RedHat, you can setup those repositories usi
|
|||
|
||||
The `artisan` script is in the root directory of where the application is setup, with packages its often `/opt/belledonne-communications/share/flexisip-account-manager/flexiapi/`.
|
||||
|
||||
⚠️ If you want to enable JWT authentication the php-sodium dependency is required, on Rockylinux it is only available in the Remi repository in some cases. You can install it with the following steps:
|
||||
|
||||
dnf -y install https://rpms.remirepo.net/enterprise/remi-release-{rockylinux-release}.rpm
|
||||
dnf -y module reset php
|
||||
dnf -y module enable php:remi-{php-version}
|
||||
dnf -y update php\*
|
||||
dnf -y install php-sodium
|
||||
|
||||
# 2. Web server configuration
|
||||
|
||||
The package will deploy a `flexisip-account-manager.conf` file in the apache2 configuration directory.
|
||||
|
|
@ -31,6 +39,8 @@ This file can be loaded and configured in your specific VirtualHost configuratio
|
|||
|
||||
To know more about the web server configuration part, you can directly [visit the official Laravel installation documentation](https://laravel.com/docs/).
|
||||
|
||||
⚠️ The Account Manager is handling files upload, please ensure that you raised `upload_max_filesize` and `post_max_size` to a reasonable number in your `php.ini` file to prevent file upload errors.
|
||||
|
||||
# 3. .env file configuration
|
||||
|
||||
Complete all the variables in the `.env` file (from the `.env.example` one if you setup the instance manually) or by overwriting them in your Docker or web-server configuration.
|
||||
|
|
|
|||
21
Makefile
21
Makefile
|
|
@ -50,7 +50,7 @@ package-common:
|
|||
cp -R cron/ $(OUTPUT_DIR)/flexisip-account-manager/
|
||||
cp flexisip-account-manager.spec.run $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec
|
||||
|
||||
tar cvf flexisip-account-manager.tar.gz -C $(OUTPUT_DIR) flexisip-account-manager
|
||||
tar cf flexisip-account-manager.tar.gz -C $(OUTPUT_DIR) flexisip-account-manager
|
||||
mv flexisip-account-manager.tar.gz $(OUTPUT_DIR)/rpmbuild/SOURCES/flexisip-account-manager.tar.gz
|
||||
|
||||
package-end-common:
|
||||
|
|
@ -59,15 +59,20 @@ package-end-common:
|
|||
|
||||
rpm-el8-only:
|
||||
mkdir -p build
|
||||
sed -i 's/Requires:.*/Requires: php >= 8.1, php-gd, php-pdo, php-redis, php-mysqlnd, php-mbstring/g' $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec
|
||||
rpmbuild -v -bb --define 'dist .el8' --define '_topdir $(OUTPUT_DIR)/rpmbuild' --define "_rpmdir $(OUTPUT_DIR)/rpmbuild" $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec
|
||||
sed -i 's/Requires:.*/Requires: php >= 8.2, php-gd, php-pdo, php-redis, php-mysqlnd, php-mbstring/g' $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec
|
||||
rpmbuild --quiet -bb --define 'dist .el8' --define '_topdir $(OUTPUT_DIR)/rpmbuild' --define "_rpmdir $(OUTPUT_DIR)/rpmbuild" $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec
|
||||
@echo "📦✅ RPM el8 Package Created"
|
||||
|
||||
rpm-el9-only:
|
||||
mkdir -p build
|
||||
rpmbuild -v -bb --define 'dist .el9' --define '_topdir $(OUTPUT_DIR)/rpmbuild' --define "_rpmdir $(OUTPUT_DIR)/rpmbuild" $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec
|
||||
rpmbuild --quiet -bb --define 'dist .el9' --define '_topdir $(OUTPUT_DIR)/rpmbuild' --define "_rpmdir $(OUTPUT_DIR)/rpmbuild" $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec
|
||||
@echo "📦✅ RPM el9 Package Created"
|
||||
|
||||
rpm-el10-only:
|
||||
mkdir -p build
|
||||
rpmbuild --quiet -bb --define 'dist .el10' --define '_topdir $(OUTPUT_DIR)/rpmbuild' --define "_rpmdir $(OUTPUT_DIR)/rpmbuild" $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec
|
||||
@echo "📦✅ RPM el10 Package Created"
|
||||
|
||||
rpm-cleanup:
|
||||
@echo "🧹 Cleanup"
|
||||
mv rpmbuild/*/*.rpm build/.
|
||||
|
|
@ -76,11 +81,11 @@ rpm-cleanup:
|
|||
deb-only:
|
||||
mkdir -p build
|
||||
sed -i 's/posttrans/post/g' $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec
|
||||
rpmbuild -v -bb --with deb --define '_topdir $(OUTPUT_DIR)/rpmbuild' --define "_rpmfilename tmp.rpm" --define "_rpmdir $(OUTPUT_DIR)/rpmbuild" $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec
|
||||
rpmbuild --quiet -bb --with deb --define '_topdir $(OUTPUT_DIR)/rpmbuild' --define "_rpmfilename tmp.rpm" --define "_rpmdir $(OUTPUT_DIR)/rpmbuild" $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec
|
||||
fakeroot alien -g -k --scripts $(OUTPUT_DIR)/rpmbuild/tmp.rpm
|
||||
rm -r $(OUTPUT_DIR)/rpmbuild
|
||||
rm -rf $(OUTPUT_DIR)/*.orig
|
||||
sed -i 's/Depends:.*/Depends: $${shlibs:Depends}, php (>= 8.1), php-xml, php-pdo, php-gd, php-redis, php-mysql, php-mbstring, php-sqlite3/g' $(OUTPUT_DIR)/bc-flexisip-account-manager*/debian/control
|
||||
sed -i 's/Depends:.*/Depends: $${shlibs:Depends}, php (>= 8.2), php-xml, php-pdo, php-gd, php-redis, php-mysql, php-mbstring, php-sqlite3/g' $(OUTPUT_DIR)/bc-flexisip-account-manager*/debian/control
|
||||
|
||||
cd `ls -rt $(OUTPUT_DIR) | tail -1` && dpkg-buildpackage --no-sign
|
||||
@echo "📦✅ DEB Package Created"
|
||||
|
|
@ -101,6 +106,10 @@ package-el9: rpm-el9-only rpm-cleanup cleanup-package-semvers package-end-common
|
|||
rpm-el9: prepare-common package-el9
|
||||
rpm-el9-dev: prepare-dev package-semvers package-common package-el9
|
||||
|
||||
package-el10: rpm-el10-only rpm-cleanup cleanup-package-semvers package-end-common
|
||||
rpm-el10: prepare-common package-el10
|
||||
rpm-el10-dev: prepare-dev package-semvers package-common package-el10
|
||||
|
||||
package-deb: deb-only cleanup-package-semvers package-end-common
|
||||
deb: prepare-common package-deb
|
||||
deb-dev: prepare-dev package-semvers package-common package-deb
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ Flexisip is dual licensed, and can be licensed and distributed:
|
|||
|
||||
# Documentation
|
||||
|
||||
Once deployed you can have access to the global and API documentation on the `/api` and `/documentation` pages.
|
||||
Once deployed you can have access to the global and API documentation on the `/api` and `/provisioning/documentation` pages.
|
||||
|
||||
# Setup
|
||||
|
||||
|
|
@ -25,7 +25,6 @@ Check the [INSTALL.md](INSTALL.md) and [CHANGELOG.md](CHANGELOG.md) files.
|
|||
|
||||
## Usage
|
||||
|
||||
For the web panel, a general documentation is available under the `/documentation` page.
|
||||
For the REST API, the `/api` page contains all the required documentation to authenticate and request the API.
|
||||
FlexiAPI is also providing endpoints to provision Liblinphone powered devices. You can find more documentation about it on the `/provisioning/documentation` documentation page.
|
||||
|
||||
|
|
@ -41,7 +40,7 @@ Create or update a Space, required to then create accounts afterward. The `super
|
|||
|
||||
### Import the old DotEnv instance configuration into a Space
|
||||
|
||||
Since 1.7 some environnement instance configuration variables were moved into the Space configuration, you can import them using this command.
|
||||
Since 2.0 some environnement instance configuration variables were moved into the Space configuration, you can import them using this command.
|
||||
|
||||
php artisan spaces:import-configuration-from-dot-env {sip_domain}
|
||||
|
||||
|
|
|
|||
115
RELEASE.md
115
RELEASE.md
|
|
@ -1,115 +0,0 @@
|
|||
# Releases
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
||||
|
||||
## [2.0]
|
||||
|
||||
### Added
|
||||
|
||||
- **Spaces:** A new way to manage your SIP domains and hosts. A Space is defined by a unique SIP Domain and Host pair.
|
||||
- **New mandatory DotEnv variable** `APP_ROOT_HOST`, replaces `APP_URL` and `APP_SIP_DOMAIN` that are now configured using the new dedicated Artisan script. It defines the root hostname where all the Spaces will be configured. All the Spaces will be as subdomains of `APP_ROOT_HOST` except one that can be equal to `APP_ROOT_HOST`. Example: if `APP_ROOT_HOST=myhost.com` the Spaces hosts will be `myhost.com`, `alpha.myhost.com` , `beta.myhost.com`...
|
||||
- **New DotEnv variable:** `APP_ACCOUNT_RECOVERY_TOKEN_EXPIRATION_MINUTES=0` Number of minutes before expiring the recovery tokens
|
||||
- **New Artisan script** `php artisan spaces:create-update {sip_domain} {host} {name} {--super}`, replaces `php artisan sip_domains:create-update {sip_domain} {--super}`. Can create a Space or update a Space Host base on its Space SIP Domain.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Removing and moving DotEnv instance environnement variables to the Spaces** The following DotEnv variables were removed. You can now configure them directly in the designated spaces after the migration.
|
||||
- INSTANCE_COPYRIGHT
|
||||
- INSTANCE_INTRO_REGISTRATION
|
||||
- INSTANCE_CUSTOM_THEME
|
||||
- WEB_PANEL
|
||||
- PUBLIC_REGISTRATION
|
||||
- PHONE_AUTHENTICATION
|
||||
- DEVICES_MANAGEMENT
|
||||
- INTERCOM_FEATURES
|
||||
- NEWSLETTER_REGISTRATION_ADDRESS
|
||||
- ACCOUNT_PROXY_REGISTRAR_ADDRESS
|
||||
- ACCOUNT_TRANSPORT_PROTOCOL_TEXT
|
||||
- ACCOUNT_REALM
|
||||
- ACCOUNT_PROVISIONING_RC_FILE
|
||||
- ACCOUNT_PROVISIONING_OVERWRITE_ALL
|
||||
- ACCOUNT_PROVISIONING_USE_X_LINPHONE_PROVISIONING_HEADER
|
||||
|
||||
### Migrate from [1.6]
|
||||
|
||||
1. Deploy the new version and migrate the database.
|
||||
|
||||
```
|
||||
php artisan migrate
|
||||
```
|
||||
|
||||
2. Set `APP_ROOT_HOST` in `.env` or as an environnement variable. And remove `APP_URL` and `APP_SIP_DOMAIN`
|
||||
|
||||
```
|
||||
APP_ROOT_HOST=myhost.com
|
||||
```
|
||||
|
||||
3. The migration script will automatically copy the `sip_domain` into `host` in the `spaces` table. You then have to "fix" the hosts and set them to equal or be subdomains of `APP_ROOT_HOST`.
|
||||
|
||||
```
|
||||
php artisan spaces:create-update my.sip myhost.com "My Super Space" --super # You can set some Spaces as SuperSpaces, the admin will be able to manage the other spaces
|
||||
php artisan spaces:create-update alpha.sip alpha.myhost.com "Alpha Space"
|
||||
php artisan spaces:create-update beta.sip beta.myhost.com "Beta Space"
|
||||
...
|
||||
```
|
||||
|
||||
4. Configure your web server to point the `APP_ROOT_HOST` and subdomains to the app. See the related documentation in [`INSTALL.md` file](INSTALL.md#31-mandatory-app_root_host-variable).
|
||||
|
||||
5. Configure your Spaces.
|
||||
|
||||
6. (Optional) Import the old instance DotEnv environnement variables into a space.
|
||||
|
||||
7. Remove the instance based environnement variables (see **Changed** above) and configure them directly in the spaces using the API or Web Panel.
|
||||
|
||||
⚠️ Be careful, during this import only the project DotEnv file variables will be imported, other environnement (eg. set in Apache, nginx or Docker) will be ignored.
|
||||
|
||||
⚠️ The content of the `ACCOUNT_PROVISIONING_RC_FILE` will not be imported. You will have to extract the sections and lines that you want to use manually using the dedicated form or the API.
|
||||
|
||||
```
|
||||
php artisan spaces:import-configuration-from-dot-env {sip_domain}
|
||||
```
|
||||
|
||||
You can find more details regarding those steps in the [`INSTALL.md`](INSTALL.md) and [`README.md`](README.md) files.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- **Last major version supporting the deprecated endpoints of the API**
|
||||
|
||||
## [1.6] - 2024-12-30
|
||||
|
||||
### Added
|
||||
|
||||
- **Phone validation** Phone numbers are now strictly validated and countries can be enabled disabled to prevent spam
|
||||
- **SIP Domains** Account SIP domains can now be managed from the UI and API
|
||||
- **CoTURN Credential** Get CoTURN credentials from the API
|
||||
- **RFC 8898 Support**
|
||||
|
||||
### Migrate from [1.5]
|
||||
|
||||
Nothing specific to do
|
||||
|
||||
## [1.5] - 2024-08-29
|
||||
|
||||
### Added
|
||||
|
||||
- **Account activity view:** new panel, available behind the Activity tab, will allow any admin to follow the activity of the accounts they manage.
|
||||
- **Detect and block abusive accounts:** This activity tracking is coming with a related tool that is measuring the accounts activity and automatically block them if it detects some unusual behaviors on the service. An account can also directly be blocked and unblocked from the setting panel. Two new setting variables will allow you to fine tune those behaviors triggers.
|
||||
- **New DotEnv variable:** `BLOCKING_TIME_PERIOD_CHECK=30` Time span on which the blocking service will proceed, in minutes
|
||||
- **New DotEnv variable:** `BLOCKING_AMOUNT_EVENTS_AUTHORIZED_DURING_PERIOD=5` Amount of account events authorized during this period
|
||||
- **OAuth JWT Authentication:** OAuth support with the handling of JWE tokens issues by a third party service such as Keycloack.
|
||||
- **New DotEnv variable:** `JWT_RSA_PUBLIC_KEY_PEM=`
|
||||
- **New DotEnv variable:** `JWT_SIP_IDENTIFIER=sip_identifier`
|
||||
- **Super-domains and super-admins support:** Introduce SIP domains management. The app accounts are now divided by their domains with their own respective administrators that can only see and manage their own domain accounts and settings. On top of that it is possible to configure a SIP domain as a "super-domain" and then allow its admins to become "super-admins". Those super-admins will then be able to manage all the accounts handled by the instance and create/edit/delete the other SIP domains. Add new endpoints and a new super-admin role in the API to manage the SIP domains. SIP domains can also be created and updated directly from the console using a new artisan script (documented in the README);
|
||||
- **New Artisan script:** `php artisan sip_domains:create-update {domain} {--super}`
|
||||
- **Account Dictionary:** Each account can now handle a specific dictionary, configurable by the API or directly the web panel. This dictionary allows developers to store arbitrary `key -> value pairs` on each accounts.
|
||||
- **Vcard storage:** Attach custom vCards on a dedicated account using new endpoints in the API. The published vCard are validated before being stored.
|
||||
|
||||
### Changed
|
||||
|
||||
- **User management of their own devices:** Allowing users will be able to manage its own devices. Specific API endpoints were also added to manage them directly from the clients.
|
||||
- **Migration to hCaptcha:** Migrate from Google Recaptcha to hCaptcha in this release.
|
||||
- **New DotEnv variable:** HCAPTCHA_SECRET=secret-key
|
||||
- **New DotEnv variable:** HCAPTCHA_SITEKEY=site-key
|
||||
- **Localization support:** The API is now accepting the `Accept-Language` header and adapt its internal localization to the client/browser one. For the moment only French and English are supported but more languages could be added in the future.
|
||||
1
cron/flexiapi.cron
Normal file
1
cron/flexiapi.cron
Normal file
|
|
@ -0,0 +1 @@
|
|||
* * * * * apache /opt/belledonne-communications/share/flexisip-account-manager/flexiapi/artisan schedule:run >> /dev/null 2>&1
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
cd /opt/belledonne-communications/share/flexisip-account-manager/flexiapi/
|
||||
sudo -su www-data && php artisan digest:clear-nonces 60
|
||||
sudo -su www-data && php artisan accounts:clear-api-keys 60
|
||||
sudo -su www-data && php artisan accounts:clear-accounts-tombstones 7 --apply
|
||||
sudo -su www-data && php artisan accounts:clear-api-keys 60
|
||||
sudo -su www-data && php artisan accounts:clear-files 30 --apply
|
||||
sudo -su www-data && php artisan accounts:clear-unconfirmed 30 --apply
|
||||
sudo -su www-data && php artisan app:clear-statistics 30 --apply
|
||||
sudo -su www-data && php artisan digest:clear-nonces 60
|
||||
sudo -su www-data && php artisan spaces:expiration-emails
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
cd /opt/belledonne-communications/share/flexisip-account-manager/flexiapi/
|
||||
php artisan digest:clear-nonces 60
|
||||
php artisan accounts:clear-api-keys 60
|
||||
php artisan accounts:clear-accounts-tombstones 7 --apply
|
||||
php artisan accounts:clear-api-keys 60
|
||||
php artisan accounts:clear-files 30 --apply
|
||||
php artisan accounts:clear-unconfirmed 30 --apply
|
||||
php artisan spaces:expiration-emails
|
||||
php artisan app:clear-statistics 30 --apply
|
||||
php artisan digest:clear-nonces 60
|
||||
php artisan spaces:expiration-emails
|
||||
|
|
|
|||
|
|
@ -13,11 +13,6 @@ TERMS_OF_USE_URL= # A URL pointing to the Terms of Use
|
|||
PRIVACY_POLICY_URL= # A URL pointing to the Privacy Policy
|
||||
APP_PROJECT_URL= # A URL pointing to the project information page
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
|
||||
# Risky toggles
|
||||
APP_DANGEROUS_ENDPOINTS=false # Enable some dangerous endpoints used for XMLRPC like fallback usage
|
||||
|
||||
# Expiration time for tokens and code, in minutes, 0 means no expiration
|
||||
APP_API_ACCOUNT_CREATION_TOKEN_RETRY_MINUTES=60 # Number of minutes between two consecutive account_creation_token creation
|
||||
APP_ACCOUNT_CREATION_TOKEN_EXPIRATION_MINUTES=0
|
||||
|
|
@ -63,10 +58,9 @@ REDIS_DB=
|
|||
|
||||
# Logs
|
||||
# Ensure that you have the proper SELinux configuration to write in the storage directory, see the README
|
||||
LOG_CHANNEL=stack
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
QUEUE_CONNECTION=sync
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
# SMTP and emails
|
||||
|
|
@ -103,3 +97,5 @@ HCAPTCHA_SITEKEY=site-key
|
|||
JWT_RSA_PUBLIC_KEY_PEM=
|
||||
JWT_SIP_IDENTIFIER=
|
||||
|
||||
# Temporary toggles
|
||||
APP_SHOW_LOGIN_COUNTER_TEMP= # default true
|
||||
|
|
@ -23,8 +23,8 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Carbon\Carbon;
|
||||
|
||||
use Awobaz\Compoships\Compoships;
|
||||
|
|
@ -36,7 +36,7 @@ class Account extends Authenticatable
|
|||
use HasFactory;
|
||||
use Compoships;
|
||||
|
||||
protected $with = ['passwords', 'emailChangeCode', 'types', 'actions', 'dictionaryEntries'];
|
||||
protected $with = ['passwords', 'emailChangeCode', 'types', 'actions', 'dictionaryEntries', 'carddavServers'];
|
||||
protected $hidden = ['expire_time', 'pivot', 'currentProvisioningToken', 'currentRecoveryCode', 'dictionaryEntries'];
|
||||
protected $appends = ['realm', 'provisioning_token', 'provisioning_token_expire_at', 'dictionary'];
|
||||
protected $casts = [
|
||||
|
|
@ -73,7 +73,10 @@ class Account extends Authenticatable
|
|||
return;
|
||||
}
|
||||
|
||||
$builder->where('domain', config('app.sip_domain'));
|
||||
/**
|
||||
* config('app.sip_domain') is required for the Tests suit
|
||||
*/
|
||||
$builder->where('domain', config('app.sip_domain') ?? space()->domain);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +134,23 @@ class Account extends Authenticatable
|
|||
return $this->belongsToMany(Account::class, 'contacts', 'account_id', 'contact_id');
|
||||
}
|
||||
|
||||
public function files()
|
||||
{
|
||||
return $this->hasMany(AccountFile::class)->latest();
|
||||
}
|
||||
|
||||
public function voicemails()
|
||||
{
|
||||
return $this->hasMany(AccountFile::class)
|
||||
->whereIn('content_type', AccountFile::VOICEMAIL_CONTENTTYPES)
|
||||
->latest();
|
||||
}
|
||||
|
||||
public function uploadedVoicemails()
|
||||
{
|
||||
return $this->voicemails()->whereNotNull('name');
|
||||
}
|
||||
|
||||
public function vcardsStorage()
|
||||
{
|
||||
return $this->hasMany(VcardStorage::class);
|
||||
|
|
@ -146,6 +166,12 @@ class Account extends Authenticatable
|
|||
return $this->hasMany(AccountDictionaryEntry::class);
|
||||
}
|
||||
|
||||
public function carddavServers()
|
||||
{
|
||||
return $this->belongsToMany(SpaceCardDavServer::class, 'account_carddav_credentials', 'account_id', 'space_carddav_server_id')
|
||||
->withPivot('username', 'realm', 'algorithm', 'password');
|
||||
}
|
||||
|
||||
public function getDictionaryAttribute()
|
||||
{
|
||||
if ($this->dictionaryEntries->isEmpty()) return new stdClass;
|
||||
|
|
@ -266,6 +292,18 @@ class Account extends Authenticatable
|
|||
return $this->hasMany(AuthToken::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset password
|
||||
*/
|
||||
|
||||
public function getCurrentResetPasswordUrlAttribute(): string
|
||||
{
|
||||
return replaceHost(
|
||||
route('account.reset_password_email.change', $this->currentResetPasswordEmailToken->token),
|
||||
$this->space->host
|
||||
);
|
||||
}
|
||||
|
||||
public function currentResetPasswordEmailToken()
|
||||
{
|
||||
return $this->hasOne(ResetPasswordEmailToken::class)->where('used', false)->latestOfMany();
|
||||
|
|
@ -306,6 +344,15 @@ class Account extends Authenticatable
|
|||
return null;
|
||||
}
|
||||
|
||||
public function getRemainingCardDavCredentialsCreatableAttribute(): Collection
|
||||
{
|
||||
return $this->space->carddavServers()->whereNotIn('id', function ($query) {
|
||||
$query->select('space_carddav_server_id')
|
||||
->from('account_carddav_credentials')
|
||||
->where('account_id', $this->id);
|
||||
})->get();
|
||||
}
|
||||
|
||||
public function getIdentifierAttribute(): string
|
||||
{
|
||||
return $this->attributes['username'] . '@' . $this->attributes['domain'];
|
||||
|
|
|
|||
20
flexiapi/app/AccountCardDavCredentials.php
Normal file
20
flexiapi/app/AccountCardDavCredentials.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class AccountCardDavCredentials extends Model
|
||||
{
|
||||
protected $table = 'account_carddav_credentials';
|
||||
|
||||
public function cardDavServer()
|
||||
{
|
||||
return $this->hasOne(SpaceCardDavServer::class, 'id', 'space_carddav_server_id');
|
||||
}
|
||||
|
||||
public function getIdentifierAttribute()
|
||||
{
|
||||
return $this->username . '@' . $this->domain;
|
||||
}
|
||||
}
|
||||
74
flexiapi/app/AccountFile.php
Normal file
74
flexiapi/app/AccountFile.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class AccountFile extends Model
|
||||
{
|
||||
use HasUuids;
|
||||
|
||||
public const VOICEMAIL_CONTENTTYPES = ['audio/opus', 'audio/wav'];
|
||||
public const FILES_PATH = 'files';
|
||||
protected $hidden = ['account_id', 'updated_at', 'sending_by_mail_at', 'sent_by_mail_at', 'sending_by_mail_tryouts'];
|
||||
protected $appends = ['download_url'];
|
||||
protected $casts = [
|
||||
'uploaded_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::deleting(function (AccountFile $accountFile) {
|
||||
Storage::delete($accountFile->getPathAttribute());
|
||||
});
|
||||
}
|
||||
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class)->withoutGlobalScopes();
|
||||
}
|
||||
|
||||
public function getMaxUploadSizeAttribute(): ?int
|
||||
{
|
||||
return maxUploadSize();
|
||||
}
|
||||
|
||||
public function getUploadUrlAttribute(): ?string
|
||||
{
|
||||
return route('file.upload', $this->attributes['id']);
|
||||
}
|
||||
|
||||
public function getPathAttribute(): string
|
||||
{
|
||||
return self::FILES_PATH . '/' . $this->attributes['name'];
|
||||
}
|
||||
|
||||
public function getUrlAttribute(): ?string
|
||||
{
|
||||
return !empty($this->attributes['name'])
|
||||
&& !empty($this->attributes['id'])
|
||||
? replaceHost(
|
||||
route('file.show', ['uuid' => $this->attributes['id'], 'name' => $this->attributes['name']]),
|
||||
$this->account->space->host
|
||||
)
|
||||
: null;
|
||||
}
|
||||
|
||||
public function getDownloadUrlAttribute(): ?string
|
||||
{
|
||||
return !empty($this->attributes['name'])
|
||||
&& !empty($this->attributes['id'])
|
||||
? replaceHost(route(
|
||||
'file.download',
|
||||
['uuid' => $this->attributes['id'], 'name' => $this->attributes['name']]
|
||||
), $this->account->space->host)
|
||||
: null;
|
||||
}
|
||||
|
||||
public function isVoicemailAudio(): bool
|
||||
{
|
||||
return in_array($this->attributes['content_type'], self::VOICEMAIL_CONTENTTYPES);
|
||||
}
|
||||
}
|
||||
|
|
@ -29,11 +29,6 @@ class ClearAccountsTombstones extends Command
|
|||
protected $signature = 'accounts:clear-accounts-tombstones {days} {--apply}';
|
||||
protected $description = 'Clear deleted accounts tombstones after n days';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$tombstones = AccountTombstone::where(
|
||||
|
|
|
|||
35
flexiapi/app/Console/Commands/Accounts/ClearFiles.php
Normal file
35
flexiapi/app/Console/Commands/Accounts/ClearFiles.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands\Accounts;
|
||||
|
||||
use App\AccountFile;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class ClearFiles extends Command
|
||||
{
|
||||
protected $signature = 'accounts:clear-files {days} {--apply}';
|
||||
protected $description = 'Remove the uploaded files after n days';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$files = AccountFile::where(
|
||||
'created_at',
|
||||
'<',
|
||||
Carbon::now()->subDays($this->argument('days'))->toDateTimeString()
|
||||
);
|
||||
|
||||
$count = $files->count();
|
||||
|
||||
if ($this->option('apply')) {
|
||||
$this->info($count . ' files in deletion…');
|
||||
$files->delete();
|
||||
$this->info($count . ' files deleted');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$this->info($count . ' files to delete');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -29,11 +29,6 @@ class ClearUnconfirmed extends Command
|
|||
protected $signature = 'accounts:clear-unconfirmed {days} {--apply} {--and-confirmed}';
|
||||
protected $description = 'Clear unconfirmed accounts after n days';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$accounts = Account::where(
|
||||
|
|
|
|||
|
|
@ -30,18 +30,15 @@ class CreateAdminTest extends Command
|
|||
protected $signature = 'accounts:create-admin-test';
|
||||
protected $description = 'Create a test admin account, only for tests purpose';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$username = 'admin_test';
|
||||
$domain = 'sip.example.org';
|
||||
|
||||
$this->call('spaces:create-update', [
|
||||
'domain' => $domain,
|
||||
'sip_domain' => $domain,
|
||||
'host' => $domain,
|
||||
'name' => $domain,
|
||||
'--super' => 'true'
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -28,11 +28,6 @@ class Seed extends Command
|
|||
protected $signature = 'accounts:seed {json-file-path}';
|
||||
protected $description = 'Seed some accounts from a JSON file';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$file = $this->argument('json-file-path');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands\Accounts;
|
||||
|
||||
use App\AccountFile;
|
||||
use App\Mail\Voicemail;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendVoicemailsEmails extends Command
|
||||
{
|
||||
protected $signature = 'accounts:send-voicemails-emails {--tryout}';
|
||||
protected $description = 'Send the voicemail emails';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$voicemails = AccountFile::whereNotNull('uploaded_at')
|
||||
->whereNull('sent_by_mail_at')
|
||||
->where('sending_by_mail_tryouts', '<', is_int($this->option('tryout'))
|
||||
? $this->option('tryout')
|
||||
: 3)
|
||||
->get();
|
||||
|
||||
foreach ($voicemails as $voicemail) {
|
||||
$voicemail->sending_by_mail_at = Carbon::now();
|
||||
$voicemail->save();
|
||||
|
||||
if (Mail::to(users: $voicemail->account)->send(new Voicemail($voicemail))) {
|
||||
$voicemail->sent_by_mail_at = Carbon::now();
|
||||
$this->info('Voicemail sent to ' . $voicemail->account->identifier);
|
||||
} else {
|
||||
$voicemail->sending_by_mail_tryouts++;
|
||||
$this->info('Error sending voicemail to ' . $voicemail->account->identifier);
|
||||
}
|
||||
|
||||
$voicemail->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,11 +28,6 @@ class SetAdmin extends Command
|
|||
protected $signature = 'accounts:set-admin {id}';
|
||||
protected $description = 'Give the admin role to an account';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$account = Account::withoutGlobalScopes()->where('id', $this->argument('id'))->first();
|
||||
|
|
|
|||
47
flexiapi/app/Console/Commands/ClearStatistics.php
Normal file
47
flexiapi/app/Console/Commands/ClearStatistics.php
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\StatisticsCall;
|
||||
use App\StatisticsMessage;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class ClearStatistics extends Command
|
||||
{
|
||||
protected $signature = 'app:clear-statistics {days} {--apply}';
|
||||
protected $description = 'Command description';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$calls = StatisticsCall::where(
|
||||
'created_at',
|
||||
'<',
|
||||
Carbon::now()->subDays($this->argument('days'))->toDateTimeString()
|
||||
);
|
||||
$messages = StatisticsMessage::where(
|
||||
'created_at',
|
||||
'<',
|
||||
Carbon::now()->subDays($this->argument('days'))->toDateTimeString()
|
||||
);
|
||||
|
||||
$callsCount = $calls->count();
|
||||
$messagesCount = $messages->count();
|
||||
|
||||
if ($this->option('apply')) {
|
||||
$this->info($callsCount . ' calls statistics in deletion…');
|
||||
$calls->delete();
|
||||
$this->info($callsCount . ' calls statistics deleted');
|
||||
|
||||
$this->info($messagesCount . ' messages statistics in deletion…');
|
||||
$messages->delete();
|
||||
$this->info($messagesCount . ' messages statistics deleted');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$this->info($callsCount . ' calls statistics to delete');
|
||||
$this->info($messagesCount . ' messages statistics to delete');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
33
flexiapi/app/Console/Commands/UpdatePhoneCountries.php
Normal file
33
flexiapi/app/Console/Commands/UpdatePhoneCountries.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\PhoneCountry;
|
||||
use Illuminate\Console\Command;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
|
||||
class UpdatePhoneCountries extends Command
|
||||
{
|
||||
protected $signature = 'app:update-phone-countries';
|
||||
protected $description = 'Update the phone_countries table from the getCountryCodes() function';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$phoneNumberUtils = PhoneNumberUtil::getInstance();
|
||||
$countryCodes = getCountryCodes();
|
||||
|
||||
foreach (array_diff(
|
||||
array_keys($countryCodes),
|
||||
PhoneCountry::pluck('code')->toArray()
|
||||
) as $code) {
|
||||
if ($resolvedMetadata = $phoneNumberUtils->getMetadataForRegion($code)) {
|
||||
$phoneCountry = new PhoneCountry();
|
||||
$phoneCountry->code = $code;
|
||||
$phoneCountry->country_code = $resolvedMetadata->getCountryCode();
|
||||
$phoneCountry->save();
|
||||
|
||||
$this->info($code . ' - ' . $countryCodes[$code] . ' inserted');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -40,7 +40,7 @@ abstract class Consommable extends Model
|
|||
public function getExpireAtAttribute(): ?string
|
||||
{
|
||||
if ($this->isExpirable()) {
|
||||
return $this->created_at->addMinutes(config('app.' . $this->configExpirationMinutesKey))->toJSON();
|
||||
return $this->created_at->addMinutes((int)config('app.' . $this->configExpirationMinutesKey))->toJSON();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
@ -49,13 +49,13 @@ abstract class Consommable extends Model
|
|||
public function expired(): bool
|
||||
{
|
||||
return ($this->isExpirable()
|
||||
&& Carbon::now()->subMinutes(config('app.' . $this->configExpirationMinutesKey))->isAfter($this->created_at));
|
||||
&& Carbon::now()->subMinutes((int)config('app.' . $this->configExpirationMinutesKey))->isAfter($this->created_at));
|
||||
}
|
||||
|
||||
private function isExpirable(): bool
|
||||
{
|
||||
return $this->configExpirationMinutesKey != null
|
||||
&& config('app.' . $this->configExpirationMinutesKey) != null
|
||||
&& config('app.' . $this->configExpirationMinutesKey) > 0;
|
||||
&& (int)config('app.' . $this->configExpirationMinutesKey) > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,4 +15,9 @@ class ContactsList extends Model
|
|||
{
|
||||
return $this->belongsToMany(Account::class, 'contacts_list_contact', 'contacts_list_id', 'contact_id');
|
||||
}
|
||||
|
||||
public function space()
|
||||
{
|
||||
return $this->belongsTo(Space::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
use App\Account;
|
||||
|
|
@ -28,24 +29,15 @@ use League\CommonMark\Extension\HeadingPermalink\HeadingPermalinkExtension;
|
|||
use League\CommonMark\Extension\TableOfContents\TableOfContentsExtension;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
$hostSpace = null;
|
||||
|
||||
function space($reload = false): ?Space
|
||||
function space(): ?Space
|
||||
{
|
||||
global $hostSpace;
|
||||
|
||||
if ($hostSpace != null && $reload == false) {
|
||||
return $hostSpace;
|
||||
}
|
||||
|
||||
$hostSpace = Space::where('host', request()->host())->first();
|
||||
return $hostSpace;
|
||||
return is_object(request()->space) ? request()->space : null;
|
||||
}
|
||||
|
||||
function passwordAlgorithms(): array
|
||||
{
|
||||
return [
|
||||
'MD5' => 'md5',
|
||||
'MD5' => 'md5',
|
||||
'SHA-256' => 'sha256',
|
||||
];
|
||||
}
|
||||
|
|
@ -109,7 +101,7 @@ function markdownDocumentationView(string $view): string
|
|||
$converter->getEnvironment()->addExtension(new TableOfContentsExtension());
|
||||
|
||||
return (string) $converter->convert(
|
||||
(string)view($view, [
|
||||
(string) view($view, [
|
||||
'app_name' => space()->name
|
||||
])->render()
|
||||
);
|
||||
|
|
@ -167,7 +159,15 @@ function resolveDomain(Request $request): string
|
|||
&& $request->user()
|
||||
&& $request->user()->superAdmin
|
||||
? $request->get('domain')
|
||||
: config('app.sip_domain');
|
||||
: $request->space->domain;
|
||||
}
|
||||
|
||||
function maxUploadSize(): int
|
||||
{
|
||||
return min(
|
||||
ini_parse_quantity(ini_get('upload_max_filesize')),
|
||||
ini_parse_quantity(ini_get('post_max_size'))
|
||||
);
|
||||
}
|
||||
|
||||
function captchaConfigured(): bool
|
||||
|
|
@ -214,7 +214,7 @@ function validateIsoDate($attribute, $value, $parameters, $validator): bool
|
|||
// Regex from https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/
|
||||
: '/^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/';
|
||||
|
||||
return (bool)preg_match($regex, $value);
|
||||
return (bool) preg_match($regex, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -226,236 +226,253 @@ function validateIsoDate($attribute, $value, $parameters, $validator): bool
|
|||
function getCountryCodes()
|
||||
{
|
||||
return [
|
||||
'AF' => 'Afghanistan',
|
||||
'AX' => 'Åland Islands',
|
||||
'AL' => 'Albania',
|
||||
'DZ' => 'Algeria',
|
||||
'AS' => 'American Samoa',
|
||||
'AD' => 'Andorra',
|
||||
'AO' => 'Angola',
|
||||
'AI' => 'Anguilla',
|
||||
'AE' => 'United Arab Emirates',
|
||||
'AF' => 'Afghanistan',
|
||||
'AG' => 'Antigua & Barbuda',
|
||||
'AI' => 'Anguilla',
|
||||
'AL' => 'Albania',
|
||||
'AM' => 'Armenia',
|
||||
'AO' => 'Angola',
|
||||
'AQ' => 'Antarctica',
|
||||
'AR' => 'Argentina',
|
||||
'AU' => 'Australia',
|
||||
'AS' => 'American Samoa',
|
||||
'AT' => 'Austria',
|
||||
'AU' => 'Australia',
|
||||
'AW' => 'Aruba',
|
||||
'AX' => 'Åland Islands',
|
||||
'AZ' => 'Azerbaijan',
|
||||
'BS' => 'Bahamas',
|
||||
'BH' => 'Bahrain',
|
||||
'BD' => 'Bangladesh',
|
||||
'BB' => 'Barbados',
|
||||
'BY' => 'Belarus',
|
||||
'BE' => 'Belgium',
|
||||
'BZ' => 'Belize',
|
||||
'BJ' => 'Benin',
|
||||
'BM' => 'Bermuda',
|
||||
'BT' => 'Bhutan',
|
||||
'BO' => 'Bolivia',
|
||||
'BA' => 'Bosnia & Herzegovina',
|
||||
'BW' => 'Botswana',
|
||||
'BR' => 'Brazil',
|
||||
'IO' => 'British Indian Ocean Territory',
|
||||
'BN' => 'Brunei',
|
||||
'BG' => 'Bulgaria',
|
||||
'BB' => 'Barbados',
|
||||
'BD' => 'Bangladesh',
|
||||
'BE' => 'Belgium',
|
||||
'BF' => 'Burkina Faso',
|
||||
'BG' => 'Bulgaria',
|
||||
'BH' => 'Bahrain',
|
||||
'BI' => 'Burundi',
|
||||
'KH' => 'Cambodia',
|
||||
'CM' => 'Cameroon',
|
||||
'BJ' => 'Benin',
|
||||
'BL' => 'St. Barthélemy',
|
||||
'BM' => 'Bermuda',
|
||||
'BN' => 'Brunei',
|
||||
'BO' => 'Bolivia',
|
||||
'BQ' => 'Bonaire, Sint Eustatius & Saba',
|
||||
'BR' => 'Brazil',
|
||||
'BS' => 'Bahamas',
|
||||
'BT' => 'Bhutan',
|
||||
'BV' => 'Bouvet Island',
|
||||
'BW' => 'Botswana',
|
||||
'BY' => 'Belarus',
|
||||
'BZ' => 'Belize',
|
||||
'CA' => 'Canada',
|
||||
'CV' => 'Cape Verde',
|
||||
'KY' => 'Cayman Islands',
|
||||
'CF' => 'Central African Republic',
|
||||
'TD' => 'Chad',
|
||||
'CL' => 'Chile',
|
||||
'CN' => 'China',
|
||||
'CX' => 'Christmas Island',
|
||||
'CC' => 'Cocos (Keeling) Islands',
|
||||
'CO' => 'Colombia',
|
||||
'KM' => 'Comoros',
|
||||
'CG' => 'Congo - Brazzaville',
|
||||
'CD' => 'Congo - Kinshasa',
|
||||
'CF' => 'Central African Republic',
|
||||
'CG' => 'Congo - Brazzaville',
|
||||
'CH' => 'Switzerland',
|
||||
'CI' => "Côte d'Ivoire",
|
||||
'CK' => 'Cook Islands',
|
||||
'CL' => 'Chile',
|
||||
'CM' => 'Cameroon',
|
||||
'CN' => 'China',
|
||||
'CO' => 'Colombia',
|
||||
'CR' => 'Costa Rica',
|
||||
'CI' => 'Côte d’Ivoire',
|
||||
'HR' => 'Croatia',
|
||||
'CU' => 'Cuba',
|
||||
'CV' => 'Cabo Verde',
|
||||
'CW' => 'Curaçao',
|
||||
'CX' => 'Christmas Island',
|
||||
'CY' => 'Cyprus',
|
||||
'CZ' => 'Czechia',
|
||||
'DK' => 'Denmark',
|
||||
'DE' => 'Germany',
|
||||
'DJ' => 'Djibouti',
|
||||
'DK' => 'Denmark',
|
||||
'DM' => 'Dominica',
|
||||
'DO' => 'Dominican Republic',
|
||||
'DZ' => 'Algeria',
|
||||
'EC' => 'Ecuador',
|
||||
'EG' => 'Egypt',
|
||||
'SV' => 'El Salvador',
|
||||
'GQ' => 'Equatorial Guinea',
|
||||
'ER' => 'Eritrea',
|
||||
'EE' => 'Estonia',
|
||||
'EG' => 'Egypt',
|
||||
'EH' => 'Western Sahara',
|
||||
'ER' => 'Eritrea',
|
||||
'ES' => 'Spain',
|
||||
'ET' => 'Ethiopia',
|
||||
'FK' => 'Falkland Islands',
|
||||
'FO' => 'Faroe Islands',
|
||||
'FJ' => 'Fiji',
|
||||
'FI' => 'Finland',
|
||||
'FJ' => 'Fiji',
|
||||
'FK' => 'Falkland Islands',
|
||||
'FM' => 'Micronesia',
|
||||
'FO' => 'Faroe Islands',
|
||||
'FR' => 'France',
|
||||
'GF' => 'French Guiana',
|
||||
'PF' => 'French Polynesia',
|
||||
'GA' => 'Gabon',
|
||||
'GM' => 'Gambia',
|
||||
'GB' => 'United Kingdom',
|
||||
'GD' => 'Grenada',
|
||||
'GE' => 'Georgia',
|
||||
'DE' => 'Germany',
|
||||
'GF' => 'French Guiana',
|
||||
'GG' => 'Guernsey',
|
||||
'GH' => 'Ghana',
|
||||
'GI' => 'Gibraltar',
|
||||
'GR' => 'Greece',
|
||||
'GL' => 'Greenland',
|
||||
'GD' => 'Grenada',
|
||||
'GP' => 'Guadeloupe',
|
||||
'GU' => 'Guam',
|
||||
'GT' => 'Guatemala',
|
||||
'GG' => 'Guernsey',
|
||||
'GM' => 'Gambia',
|
||||
'GN' => 'Guinea',
|
||||
'GP' => 'Guadeloupe',
|
||||
'GQ' => 'Equatorial Guinea',
|
||||
'GR' => 'Greece',
|
||||
'GS' => 'South Georgia & South Sandwich Islands',
|
||||
'GT' => 'Guatemala',
|
||||
'GU' => 'Guam',
|
||||
'GW' => 'Guinea-Bissau',
|
||||
'GY' => 'Guyana',
|
||||
'HT' => 'Haiti',
|
||||
'HK' => 'Hong Kong',
|
||||
'HM' => 'Heard & McDonald Islands',
|
||||
'HN' => 'Honduras',
|
||||
'HK' => 'Hong Kong SAR China',
|
||||
'HR' => 'Croatia',
|
||||
'HT' => 'Haiti',
|
||||
'HU' => 'Hungary',
|
||||
'IS' => 'Iceland',
|
||||
'IN' => 'India',
|
||||
'ID' => 'Indonesia',
|
||||
'IR' => 'Iran',
|
||||
'IQ' => 'Iraq',
|
||||
'IE' => 'Ireland',
|
||||
'IM' => 'Isle of Man',
|
||||
'IL' => 'Israel',
|
||||
'IM' => 'Isle of Man',
|
||||
'IN' => 'India',
|
||||
'IO' => 'British Indian Ocean Territory',
|
||||
'IQ' => 'Iraq',
|
||||
'IR' => 'Iran',
|
||||
'IS' => 'Iceland',
|
||||
'IT' => 'Italy',
|
||||
'JM' => 'Jamaica',
|
||||
'JP' => 'Japan',
|
||||
'JE' => 'Jersey',
|
||||
'JM' => 'Jamaica',
|
||||
'JO' => 'Jordan',
|
||||
'KZ' => 'Kazakhstan',
|
||||
'JP' => 'Japan',
|
||||
'KE' => 'Kenya',
|
||||
'KG' => 'Kyrgyzstan',
|
||||
'KH' => 'Cambodia',
|
||||
'KI' => 'Kiribati',
|
||||
'KM' => 'Comoros',
|
||||
'KN' => 'St. Kitts & Nevis',
|
||||
'KP' => 'North Korea',
|
||||
'KR' => 'South Korea',
|
||||
'KW' => 'Kuwait',
|
||||
'KG' => 'Kyrgyzstan',
|
||||
'KY' => 'Cayman Islands',
|
||||
'KZ' => 'Kazakhstan',
|
||||
'LA' => 'Laos',
|
||||
'LV' => 'Latvia',
|
||||
'LB' => 'Lebanon',
|
||||
'LS' => 'Lesotho',
|
||||
'LR' => 'Liberia',
|
||||
'LY' => 'Libya',
|
||||
'LC' => 'St. Lucia',
|
||||
'LI' => 'Liechtenstein',
|
||||
'LK' => 'Sri Lanka',
|
||||
'LR' => 'Liberia',
|
||||
'LS' => 'Lesotho',
|
||||
'LT' => 'Lithuania',
|
||||
'LU' => 'Luxembourg',
|
||||
'MO' => 'Macao SAR China',
|
||||
'MK' => 'North Macedonia',
|
||||
'LV' => 'Latvia',
|
||||
'LY' => 'Libya',
|
||||
'MA' => 'Morocco',
|
||||
'MC' => 'Monaco',
|
||||
'MD' => 'Moldova',
|
||||
'ME' => 'Montenegro',
|
||||
'MF' => 'St. Martin',
|
||||
'MG' => 'Madagascar',
|
||||
'MW' => 'Malawi',
|
||||
'MY' => 'Malaysia',
|
||||
'MV' => 'Maldives',
|
||||
'ML' => 'Mali',
|
||||
'MT' => 'Malta',
|
||||
'MH' => 'Marshall Islands',
|
||||
'MK' => 'North Macedonia',
|
||||
'ML' => 'Mali',
|
||||
'MM' => 'Myanmar',
|
||||
'MN' => 'Mongolia',
|
||||
'MO' => 'Macao',
|
||||
'MP' => 'Northern Mariana Islands',
|
||||
'MQ' => 'Martinique',
|
||||
'MR' => 'Mauritania',
|
||||
'MU' => 'Mauritius',
|
||||
'YT' => 'Mayotte',
|
||||
'MX' => 'Mexico',
|
||||
'FM' => 'Micronesia',
|
||||
'MD' => 'Moldova',
|
||||
'MC' => 'Monaco',
|
||||
'MN' => 'Mongolia',
|
||||
'ME' => 'Montenegro',
|
||||
'MS' => 'Montserrat',
|
||||
'MA' => 'Morocco',
|
||||
'MT' => 'Malta',
|
||||
'MU' => 'Mauritius',
|
||||
'MV' => 'Maldives',
|
||||
'MW' => 'Malawi',
|
||||
'MX' => 'Mexico',
|
||||
'MY' => 'Malaysia',
|
||||
'MZ' => 'Mozambique',
|
||||
'MM' => 'Myanmar (Burma)',
|
||||
'NA' => 'Namibia',
|
||||
'NR' => 'Nauru',
|
||||
'NP' => 'Nepal',
|
||||
'NL' => 'Netherlands',
|
||||
'NC' => 'New Caledonia',
|
||||
'NZ' => 'New Zealand',
|
||||
'NI' => 'Nicaragua',
|
||||
'NE' => 'Niger',
|
||||
'NG' => 'Nigeria',
|
||||
'NU' => 'Niue',
|
||||
'NF' => 'Norfolk Island',
|
||||
'MP' => 'Northern Mariana Islands',
|
||||
'NG' => 'Nigeria',
|
||||
'NI' => 'Nicaragua',
|
||||
'NL' => 'Netherlands',
|
||||
'NO' => 'Norway',
|
||||
'NP' => 'Nepal',
|
||||
'NR' => 'Nauru',
|
||||
'NU' => 'Niue',
|
||||
'NZ' => 'New Zealand',
|
||||
'OM' => 'Oman',
|
||||
'PK' => 'Pakistan',
|
||||
'PW' => 'Palau',
|
||||
'PS' => 'Palestinian Territories',
|
||||
'PA' => 'Panama',
|
||||
'PG' => 'Papua New Guinea',
|
||||
'PY' => 'Paraguay',
|
||||
'PE' => 'Peru',
|
||||
'PF' => 'French Polynesia',
|
||||
'PG' => 'Papua New Guinea',
|
||||
'PH' => 'Philippines',
|
||||
'PK' => 'Pakistan',
|
||||
'PL' => 'Poland',
|
||||
'PT' => 'Portugal',
|
||||
'PM' => 'St. Pierre & Miquelon',
|
||||
'PN' => 'Pitcairn Islands',
|
||||
'PR' => 'Puerto Rico',
|
||||
'PS' => 'Palestine',
|
||||
'PT' => 'Portugal',
|
||||
'PW' => 'Palau',
|
||||
'PY' => 'Paraguay',
|
||||
'QA' => 'Qatar',
|
||||
'RE' => 'Réunion',
|
||||
'RO' => 'Romania',
|
||||
'RS' => 'Serbia',
|
||||
'RU' => 'Russia',
|
||||
'RW' => 'Rwanda',
|
||||
'SH' => 'St. Helena',
|
||||
'KN' => 'St. Kitts & Nevis',
|
||||
'LC' => 'St. Lucia',
|
||||
'PM' => 'St. Pierre & Miquelon',
|
||||
'VC' => 'St. Vincent & Grenadines',
|
||||
'WS' => 'Samoa',
|
||||
'SM' => 'San Marino',
|
||||
'ST' => 'São Tomé & Príncipe',
|
||||
'SA' => 'Saudi Arabia',
|
||||
'SN' => 'Senegal',
|
||||
'RS' => 'Serbia',
|
||||
'SC' => 'Seychelles',
|
||||
'SL' => 'Sierra Leone',
|
||||
'SG' => 'Singapore',
|
||||
'SK' => 'Slovakia',
|
||||
'SI' => 'Slovenia',
|
||||
'SB' => 'Solomon Islands',
|
||||
'SO' => 'Somalia',
|
||||
'ZA' => 'South Africa',
|
||||
'ES' => 'Spain',
|
||||
'LK' => 'Sri Lanka',
|
||||
'SC' => 'Seychelles',
|
||||
'SD' => 'Sudan',
|
||||
'SR' => 'Suriname',
|
||||
'SJ' => 'Svalbard & Jan Mayen',
|
||||
'SZ' => 'Eswatini',
|
||||
'SE' => 'Sweden',
|
||||
'CH' => 'Switzerland',
|
||||
'SG' => 'Singapore',
|
||||
'SH' => 'St. Helena',
|
||||
'SI' => 'Slovenia',
|
||||
'SJ' => 'Svalbard & Jan Mayen',
|
||||
'SK' => 'Slovakia',
|
||||
'SL' => 'Sierra Leone',
|
||||
'SM' => 'San Marino',
|
||||
'SN' => 'Senegal',
|
||||
'SO' => 'Somalia',
|
||||
'SR' => 'Suriname',
|
||||
'SS' => 'South Sudan',
|
||||
'ST' => 'São Tomé & Príncipe',
|
||||
'SV' => 'El Salvador',
|
||||
'SX' => 'Sint Maarten',
|
||||
'SY' => 'Syria',
|
||||
'TW' => 'Taiwan',
|
||||
'TJ' => 'Tajikistan',
|
||||
'TZ' => 'Tanzania',
|
||||
'TH' => 'Thailand',
|
||||
'TL' => 'Timor-Leste',
|
||||
'TG' => 'Togo',
|
||||
'TK' => 'Tokelau',
|
||||
'TO' => 'Tonga',
|
||||
'TT' => 'Trinidad & Tobago',
|
||||
'TN' => 'Tunisia',
|
||||
'TM' => 'Turkmenistan',
|
||||
'SZ' => 'Eswatini',
|
||||
'TC' => 'Turks & Caicos Islands',
|
||||
'TD' => 'Chad',
|
||||
'TF' => 'French Southern Territories',
|
||||
'TG' => 'Togo',
|
||||
'TH' => 'Thailand',
|
||||
'TJ' => 'Tajikistan',
|
||||
'TK' => 'Tokelau',
|
||||
'TL' => 'Timor-Leste',
|
||||
'TM' => 'Turkmenistan',
|
||||
'TN' => 'Tunisia',
|
||||
'TO' => 'Tonga',
|
||||
'TR' => 'Türkiye',
|
||||
'TT' => 'Trinidad & Tobago',
|
||||
'TV' => 'Tuvalu',
|
||||
'UG' => 'Uganda',
|
||||
'TW' => 'Taiwan',
|
||||
'TZ' => 'Tanzania',
|
||||
'UA' => 'Ukraine',
|
||||
'AE' => 'United Arab Emirates',
|
||||
'GB' => 'United Kingdom',
|
||||
'UG' => 'Uganda',
|
||||
'UM' => 'U.S. Minor Outlying Islands',
|
||||
'US' => 'United States',
|
||||
'UY' => 'Uruguay',
|
||||
'UZ' => 'Uzbekistan',
|
||||
'VU' => 'Vanuatu',
|
||||
'VA' => 'Holy See (Vatican City)',
|
||||
'VC' => 'St. Vincent & Grenadines',
|
||||
'VE' => 'Venezuela',
|
||||
'VN' => 'Vietnam',
|
||||
'VG' => 'British Virgin Islands',
|
||||
'VI' => 'U.S. Virgin Islands',
|
||||
'VN' => 'Vietnam',
|
||||
'VU' => 'Vanuatu',
|
||||
'WF' => 'Wallis & Futuna',
|
||||
'EH' => 'Western Sahara',
|
||||
'WS' => 'Samoa',
|
||||
'YE' => 'Yemen',
|
||||
'YT' => 'Mayotte',
|
||||
'ZA' => 'South Africa',
|
||||
'ZM' => 'Zambia',
|
||||
'ZW' => 'Zimbabwe',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ class AuthenticateController extends Controller
|
|||
return redirect()->route('account.dashboard');
|
||||
}
|
||||
|
||||
return view('account.login', [
|
||||
return view('account.login', config('app.show_login_counter_temp') ? [
|
||||
'count' => Account::where('activated', true)->count()
|
||||
]);
|
||||
]: []);
|
||||
}
|
||||
|
||||
public function authenticate(Request $request)
|
||||
|
|
|
|||
32
flexiapi/app/Http/Controllers/Account/FileController.php
Normal file
32
flexiapi/app/Http/Controllers/Account/FileController.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Account;
|
||||
|
||||
use App\AccountFile;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class FileController extends Controller
|
||||
{
|
||||
public function show(string $uuid, string $name)
|
||||
{
|
||||
$file = AccountFile::findOrFail($uuid);
|
||||
|
||||
if ($file->name != $name) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
return Storage::get($file->path);
|
||||
}
|
||||
|
||||
public function download(string $uuid, string $name)
|
||||
{
|
||||
$file = AccountFile::findOrFail($uuid);
|
||||
|
||||
if ($file->name != $name) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
return Storage::download($file->path);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ namespace App\Http\Controllers\Account;
|
|||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class PasswordController extends Controller
|
||||
|
|
@ -46,12 +47,10 @@ class PasswordController extends Controller
|
|||
|
||||
if ($account->passwords()->count() > 0) {
|
||||
Log::channel('events')->info('Web: Password changed', ['id' => $account->identifier]);
|
||||
|
||||
return redirect()->route('account.dashboard');
|
||||
return redirect()->route('account.logout');
|
||||
}
|
||||
|
||||
Log::channel('events')->info('Web: Password set for the first time', ['id' => $account->identifier]);
|
||||
|
||||
return redirect()->route('account.dashboard');
|
||||
return redirect()->route('account.logout');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ class ProvisioningController extends Controller
|
|||
private function checkProvisioningHeader(Request $request)
|
||||
{
|
||||
if (!$request->hasHeader('x-linphone-provisioning')
|
||||
&& space()?->provisioning_use_linphone_provisioning_header) {
|
||||
&& $request->space->provisioning_use_linphone_provisioning_header) {
|
||||
abort(400, 'x-linphone-provisioning header is missing');
|
||||
}
|
||||
}
|
||||
|
|
@ -172,8 +172,8 @@ class ProvisioningController extends Controller
|
|||
|
||||
$dom->appendChild($config);
|
||||
|
||||
if (space()?->custom_provisioning_entries) {
|
||||
$rc = parse_ini_string(space()->custom_provisioning_entries, true);
|
||||
if ($request->space?->custom_provisioning_entries) {
|
||||
$rc = parse_ini_string($request->space->custom_provisioning_entries, true);
|
||||
|
||||
foreach ($rc as $sectionName => $values) {
|
||||
$section = $dom->createElement('section');
|
||||
|
|
@ -189,6 +189,44 @@ class ProvisioningController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
$remoteContactDirectoryCounter = 0;
|
||||
$authInfoIndex = 0;
|
||||
|
||||
// CardDav servers
|
||||
|
||||
if ($request->space?->carddavServers) {
|
||||
foreach ($request->space->carddavServers as $carddavServer) {
|
||||
$carddavServer->getProvisioningSection($config, $remoteContactDirectoryCounter);
|
||||
$remoteContactDirectoryCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($account) {
|
||||
foreach ($account->carddavServers as $carddavServer) {
|
||||
$section = $dom->createElement('section');
|
||||
$section->setAttribute('name', 'auth_info_' . $authInfoIndex);
|
||||
$config->appendChild($section);
|
||||
|
||||
$entry = $dom->createElement('entry', $carddavServer->pivot->username);
|
||||
$entry->setAttribute('name', 'username');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$entry = $dom->createElement('entry', $carddavServer->pivot->realm);
|
||||
$entry->setAttribute('name', 'realm');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$entry = $dom->createElement('entry', $carddavServer->pivot->password);
|
||||
$entry->setAttribute('name', 'ha1');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$entry = $dom->createElement('entry', $carddavServer->pivot->algorithm);
|
||||
$entry->setAttribute('name', 'algorithm');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$authInfoIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// Password reset
|
||||
if ($account && $request->has('reset_password')) {
|
||||
$account->updatePassword(Str::random(10));
|
||||
|
|
@ -251,7 +289,6 @@ class ProvisioningController extends Controller
|
|||
}
|
||||
|
||||
$passwords = $account->passwords()->get();
|
||||
$authInfoIndex = 0;
|
||||
|
||||
foreach ($passwords as $password) {
|
||||
$section = $xpath->query("//section[@name='auth_info_" . $authInfoIndex . "']")->item(0);
|
||||
|
|
@ -297,7 +334,7 @@ class ProvisioningController extends Controller
|
|||
}
|
||||
|
||||
// Overwrite all the entries
|
||||
if (space()?->custom_provisioning_overwrite_all) {
|
||||
if ($request->space?->custom_provisioning_overwrite_all) {
|
||||
$xpath = new \DOMXpath($dom);
|
||||
$entries = $xpath->query("//section/entry");
|
||||
if (!is_null($entries)) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ namespace App\Http\Controllers\Account;
|
|||
use App\ResetPasswordEmailToken;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ResetPasswordEmailController extends Controller
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
namespace App\Http\Controllers\Admin\Account;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
|
@ -26,7 +26,7 @@ use Illuminate\Support\Facades\Log;
|
|||
use App\Account;
|
||||
use App\AccountType;
|
||||
|
||||
class AccountAccountTypeController extends Controller
|
||||
class AccountTypeController extends Controller
|
||||
{
|
||||
public function create(int $id)
|
||||
{
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
namespace App\Http\Controllers\Admin\Account;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
|
@ -27,7 +27,7 @@ use App\Account;
|
|||
use App\AccountAction;
|
||||
use App\Rules\NoUppercase;
|
||||
|
||||
class AccountActionController extends Controller
|
||||
class ActionController extends Controller
|
||||
{
|
||||
public function create(int $accountId)
|
||||
{
|
||||
|
|
@ -99,6 +99,7 @@ class AccountActionController extends Controller
|
|||
$account = Account::findOrFail($accountId);
|
||||
|
||||
return view('admin.account.action.delete', [
|
||||
'account' => $account,
|
||||
'action' => $account->actions()
|
||||
->where('id', $actionId)
|
||||
->firstOrFail()
|
||||
|
|
@ -17,13 +17,13 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
namespace App\Http\Controllers\Admin\Account;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
use App\Account;
|
||||
|
||||
class AccountActivityController extends Controller
|
||||
class ActivityController extends Controller
|
||||
{
|
||||
public function index(int $accountId)
|
||||
{
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2023 Belledonne Communications SARL, All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin\Account;
|
||||
|
||||
use App\Account;
|
||||
use App\AccountCardDavCredentials;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Http\Requests\Account\CardDavCredentials;
|
||||
|
||||
class CardDavCredentialsController extends Controller
|
||||
{
|
||||
public function create(int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$this->checkFeatureEnabled($account);
|
||||
|
||||
return view('admin.account.carddav.create', [
|
||||
'account' => $account,
|
||||
'carddavServers' => $account->remainingCardDavCredentialsCreatable
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(CardDavCredentials $request, int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$this->checkFeatureEnabled($account);
|
||||
|
||||
$request->validate([
|
||||
'carddav_id' => ['required', Rule::exists('space_carddav_servers', 'id')->where(function (Builder $query) use ($account) {
|
||||
return $query->where('space_id', $account->space->id);
|
||||
})]
|
||||
]);
|
||||
|
||||
$accountCarddavCredentials = new AccountCardDavCredentials;
|
||||
$accountCarddavCredentials->space_carddav_server_id = $request->get('carddav_id');
|
||||
$accountCarddavCredentials->account_id = $account->id;
|
||||
$accountCarddavCredentials->username = $request->get('username');
|
||||
$accountCarddavCredentials->realm = $request->get('realm');
|
||||
$accountCarddavCredentials->password = bchash(
|
||||
$request->get('username'),
|
||||
$request->get('realm'),
|
||||
$request->get('password'),
|
||||
$request->get('algorithm')
|
||||
);
|
||||
$accountCarddavCredentials->algorithm = $request->get('algorithm');
|
||||
$accountCarddavCredentials->save();
|
||||
|
||||
return redirect()->route('admin.account.show', $account);
|
||||
}
|
||||
|
||||
public function delete(int $accountId, int $cardDavId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$this->checkFeatureEnabled($account);
|
||||
|
||||
$accountCarddavCredentials = AccountCardDavCredentials::where('space_carddav_server_id', $cardDavId)
|
||||
->where('account_id', $account->id)
|
||||
->firstOrFail();
|
||||
|
||||
return view('admin.account.carddav.delete', [
|
||||
'account' => $account,
|
||||
'carddavCredentials' => $accountCarddavCredentials,
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(Request $request, int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$this->checkFeatureEnabled($account);
|
||||
|
||||
$accountCarddavCredentials = AccountCardDavCredentials::where('space_carddav_server_id', $request->carddav_id)
|
||||
->where('account_id', $account->id)
|
||||
->delete();
|
||||
|
||||
return redirect()->route('admin.account.show', $account);
|
||||
}
|
||||
|
||||
private function checkFeatureEnabled(Account $account)
|
||||
{
|
||||
if (!$account->space->carddav_user_credentials) {
|
||||
abort(403, 'CardDav Credentials features disabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
namespace App\Http\Controllers\Admin\Account;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
|
@ -26,7 +26,7 @@ use Illuminate\Support\Facades\Log;
|
|||
use App\Account;
|
||||
use App\ContactsList;
|
||||
|
||||
class AccountContactController extends Controller
|
||||
class ContactController extends Controller
|
||||
{
|
||||
public function index(int $accountId)
|
||||
{
|
||||
|
|
@ -17,14 +17,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
namespace App\Http\Controllers\Admin\Account;
|
||||
|
||||
use App\Account;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Libraries\FlexisipRedisConnector;
|
||||
|
||||
class AccountDeviceController extends Controller
|
||||
class DeviceController extends Controller
|
||||
{
|
||||
public function index(int $accountId)
|
||||
{
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
namespace App\Http\Controllers\Admin\Account;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
|
@ -25,7 +25,7 @@ use Illuminate\Http\Request;
|
|||
use App\Account;
|
||||
use App\AccountDictionaryEntry;
|
||||
|
||||
class AccountDictionaryController extends Controller
|
||||
class DictionaryController extends Controller
|
||||
{
|
||||
public function create(int $accountId)
|
||||
{
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin\Account;
|
||||
|
||||
use App\Account;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FileController extends Controller
|
||||
{
|
||||
public function delete(int $accountId, string $fileId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$file = $account->files()->where('id', $fileId)->firstOrFail();
|
||||
|
||||
return view('admin.account.file.delete', [
|
||||
'account' => $account,
|
||||
'file' => $file
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(Request $request, int $accountId, string $fileId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$accountFile = $account->files()
|
||||
->where('id', $fileId)
|
||||
->firstOrFail();
|
||||
$accountFile->delete();
|
||||
|
||||
return redirect()->route('admin.account.show', $account)->withFragment('#files');
|
||||
}
|
||||
}
|
||||
|
|
@ -17,12 +17,13 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
namespace App\Http\Controllers\Admin\Account;
|
||||
|
||||
use App\Account;
|
||||
use App\ExternalAccount;
|
||||
use App\Password;
|
||||
use App\PhoneCountry;
|
||||
use App\Space;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
|
|
@ -31,7 +32,7 @@ use Illuminate\Support\Facades\Storage;
|
|||
use Illuminate\Validation\Rules\File;
|
||||
use Propaganistas\LaravelPhone\PhoneNumber;
|
||||
|
||||
class AccountImportController extends Controller
|
||||
class ImportController extends Controller
|
||||
{
|
||||
private Collection $errors;
|
||||
private string $importDirectory = 'imported_csv';
|
||||
|
|
@ -44,7 +45,9 @@ class AccountImportController extends Controller
|
|||
public function create(Request $request)
|
||||
{
|
||||
return view('admin.account.import.create', [
|
||||
'domains' => Account::select('domain')->distinct()->get()->pluck('domain')
|
||||
'domains' => $request->user()->superAdmin
|
||||
? Space::pluck('domain')
|
||||
: [$request->user()->domain]
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -52,10 +55,12 @@ class AccountImportController extends Controller
|
|||
{
|
||||
$request->validate([
|
||||
'csv' => ['required', File::types(['csv', 'txt'])],
|
||||
'domain' => 'required|exists:accounts'
|
||||
'domain' => 'required|exists:spaces,domain'
|
||||
]);
|
||||
|
||||
$domain = $request->get('domain');
|
||||
$domain = $request->user()->superAdmin
|
||||
? $request->get('domain')
|
||||
: $request->user()->domain;
|
||||
|
||||
/**
|
||||
* General formating checking
|
||||
|
|
@ -148,7 +153,7 @@ class AccountImportController extends Controller
|
|||
// Emails
|
||||
|
||||
if ($emails = $lines->pluck('email')->filter(function ($value) {
|
||||
return !filter_var($value, FILTER_VALIDATE_EMAIL);
|
||||
return $value != '' && !filter_var($value, FILTER_VALIDATE_EMAIL);
|
||||
})) {
|
||||
if ($emails->isNotEmpty()) {
|
||||
$this->errors['Some emails are not correct'] = $emails->join(', ', ' and ');
|
||||
|
|
@ -162,7 +167,7 @@ class AccountImportController extends Controller
|
|||
$this->errors['Those emails numbers already exists'] = $existingEmails->join(', ', ' and ');
|
||||
}
|
||||
|
||||
if ($emails = $lines->pluck('email')->duplicates()) {
|
||||
if ($emails = $lines->pluck('email')->filter(fn (string $value) => $value != '')->duplicates()) {
|
||||
if ($emails->isNotEmpty()) {
|
||||
$this->errors['Those emails are declared several times'] = $emails->join(', ', ' and ');
|
||||
}
|
||||
|
|
@ -170,6 +175,8 @@ class AccountImportController extends Controller
|
|||
|
||||
// External account
|
||||
|
||||
$checkExternalUsernameDomains = collect();
|
||||
|
||||
foreach ($lines as $line) {
|
||||
if ($line->external_username != null && ($line->external_password == null || $line->external_domain == null)) {
|
||||
$this->errors['Line ' . $line->line . ': The mandatory external account columns must be filled'] = '';
|
||||
|
|
@ -186,16 +193,26 @@ class AccountImportController extends Controller
|
|||
$this->errors['Line ' . $line->line . ': External protocol must be UDP, TCP or TLS'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
$checkExternalUsernameDomains->push($line->external_username . ',' . $line->external_domain);
|
||||
}
|
||||
|
||||
foreach ($checkExternalUsernameDomains->duplicates() as $duplicate) {
|
||||
$this->errors['The following external account is used several times: ' . $duplicate] = '';
|
||||
}
|
||||
|
||||
$filePath = $this->errors->isEmpty()
|
||||
? Storage::putFile($this->importDirectory, $request->file('csv'))
|
||||
: null;
|
||||
|
||||
if ($filePath == false) {
|
||||
$this->errors['The CSV file was not imported properly on the server'] = '';
|
||||
}
|
||||
|
||||
return view('admin.account.import.check', [
|
||||
'linesCount' => $lines->count(),
|
||||
'errors' => $this->errors,
|
||||
'domain' => $request->get('domain'),
|
||||
'domain' => $domain,
|
||||
'filePath' => $filePath
|
||||
]);
|
||||
}
|
||||
|
|
@ -204,9 +221,13 @@ class AccountImportController extends Controller
|
|||
{
|
||||
$request->validate([
|
||||
'file_path' => 'required',
|
||||
'domain' => 'required|exists:accounts'
|
||||
'domain' => 'required|exists:spaces,domain'
|
||||
]);
|
||||
|
||||
$domain = $request->user()->superAdmin
|
||||
? $request->get('domain')
|
||||
: $request->user()->domain;
|
||||
|
||||
$lines = $this->csvToCollection(storage_path('app/' . $request->get('file_path')));
|
||||
|
||||
$accounts = [];
|
||||
|
|
@ -250,7 +271,7 @@ class AccountImportController extends Controller
|
|||
|
||||
array_push($accounts, [
|
||||
'username' => $line->username,
|
||||
'domain' => $request->get('domain'),
|
||||
'domain' => $domain,
|
||||
'email' => $line->email,
|
||||
'activated' => $line->status == 'active',
|
||||
'ip_address' => '127.0.0.1',
|
||||
|
|
@ -265,9 +286,10 @@ class AccountImportController extends Controller
|
|||
// Set admins accounts
|
||||
foreach ($admins as $username) {
|
||||
$account = Account::where('username', $username)
|
||||
->where('domain', $request->get('domain'))
|
||||
->where('domain', $domain)
|
||||
->first();
|
||||
$account->admin = true;
|
||||
$account->save();
|
||||
}
|
||||
|
||||
// Set passwords
|
||||
|
|
@ -275,7 +297,7 @@ class AccountImportController extends Controller
|
|||
$passwordsToInsert = [];
|
||||
|
||||
$passwordAccounts = Account::whereIn('username', array_keys($passwords))
|
||||
->where('domain', $request->get('domain'))
|
||||
->where('domain', $domain)
|
||||
->get();
|
||||
|
||||
$algorithm = config('app.account_default_password_algorithm');
|
||||
|
|
@ -285,7 +307,7 @@ class AccountImportController extends Controller
|
|||
'account_id' => $passwordAccount->id,
|
||||
'password' => bchash(
|
||||
$passwordAccount->username,
|
||||
space()?->account_realm ?? $request->get('domain'),
|
||||
$request->space?->account_realm ?? $domain,
|
||||
$passwords[$passwordAccount->username],
|
||||
$algorithm
|
||||
),
|
||||
|
|
@ -300,7 +322,7 @@ class AccountImportController extends Controller
|
|||
$externalAccountsToInsert = [];
|
||||
|
||||
$externalAccounts = Account::whereIn('username', array_keys($externals))
|
||||
->where('domain', $request->get('domain'))
|
||||
->where('domain', $domain)
|
||||
->get();
|
||||
|
||||
foreach ($externalAccounts as $externalAccount) {
|
||||
|
|
@ -314,7 +336,7 @@ class AccountImportController extends Controller
|
|||
// Set phone accounts
|
||||
foreach ($phones as $username => $phone) {
|
||||
$account = Account::where('username', $username)
|
||||
->where('domain', $request->get('domain'))
|
||||
->where('domain', $domain)
|
||||
->first();
|
||||
$account->phone = $phone;
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
namespace App\Http\Controllers\Admin\Account;
|
||||
|
||||
use App\Account;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
|
@ -26,7 +26,7 @@ use App\StatisticsCall;
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class AccountStatisticsController extends Controller
|
||||
class StatisticsController extends Controller
|
||||
{
|
||||
public function edit(Request $request, int $accountId)
|
||||
{
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
namespace App\Http\Controllers\Admin\Account;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
|
@ -27,7 +27,7 @@ use Illuminate\Validation\Rule;
|
|||
use App\AccountType;
|
||||
use App\Rules\NoUppercase;
|
||||
|
||||
class AccountTypeController extends Controller
|
||||
class TypeController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin\Space;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Space\CardDavServer;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use App\Space;
|
||||
use App\SpaceCardDavServer;
|
||||
|
||||
class CardDavServerController extends Controller
|
||||
{
|
||||
public function create(Space $space)
|
||||
{
|
||||
return view('admin.space.carddav_server.create_edit', [
|
||||
'space' => $space,
|
||||
'carddavServer' => new SpaceCardDavServer
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(CardDavServer $request, Space $space)
|
||||
{
|
||||
$carddavServer = new SpaceCardDavServer;
|
||||
$carddavServer->space_id = $space->id;
|
||||
$carddavServer->fill($request->validated());
|
||||
$carddavServer->enabled = getRequestBoolean($request, 'enabled');
|
||||
$carddavServer->use_exact_match_policy = getRequestBoolean($request, 'use_exact_match_policy');
|
||||
$carddavServer->save();
|
||||
|
||||
return redirect()->route('admin.spaces.integration', $space);
|
||||
}
|
||||
|
||||
public function edit(Space $space, int $carddavServerId)
|
||||
{
|
||||
return view('admin.space.carddav_server.create_edit', [
|
||||
'space' => $space,
|
||||
'carddavServer' => $space->carddavServers()->findOrFail($carddavServerId)
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(CardDavServer $request, Space $space, int $carddavServerId)
|
||||
{
|
||||
$carddavServer = $space->carddavServers()->findOrFail($carddavServerId);
|
||||
$carddavServer->fill($request->validated());
|
||||
$carddavServer->enabled = getRequestBoolean($request, 'enabled');
|
||||
$carddavServer->use_exact_match_policy = getRequestBoolean($request, 'use_exact_match_policy');
|
||||
$carddavServer->save();
|
||||
|
||||
return redirect()->route('admin.spaces.integration', $space);
|
||||
}
|
||||
|
||||
public function delete(Space $space, int $carddavServerId)
|
||||
{
|
||||
return view('admin.space.carddav_server.delete', [
|
||||
'space' => $space,
|
||||
'carddavServer' => $space->carddavServers()->findOrFail($carddavServerId)
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(Space $space, int $carddavServerId)
|
||||
{
|
||||
$carddavServer = $space->carddavServers()->findOrFail($carddavServerId);
|
||||
$carddavServer->delete();
|
||||
|
||||
return redirect()->route('admin.spaces.integration', $space->id);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,18 +17,19 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
namespace App\Http\Controllers\Admin\Space;
|
||||
|
||||
use App\Account;
|
||||
use App\ContactsList;
|
||||
use App\Space;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ContactsListContactController extends Controller
|
||||
{
|
||||
public function add(Request $request, int $contactsListId)
|
||||
public function add(Request $request, Space $space, int $contactsListId)
|
||||
{
|
||||
$accounts = Account::orderBy('updated_at', $request->get('updated_at_order', 'desc'));
|
||||
$accounts = $space->accounts()->orderBy('updated_at', $request->get('updated_at_order', 'desc'));
|
||||
|
||||
if ($request->has('search')) {
|
||||
$accounts = $accounts->where('username', 'like', '%' . $request->get('search') . '%');
|
||||
|
|
@ -38,9 +39,9 @@ class ContactsListContactController extends Controller
|
|||
$accounts = $accounts->where('domain', $request->get('domain'));
|
||||
}
|
||||
|
||||
return view('admin.contacts_list.contacts.add', [
|
||||
'domains' => Account::groupBy('domain')->pluck('domain'),
|
||||
'contacts_list' => ContactsList::findOrFail($contactsListId),
|
||||
return view('admin.space.contacts_list.contacts.add', [
|
||||
'space' => $space,
|
||||
'contacts_list' => $space->contactsLists()->findOrFail($contactsListId),
|
||||
'params' => [
|
||||
'contacts_list_id' => $contactsListId
|
||||
],
|
||||
|
|
@ -52,33 +53,33 @@ class ContactsListContactController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
public function search(Request $request, int $contactsListId)
|
||||
public function search(Request $request, Space $space, int $contactsListId)
|
||||
{
|
||||
return redirect()->route('admin.contacts_lists.contacts.add', ['contacts_list_id' => $contactsListId] + $request->except('_token'));
|
||||
return redirect()->route('admin.spaces.contacts_lists.contacts.add', ['contacts_list_id' => $contactsListId] + $request->except('_token'));
|
||||
}
|
||||
|
||||
public function store(Request $request, int $contactsListId)
|
||||
public function store(Request $request, Space $space, int $contactsListId)
|
||||
{
|
||||
$request->validate([
|
||||
'contacts_ids' => 'required|exists:accounts,id'
|
||||
]);
|
||||
|
||||
$contactsList = ContactsList::findOrFail($contactsListId);
|
||||
$contactsList = $space->contactsLists()->findOrFail($contactsListId);
|
||||
$contactsList->contacts()->detach($request->get('contacts_ids')); // Just in case
|
||||
$contactsList->contacts()->attach($request->get('contacts_ids'));
|
||||
|
||||
return redirect()->route('admin.contacts_lists.edit', $contactsList->id);
|
||||
return redirect()->route('admin.spaces.contacts_lists.edit', [$space, $contactsList->id]);
|
||||
}
|
||||
|
||||
public function destroy(Request $request, int $contactsListId)
|
||||
public function destroy(Request $request, Space $space, int $contactsListId)
|
||||
{
|
||||
$request->validate([
|
||||
'contacts_ids' => 'required|exists:accounts,id'
|
||||
]);
|
||||
|
||||
$contactsList = ContactsList::findOrFail($contactsListId);
|
||||
$contactsList = $space->contactsLists()->findOrFail($contactsListId);
|
||||
$contactsList->contacts()->detach($request->get('contacts_ids'));
|
||||
|
||||
return redirect()->route('admin.contacts_lists.edit', $contactsList->id);
|
||||
return redirect()->route('admin.spaces.contacts_lists.edit', [$space, $contactsList->id]);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,61 +17,67 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
namespace App\Http\Controllers\Admin\Space;
|
||||
|
||||
use App\Account;
|
||||
use App\ContactsList;
|
||||
use App\Space;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ContactsListController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
public function index(Request $request, Space $space)
|
||||
{
|
||||
$request->validate([
|
||||
'order_by' => 'in:title,updated_at,contacts_count',
|
||||
'order_sort' => 'in:asc,desc',
|
||||
]);
|
||||
|
||||
$contactsLists = ContactsList::orderBy($request->get('order_by', 'updated_at'), $request->get('order_sort', 'desc'));
|
||||
$contactsLists = $space->contactsLists()->orderBy($request->get('order_by', 'updated_at'), $request->get('order_sort', 'desc'));
|
||||
|
||||
return view('admin.contacts_list.index', [
|
||||
return view('admin.space.contacts_list.index', [
|
||||
'space' => $space,
|
||||
'contacts_lists' => $contactsLists
|
||||
->paginate(20)
|
||||
->appends($request->query()),
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(Request $request)
|
||||
public function create(Request $request, Space $space)
|
||||
{
|
||||
return view('admin.contacts_list.create_edit', [
|
||||
return view('admin.space.contacts_list.create_edit', [
|
||||
'space' => $space,
|
||||
'contacts_list' => new ContactsList,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
public function store(Request $request, Space $space)
|
||||
{
|
||||
$request->validate([
|
||||
'title' => 'required|unique:contacts_lists'
|
||||
]);
|
||||
|
||||
$contactsList = new ContactsList;
|
||||
$contactsList->space_id = $space->id;
|
||||
$contactsList->title = $request->get('title');
|
||||
$contactsList->description = $request->get('description');
|
||||
$contactsList->save();
|
||||
|
||||
return redirect()->route('admin.contacts_lists.edit', $contactsList->id);
|
||||
return redirect()->route('admin.spaces.contacts_lists.edit', [$space, $contactsList->id]);
|
||||
}
|
||||
|
||||
public function search(Request $request, int $contactsListId)
|
||||
public function search(Request $request, Space $space, int $contactsListId)
|
||||
{
|
||||
return redirect()->route('admin.contacts_lists.edit', ['contacts_list_id' => $contactsListId] + $request->except('_token'));
|
||||
return redirect()->route('admin.spaces.contacts_lists.edit', [
|
||||
'space' => $space,
|
||||
'contacts_list_id' => $contactsListId] + $request->except('_token'));
|
||||
}
|
||||
|
||||
public function edit(Request $request, int $id)
|
||||
public function edit(Request $request, Space $space, int $id)
|
||||
{
|
||||
$contacts = ContactsList::findOrFail($id)->contacts();
|
||||
$contacts = $space->contactsLists()->findOrFail($id)->contacts();
|
||||
|
||||
if ($request->has('search')) {
|
||||
$contacts = $contacts->where('username', 'like', '%' . $request->get('search') . '%');
|
||||
|
|
@ -83,14 +89,15 @@ class ContactsListController extends Controller
|
|||
|
||||
$contacts = $contacts->get();
|
||||
|
||||
return view('admin.contacts_list.create_edit', [
|
||||
return view('admin.space.contacts_list.create_edit', [
|
||||
'space' => $space,
|
||||
'domains' => Account::groupBy('domain')->pluck('domain'),
|
||||
'contacts_list' => ContactsList::findOrFail($id),
|
||||
'contacts_list' => $space->contactsLists()->findOrFail($id),
|
||||
'contacts' => $contacts
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, int $id)
|
||||
public function update(Request $request, Space $space, int $id)
|
||||
{
|
||||
$request->validate([
|
||||
'title' => [
|
||||
|
|
@ -99,26 +106,27 @@ class ContactsListController extends Controller
|
|||
],
|
||||
]);
|
||||
|
||||
$contactsList = ContactsList::findOrFail($id);
|
||||
$contactsList = $space->contactsLists()->findOrFail($id);
|
||||
$contactsList->title = $request->get('title');
|
||||
$contactsList->description = $request->get('description');
|
||||
$contactsList->save();
|
||||
|
||||
return redirect()->route('admin.contacts_lists.index');
|
||||
return redirect()->route('admin.spaces.contacts_lists.index', $space);
|
||||
}
|
||||
|
||||
public function delete(int $id)
|
||||
public function delete(Space $space, int $id)
|
||||
{
|
||||
return view('admin.contacts_list.delete', [
|
||||
'contacts_list' => ContactsList::findOrFail($id),
|
||||
return view('admin.space.contacts_list.delete', [
|
||||
'space' => $space,
|
||||
'contacts_list' => $space->contactsLists()->findOrFail($id),
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(Request $request)
|
||||
public function destroy(Request $request, Space $space)
|
||||
{
|
||||
$contactsList = ContactsList::findOrFail($request->get('contacts_lists_id'));
|
||||
$contactsList = $space->contactsLists()->findOrFail($request->get('contacts_lists_id'));
|
||||
$contactsList->delete();
|
||||
|
||||
return redirect()->route('admin.contacts_lists.index');
|
||||
return redirect()->route('admin.spaces.contacts_lists.index', $space);
|
||||
}
|
||||
}
|
||||
|
|
@ -146,6 +146,7 @@ class SpaceController extends Controller
|
|||
$space->expire_at = $request->get('expire_at');
|
||||
$space->custom_theme = getRequestBoolean($request, 'custom_theme');
|
||||
$space->web_panel = getRequestBoolean($request, 'web_panel');
|
||||
$space->carddav_user_credentials = getRequestBoolean($request, 'carddav_user_credentials');
|
||||
$space->save();
|
||||
|
||||
return redirect()->route('admin.spaces.show', $space);
|
||||
|
|
|
|||
|
|
@ -106,9 +106,13 @@ class StatisticsController extends Controller
|
|||
$fromQuery = StatisticsCall::query();
|
||||
$toQuery = StatisticsCall::query();
|
||||
|
||||
if ($request->get('domain')) {
|
||||
$fromQuery->where('to_domain', $request->get('domain'));
|
||||
$toQuery->where('from_domain', $request->get('domain'));
|
||||
$domain = $request->user()->superAdmin
|
||||
? $request->get('domain')
|
||||
: $request->space->domain;
|
||||
|
||||
if ($domain) {
|
||||
$fromQuery->where('to_domain', $domain);
|
||||
$toQuery->where('from_domain', $domain);
|
||||
}
|
||||
|
||||
if ($request->get('to')) {
|
||||
|
|
|
|||
42
flexiapi/app/Http/Controllers/Api/Account/FileController.php
Normal file
42
flexiapi/app/Http/Controllers/Api/Account/FileController.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Account;
|
||||
|
||||
use App\AccountFile;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Rules\AudioMime;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class FileController extends Controller
|
||||
{
|
||||
public function upload(Request $request, string $uuid)
|
||||
{
|
||||
$file = AccountFile::findOrFail($uuid);
|
||||
|
||||
if (!empty($file->name)) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$request->validate(['file' => 'required|file']);
|
||||
|
||||
if ($file->isVoicemailAudio()) {
|
||||
$request->validate(['file' => [new AudioMime($file)]]);
|
||||
}
|
||||
|
||||
$uploadedFile = $request->file('file');
|
||||
$name = Str::random(8) . '_' . $uploadedFile->getClientOriginalName();
|
||||
|
||||
if ($uploadedFile->storeAs(AccountFile::FILES_PATH, $name)) {
|
||||
$file->name = $name;
|
||||
$file->size = $uploadedFile->getSize();
|
||||
$file->uploaded_at = Carbon::now();
|
||||
$file->save();
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
abort(503);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,12 +10,12 @@ class VcardsStorageController extends Controller
|
|||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
return (new AdminVcardsStorageController)->index($request->user()->id);
|
||||
return (new AdminVcardsStorageController)->index($request, $request->user()->id);
|
||||
}
|
||||
|
||||
public function show(Request $request, string $uuid)
|
||||
{
|
||||
return (new AdminVcardsStorageController)->show($request->user()->id, $uuid);
|
||||
return (new AdminVcardsStorageController)->show($request, $request->user()->id, $uuid);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
|
|
@ -30,6 +30,6 @@ class VcardsStorageController extends Controller
|
|||
|
||||
public function destroy(Request $request, string $uuid)
|
||||
{
|
||||
return (new AdminVcardsStorageController)->destroy($request->user()->id, $uuid);
|
||||
return (new AdminVcardsStorageController)->destroy($request, $request->user()->id, $uuid);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Account;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\Api\Admin\Account\VoicemailController as AdminVoicemailController;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class VoicemailController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
return (new AdminVoicemailController)->index($request, $request->user()->id);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
return (new AdminVoicemailController)->store($request, $request->user()->id);
|
||||
}
|
||||
|
||||
public function show(Request $request, string $uuid)
|
||||
{
|
||||
return (new AdminVoicemailController)->show($request, $request->user()->id, $uuid);
|
||||
}
|
||||
|
||||
public function destroy(Request $request, string $uuid)
|
||||
{
|
||||
return (new AdminVoicemailController)->destroy($request, $request->user()->id, $uuid);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,33 +17,32 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin;
|
||||
namespace App\Http\Controllers\Api\Admin\Account;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use App\Account;
|
||||
use App\AccountAction;
|
||||
use App\Rules\NoUppercase;
|
||||
|
||||
class AccountActionController extends Controller
|
||||
class ActionController extends Controller
|
||||
{
|
||||
public function index(int $id)
|
||||
public function index(Request $request, int $accountId)
|
||||
{
|
||||
return $this->resolveAccount($id)->actions;
|
||||
return $this->resolveAccount($request, $accountId)->actions;
|
||||
}
|
||||
|
||||
public function get(int $id, int $actionId)
|
||||
public function get(Request $request, int $accountId, int $actionId)
|
||||
{
|
||||
return $this->resolveAccount($id)
|
||||
return $this->resolveAccount($request, $accountId)
|
||||
->actions()
|
||||
->where('id', $actionId)
|
||||
->firstOrFail();
|
||||
}
|
||||
|
||||
public function store(Request $request, int $id)
|
||||
public function store(Request $request, int $accountId)
|
||||
{
|
||||
$account = $this->resolveAccount($id);
|
||||
$account = $this->resolveAccount($request, $accountId);
|
||||
|
||||
$request->validate([
|
||||
'key' => ['required', 'alpha_dash', new NoUppercase],
|
||||
|
|
@ -59,9 +58,9 @@ class AccountActionController extends Controller
|
|||
return $accountAction;
|
||||
}
|
||||
|
||||
public function update(Request $request, int $id, int $actionId)
|
||||
public function update(Request $request, int $accountId, int $actionId)
|
||||
{
|
||||
$account = $this->resolveAccount($id);
|
||||
$account = $this->resolveAccount($request, $accountId);
|
||||
|
||||
$request->validate([
|
||||
'key' => ['alpha_dash', new NoUppercase],
|
||||
|
|
@ -79,17 +78,17 @@ class AccountActionController extends Controller
|
|||
return $accountAction;
|
||||
}
|
||||
|
||||
public function destroy(int $id, int $actionId)
|
||||
public function destroy(Request $request, int $accountId, int $actionId)
|
||||
{
|
||||
return $this->resolveAccount($id)
|
||||
return $this->resolveAccount($request, $accountId)
|
||||
->actions()
|
||||
->where('id', $actionId)
|
||||
->delete();
|
||||
}
|
||||
|
||||
private function resolveAccount(int $id)
|
||||
private function resolveAccount(Request $request, int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($id);
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
if ($account->dtmf_protocol == null) abort(403, 'DTMF Protocol must be configured');
|
||||
|
||||
return $account;
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin\Account;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use App\Account;
|
||||
use App\Space;
|
||||
use App\AccountCardDavCredentials;
|
||||
use App\SpaceCardDavServer;
|
||||
use App\Http\Requests\Account\CardDavCredentials;
|
||||
|
||||
class CardDavCredentialsController extends Controller
|
||||
{
|
||||
public function index(Request $request, int $accountId)
|
||||
{
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
$cardDavServers = $account->carddavServers;
|
||||
|
||||
if ($cardDavServers->isEmpty()) return new \stdClass;
|
||||
|
||||
return $cardDavServers->map(function ($cardDavServer) {
|
||||
return $this->extractCardDavServer($cardDavServer);
|
||||
})->keyBy('carddav_id');
|
||||
}
|
||||
|
||||
public function show(Request $request, int $accountId, int $cardDavServerId)
|
||||
{
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
$cardDavServer = $account->cardDavServers()->findOrFail($cardDavServerId);
|
||||
|
||||
return $this->extractCardDavServer($cardDavServer);
|
||||
}
|
||||
|
||||
public function update(CardDavCredentials $request, int $accountId, int $cardDavServerId)
|
||||
{
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
$cardDavServer = $request->space->cardDavServers()->findOrFail($cardDavServerId);
|
||||
|
||||
$accountCarddavCredentials = AccountCardDavCredentials::where('account_id', $account->id)
|
||||
->where('space_carddav_server_id', $cardDavServer->id)
|
||||
->delete();
|
||||
|
||||
$accountCarddavCredentials = new AccountCardDavCredentials;
|
||||
$accountCarddavCredentials->space_carddav_server_id = $cardDavServer->id;
|
||||
$accountCarddavCredentials->account_id = $account->id;
|
||||
$accountCarddavCredentials->username = $request->get('username');
|
||||
$accountCarddavCredentials->realm = $request->get('realm');
|
||||
$accountCarddavCredentials->password = bchash(
|
||||
$request->get('username'),
|
||||
$request->get('realm'),
|
||||
$request->get('password'),
|
||||
$request->get('algorithm')
|
||||
);
|
||||
$accountCarddavCredentials->algorithm = $request->get('algorithm');
|
||||
return $accountCarddavCredentials->save();
|
||||
}
|
||||
|
||||
public function destroy(Request $request, int $accountId, int $cardDavServerId)
|
||||
{
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
$cardDavServer = $account->cardDavServers()->findOrFail($cardDavServerId);
|
||||
|
||||
return $cardDavServer->delete();
|
||||
}
|
||||
|
||||
private function extractCardDavServer(SpaceCardDavServer $cardDavServer)
|
||||
{
|
||||
return [
|
||||
'carddav_id' => $cardDavServer->id,
|
||||
'username' => $cardDavServer->pivot->username,
|
||||
'realm' => $cardDavServer->pivot->realm,
|
||||
'algorithm' => $cardDavServer->pivot->algorithm,
|
||||
'password' => $cardDavServer->pivot->password,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -17,40 +17,39 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin;
|
||||
namespace App\Http\Controllers\Api\Admin\Account;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use App\Account;
|
||||
|
||||
class AccountContactController extends Controller
|
||||
class ContactController extends Controller
|
||||
{
|
||||
public function index(int $id)
|
||||
public function index(Request $request, int $accountId)
|
||||
{
|
||||
return Account::findOrFail($id)->contacts;
|
||||
return $request->space->accounts()->findOrFail($accountId)->contacts;
|
||||
}
|
||||
|
||||
public function show(int $id, int $contactId)
|
||||
public function show(Request $request, int $accountId, int $contactId)
|
||||
{
|
||||
return Account::findOrFail($id)
|
||||
return $request->space->accounts()->findOrFail($accountId)
|
||||
->contacts()
|
||||
->where('id', $contactId)
|
||||
->firstOrFail();
|
||||
}
|
||||
|
||||
public function add(int $id, int $contactId)
|
||||
public function add(Request $request, int $accountId, int $contactId)
|
||||
{
|
||||
$account = Account::findOrFail($id);
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
$account->contacts()->detach($contactId);
|
||||
|
||||
if (Account::findOrFail($contactId)) {
|
||||
if ($request->space->accounts()->findOrFail($contactId)) {
|
||||
return $account->contacts()->attach($contactId);
|
||||
}
|
||||
}
|
||||
|
||||
public function remove(int $id, int $contactId)
|
||||
public function remove(Request $request, int $accountId, int $contactId)
|
||||
{
|
||||
$account = Account::findOrFail($id);
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
|
||||
if (!$account->contacts()->pluck('id')->contains($contactId)) {
|
||||
abort(404);
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin;
|
||||
namespace App\Http\Controllers\Api\Admin\Account;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
|
@ -26,7 +26,7 @@ use Illuminate\Support\Str;
|
|||
use App\AccountCreationToken;
|
||||
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
|
||||
|
||||
class AccountCreationTokenController extends Controller
|
||||
class CreationTokenController extends Controller
|
||||
{
|
||||
public function create(Request $request)
|
||||
{
|
||||
|
|
@ -17,23 +17,23 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin;
|
||||
namespace App\Http\Controllers\Api\Admin\Account;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Account;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AccountDictionaryController extends Controller
|
||||
class DictionaryController extends Controller
|
||||
{
|
||||
public function index(int $accountId)
|
||||
public function index(Request $request, int $accountId)
|
||||
{
|
||||
return Account::findOrFail($accountId)->dictionary;
|
||||
return $request->space->accounts()->findOrFail($accountId)->dictionary;
|
||||
}
|
||||
|
||||
public function show(int $accountId, string $key)
|
||||
public function show(Request $request, int $accountId, string $key)
|
||||
{
|
||||
return Account::findOrFail($accountId)->dictionaryEntries()->where('key', $key)->first();
|
||||
return $request->space->accounts()
|
||||
->findOrFail($accountId)->dictionaryEntries()->where('key', $key)->first();
|
||||
}
|
||||
|
||||
public function set(Request $request, int $accountId, string $key)
|
||||
|
|
@ -42,7 +42,7 @@ class AccountDictionaryController extends Controller
|
|||
'value' => 'required'
|
||||
]);
|
||||
|
||||
$account = Account::findOrFail($accountId);
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
$result = $account->setDictionaryEntry($key, $request->get('value'));
|
||||
|
||||
if (function_exists('accountServiceAccountEditedHook')) {
|
||||
|
|
@ -53,8 +53,15 @@ class AccountDictionaryController extends Controller
|
|||
return $result;
|
||||
}
|
||||
|
||||
public function destroy(int $accountId, string $key)
|
||||
public function destroy(Request $request, int $accountId, string $key)
|
||||
{
|
||||
return Account::findOrFail($accountId)->dictionaryEntries()->where('key', $key)->delete();
|
||||
return $request->space->accounts()
|
||||
->findOrFail($accountId)->dictionaryEntries()->where('key', $key)->delete();
|
||||
}
|
||||
|
||||
public function clear(Request $request, int $accountId)
|
||||
{
|
||||
return $request->space->accounts()
|
||||
->findOrFail($accountId)->dictionaryEntries()->delete();
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin;
|
||||
namespace App\Http\Controllers\Api\Admin\Account;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Rules\NoUppercase;
|
||||
|
|
@ -25,7 +25,7 @@ use Illuminate\Http\Request;
|
|||
|
||||
use App\AccountType;
|
||||
|
||||
class AccountTypeController extends Controller
|
||||
class TypeController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin\Account;
|
||||
|
||||
use App\Account;
|
||||
use App\AccountFile;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class VoicemailController extends Controller
|
||||
{
|
||||
public function index(Request $request, int $accountId)
|
||||
{
|
||||
return Account::findOrFail($accountId)->voicemails;
|
||||
}
|
||||
|
||||
public function store(Request $request, int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$request->validate([
|
||||
'sip_from' => 'nullable|starts_with:sip',
|
||||
'content_type' => [
|
||||
'required',
|
||||
Rule::in(AccountFile::VOICEMAIL_CONTENTTYPES),
|
||||
]
|
||||
]);
|
||||
|
||||
$voicemail = new AccountFile;
|
||||
$voicemail->account_id = $account->id;
|
||||
$voicemail->sip_from = $request->get('sip_from');
|
||||
$voicemail->content_type = $request->get('content_type');
|
||||
$voicemail->save();
|
||||
|
||||
$voicemail->append(['upload_url', 'max_upload_size']);
|
||||
|
||||
return $voicemail;
|
||||
}
|
||||
|
||||
public function show(Request $request, int $accountId, string $uuid)
|
||||
{
|
||||
return Account::findOrFail($accountId)->voicemails()->where('id', $uuid)->firstOrFail();
|
||||
}
|
||||
|
||||
public function destroy(Request $request, int $accountId, string $uuid)
|
||||
{
|
||||
return Account::findOrFail($accountId)->voicemails()->where('id', $uuid)->delete();
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,6 @@ use Illuminate\Support\Str;
|
|||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
use App\Account;
|
||||
use App\AccountTombstone;
|
||||
use App\AccountType;
|
||||
use App\ContactsList;
|
||||
|
|
@ -35,18 +34,23 @@ use App\Http\Requests\Account\Update\Api\AsAdminRequest as ApiAsAdminRequest;
|
|||
use App\Mail\Provisioning;
|
||||
use App\Mail\ResetPassword;
|
||||
use App\Services\AccountService;
|
||||
use App\Space;
|
||||
|
||||
class AccountController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
return Account::without(['passwords', 'admin'])->with(['phoneChangeCode', 'emailChangeCode'])->paginate(20);
|
||||
return $request->space->accounts()
|
||||
->without(['passwords', 'admin'])
|
||||
->with(['phoneChangeCode', 'emailChangeCode'])
|
||||
->paginate(20);
|
||||
}
|
||||
|
||||
public function show(Request $request, $accountId)
|
||||
{
|
||||
$account = Account::without(['passwords', 'admin'])->with(['phoneChangeCode', 'emailChangeCode'])->findOrFail($accountId);
|
||||
$account = $request->space->accounts()
|
||||
->without(['passwords', 'admin'])
|
||||
->with(['phoneChangeCode', 'emailChangeCode'])
|
||||
->findOrFail($accountId);
|
||||
|
||||
if ($request->user()->admin) {
|
||||
if ($account->phoneChangeCode) {
|
||||
|
|
@ -61,27 +65,29 @@ class AccountController extends Controller
|
|||
return $account;
|
||||
}
|
||||
|
||||
public function search(string $sip)
|
||||
public function search(Request $request, string $sip)
|
||||
{
|
||||
$account = Account::sip($sip)->first();
|
||||
$account = $request->space->accounts()->sip($sip)->first();
|
||||
|
||||
if (!$account) abort(404, 'SIP address not found');
|
||||
if (!$account)
|
||||
abort(404, 'SIP address not found');
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
public function searchByEmail(string $email)
|
||||
public function searchByEmail(Request $request, string $email)
|
||||
{
|
||||
$account = Account::where('email', $email)->first();
|
||||
$account = $request->space->accounts()->where('email', $email)->first();
|
||||
|
||||
if (!$account) abort(404, 'Email address not found');
|
||||
if (!$account)
|
||||
abort(404, 'Email address not found');
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
public function destroy(Request $request, int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
|
||||
if (!$account->hasTombstone()) {
|
||||
$tombstone = new AccountTombstone();
|
||||
|
|
@ -95,9 +101,9 @@ class AccountController extends Controller
|
|||
Log::channel('events')->info('API Admin: Account destroyed', ['id' => $account->identifier]);
|
||||
}
|
||||
|
||||
public function activate(int $accountId)
|
||||
public function activate(Request $request, int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
$account->activated = true;
|
||||
$account->save();
|
||||
|
||||
|
|
@ -106,9 +112,9 @@ class AccountController extends Controller
|
|||
return $account;
|
||||
}
|
||||
|
||||
public function deactivate(int $accountId)
|
||||
public function deactivate(Request $request, int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
$account->activated = false;
|
||||
$account->save();
|
||||
|
||||
|
|
@ -117,9 +123,9 @@ class AccountController extends Controller
|
|||
return $account;
|
||||
}
|
||||
|
||||
public function block(int $accountId)
|
||||
public function block(Request $request, int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
$account->blocked = true;
|
||||
$account->save();
|
||||
|
||||
|
|
@ -128,9 +134,9 @@ class AccountController extends Controller
|
|||
return $account;
|
||||
}
|
||||
|
||||
public function unblock(int $accountId)
|
||||
public function unblock(Request $request, int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
$account->blocked = false;
|
||||
$account->save();
|
||||
|
||||
|
|
@ -139,9 +145,9 @@ class AccountController extends Controller
|
|||
return $account;
|
||||
}
|
||||
|
||||
public function provision(int $accountId)
|
||||
public function provision(Request $request, int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
$account->provision();
|
||||
$account->save();
|
||||
|
||||
|
|
@ -164,55 +170,56 @@ class AccountController extends Controller
|
|||
return $account->makeVisible(['provisioning_token']);
|
||||
}
|
||||
|
||||
public function typeAdd(int $accountId, int $typeId)
|
||||
public function typeAdd(Request $request, int $accountId, int $typeId)
|
||||
{
|
||||
if (Account::findOrFail($accountId)->types()->pluck('id')->contains($typeId)) {
|
||||
if ($request->space->accounts()->findOrFail($accountId)->types()->pluck('id')->contains($typeId)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
if (AccountType::findOrFail($typeId)) {
|
||||
return Account::findOrFail($accountId)->types()->attach($typeId);
|
||||
return $request->space->accounts()->findOrFail($accountId)->types()->attach($typeId);
|
||||
}
|
||||
}
|
||||
|
||||
public function typeRemove(int $accountId, int $typeId)
|
||||
public function typeRemove(Request $request, int $accountId, int $typeId)
|
||||
{
|
||||
if (!Account::findOrFail($accountId)->types()->pluck('id')->contains($typeId)) {
|
||||
if (!$request->space->accounts()->findOrFail($accountId)->types()->pluck('id')->contains($typeId)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return Account::findOrFail($accountId)->types()->detach($typeId);
|
||||
return $request->space->accounts()->findOrFail($accountId)->types()->detach($typeId);
|
||||
}
|
||||
|
||||
public function contactsListAdd(int $accountId, int $contactsListId)
|
||||
public function contactsListAdd(Request $request, int $accountId, int $contactsListId)
|
||||
{
|
||||
if (Account::findOrFail($accountId)->contactsLists()->pluck('id')->contains($contactsListId)) {
|
||||
if ($request->space->accounts()->findOrFail($accountId)->contactsLists()->pluck('id')->contains($contactsListId)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
if (ContactsList::findOrFail($contactsListId)) {
|
||||
return Account::findOrFail($accountId)->contactsLists()->attach($contactsListId);
|
||||
return $request->space->accounts()->findOrFail($accountId)->contactsLists()->attach($contactsListId);
|
||||
}
|
||||
}
|
||||
|
||||
public function contactsListRemove(int $accountId, int $contactsListId)
|
||||
public function contactsListRemove(Request $request, int $accountId, int $contactsListId)
|
||||
{
|
||||
if (!Account::findOrFail($accountId)->contactsLists()->pluck('id')->contains($contactsListId)) {
|
||||
if (!$request->space->accounts()->findOrFail($accountId)->contactsLists()->pluck('id')->contains($contactsListId)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return Account::findOrFail($accountId)->contactsLists()->detach($contactsListId);
|
||||
return $request->space->accounts()->findOrFail($accountId)->contactsLists()->detach($contactsListId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emails
|
||||
*/
|
||||
|
||||
public function sendProvisioningEmail(int $accountId)
|
||||
public function sendProvisioningEmail(Request $request, int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
|
||||
if (!$account->email) abort(403, 'No email configured');
|
||||
if (!$account->email)
|
||||
abort(403, 'No email configured');
|
||||
|
||||
$account->provision();
|
||||
|
||||
|
|
@ -221,11 +228,12 @@ class AccountController extends Controller
|
|||
Log::channel('events')->info('API: Sending provisioning email', ['id' => $account->identifier]);
|
||||
}
|
||||
|
||||
public function sendResetPasswordEmail(int $accountId)
|
||||
public function sendResetPasswordEmail(Request $request, int $accountId)
|
||||
{
|
||||
$account = Account::findOrFail($accountId);
|
||||
$account = $request->space->accounts()->findOrFail($accountId);
|
||||
|
||||
if (!$account->email) abort(403, 'No email configured');
|
||||
if (!$account->email)
|
||||
abort(403, 'No email configured');
|
||||
|
||||
$resetPasswordEmail = new ResetPasswordEmailToken;
|
||||
$resetPasswordEmail->account_id = $account->id;
|
||||
|
|
|
|||
|
|
@ -22,21 +22,27 @@ namespace App\Http\Controllers\Api\Admin;
|
|||
use App\Http\Controllers\Controller;
|
||||
use App\Libraries\FlexisipRedisConnector;
|
||||
use App\Account;
|
||||
use Illuminate\Http\Request;
|
||||
use stdClass;
|
||||
|
||||
class DeviceController extends Controller
|
||||
{
|
||||
public function index(int $accountId)
|
||||
public function index(Request $request, int $accountId)
|
||||
{
|
||||
$devices = (new FlexisipRedisConnector)->getDevices(Account::findOrFail($accountId)->identifier);
|
||||
$devices = (new FlexisipRedisConnector)->getDevices(
|
||||
$request->space->accounts()->findOrFail($accountId)->identifier
|
||||
);
|
||||
|
||||
return ($devices->isEmpty()) ? new stdClass : $devices;
|
||||
}
|
||||
|
||||
public function destroy(int $accountId, string $uuid)
|
||||
public function destroy(Request $request, int $accountId, string $uuid)
|
||||
{
|
||||
$connector = new FlexisipRedisConnector;
|
||||
|
||||
return $connector->deleteDevice(Account::findOrFail($accountId)->identifier, $uuid);
|
||||
return $connector->deleteDevice(
|
||||
$request->space->accounts()->findOrFail($accountId)->identifier,
|
||||
$uuid
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,16 +23,12 @@ use App\Http\Controllers\Controller;
|
|||
use App\Http\Requests\ExternalAccount\CreateUpdate;
|
||||
use App\Services\AccountService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
use App\ExternalAccount;
|
||||
use App\Account;
|
||||
|
||||
class ExternalAccountController extends Controller
|
||||
{
|
||||
public function show(int $accountId)
|
||||
public function show(Request $request, int $accountId)
|
||||
{
|
||||
return Account::findOrFail($accountId)->external()->firstOrFail();
|
||||
return $request->space->accounts()->findOrFail($accountId)->external()->firstOrFail();
|
||||
}
|
||||
|
||||
public function store(CreateUpdate $request, int $accountId)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\PhoneCountry;
|
||||
|
||||
class PhoneCountryController extends Controller
|
||||
{
|
||||
public function activate(string $code)
|
||||
{
|
||||
$phoneCountry = PhoneCountry::where('code', $code)->firstOrFail();
|
||||
return PhoneCountry::where('country_code', $phoneCountry->country_code)->update(['activated' => true]);
|
||||
}
|
||||
|
||||
public function deactivate(string $code)
|
||||
{
|
||||
$phoneCountry = PhoneCountry::where('code', $code)->firstOrFail();
|
||||
return PhoneCountry::where('country_code', $phoneCountry->country_code)->update(['activated' => false]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin\Space;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Space\CardDavServer;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use App\Space;
|
||||
use App\SpaceCardDavServer;
|
||||
|
||||
class CardDavServerController extends Controller
|
||||
{
|
||||
public function index(string $domain)
|
||||
{
|
||||
return Space::where('domain', $domain)->firstOrFail()->carddavServers;
|
||||
}
|
||||
|
||||
public function show(string $domain, int $carddavServerId)
|
||||
{
|
||||
return Space::where('domain', $domain)->firstOrFail()->carddavServers()->findOrFail($carddavServerId);
|
||||
}
|
||||
|
||||
public function store(CardDavServer $request, string $domain)
|
||||
{
|
||||
$space = Space::where('domain', $domain)->firstOrFail();
|
||||
|
||||
$carddavServer = new SpaceCardDavServer;
|
||||
$carddavServer->space_id = $space->id;
|
||||
$carddavServer->fill($request->validated());
|
||||
$carddavServer->enabled = $request->has('enabled') && (bool)$request->get('enabled');
|
||||
$carddavServer->use_exact_match_policy = $request->has('use_exact_match_policy') && (bool)$request->get('use_exact_match_policy');
|
||||
|
||||
return $carddavServer->save();
|
||||
}
|
||||
|
||||
public function update(CardDavServer $request, string $domain, int $carddavServerId)
|
||||
{
|
||||
$space = Space::where('domain', $domain)->firstOrFail();
|
||||
|
||||
$carddavServer = $space->carddavServers()->findOrFail($carddavServerId);
|
||||
$carddavServer->fill($request->validated());
|
||||
$carddavServer->enabled = $request->has('enabled') && (bool)$request->get('enabled');
|
||||
$carddavServer->use_exact_match_policy = $request->has('use_exact_match_policy') && (bool)$request->get('use_exact_match_policy');
|
||||
|
||||
return $carddavServer->save();
|
||||
}
|
||||
|
||||
public function destroy(string $domain, int $carddavServerId)
|
||||
{
|
||||
$space = Space::where('domain', $domain)->firstOrFail();
|
||||
|
||||
$carddavServer = $space->carddavServers()->findOrFail($carddavServerId);
|
||||
return $carddavServer->delete();
|
||||
}
|
||||
}
|
||||
|
|
@ -17,9 +17,8 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin;
|
||||
namespace App\Http\Controllers\Api\Admin\Space;
|
||||
|
||||
use App\Account;
|
||||
use App\ContactsList;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
|
@ -28,12 +27,12 @@ class ContactsListController extends Controller
|
|||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
return ContactsList::all();
|
||||
return $request->space->contactsLists;
|
||||
}
|
||||
|
||||
public function get(int $contactsListId)
|
||||
public function get(Request $request, int $contactsListId)
|
||||
{
|
||||
return ContactsList::findOrFail($contactsListId);
|
||||
return $request->space->contactsLists()->findOrFail($contactsListId);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
|
|
@ -44,6 +43,7 @@ class ContactsListController extends Controller
|
|||
]);
|
||||
|
||||
$contactsList = new ContactsList;
|
||||
$contactsList->space_id = $request->space->id;
|
||||
$contactsList->title = $request->get('title');
|
||||
$contactsList->description = $request->get('description');
|
||||
$contactsList->save();
|
||||
|
|
@ -59,6 +59,7 @@ class ContactsListController extends Controller
|
|||
]);
|
||||
|
||||
$contactsList = ContactsList::findOrFail($contactsListId);
|
||||
$contactsList->space_id = $request->space->id;
|
||||
$contactsList->title = $request->get('title');
|
||||
$contactsList->description = $request->get('description');
|
||||
$contactsList->save();
|
||||
|
|
@ -66,25 +67,25 @@ class ContactsListController extends Controller
|
|||
return $contactsList;
|
||||
}
|
||||
|
||||
public function destroy(int $contactsListId)
|
||||
public function destroy(Request $request, int $contactsListId)
|
||||
{
|
||||
return ContactsList::where('id', $contactsListId)
|
||||
return $request->space->contactsLists()->where('id', $contactsListId)
|
||||
->delete();
|
||||
}
|
||||
|
||||
public function contactAdd(int $id, int $contactId)
|
||||
public function contactAdd(Request $request, int $id, int $contactId)
|
||||
{
|
||||
$contactsList = ContactsList::findOrFail($id);
|
||||
$contactsList = $request->space->contactsLists()->findOrFail($id);
|
||||
$contactsList->contacts()->detach($contactId);
|
||||
|
||||
if (Account::findOrFail($contactId)) {
|
||||
if ($request->space->accounts()->findOrFail($contactId)) {
|
||||
return $contactsList->contacts()->attach($contactId);
|
||||
}
|
||||
}
|
||||
|
||||
public function contactRemove(int $id, int $contactId)
|
||||
public function contactRemove(Request $request, int $id, int $contactId)
|
||||
{
|
||||
$contactsList = ContactsList::findOrFail($id);
|
||||
$contactsList = $request->space->contactsLists()->findOrFail($id);
|
||||
|
||||
if (!$contactsList->contacts()->pluck('id')->contains($contactId)) {
|
||||
abort(404);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin;
|
||||
namespace App\Http\Controllers\Api\Admin\Space;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\EmailServer\CreateUpdate;
|
||||
|
|
@ -11,14 +11,14 @@ use Illuminate\Http\Request;
|
|||
|
||||
class EmailServerController extends Controller
|
||||
{
|
||||
public function show(string $host)
|
||||
public function show(string $domain)
|
||||
{
|
||||
return Space::where('host', $host)->firstOrFail()->emailServer()->firstOrFail();
|
||||
return Space::where('domain', $domain)->firstOrFail()->emailServer()->firstOrFail();
|
||||
}
|
||||
|
||||
public function store(CreateUpdate $request, string $host)
|
||||
public function store(CreateUpdate $request, string $domain)
|
||||
{
|
||||
$space = Space::where('host', $host)->firstOrFail();
|
||||
$space = Space::where('domain', $domain)->firstOrFail();
|
||||
$emailServer = $space->emailServer ?? new SpaceEmailServer;
|
||||
|
||||
$emailServer->space_id = $space->id;
|
||||
|
|
@ -35,9 +35,9 @@ class EmailServerController extends Controller
|
|||
return $emailServer;
|
||||
}
|
||||
|
||||
public function destroy(string $host)
|
||||
public function destroy(string $domain)
|
||||
{
|
||||
$space = Space::where('host', $host)->firstOrFail();
|
||||
$space = Space::where('host', $domain)->firstOrFail();
|
||||
return $space->emailServer->delete();
|
||||
}
|
||||
}
|
||||
|
|
@ -45,38 +45,37 @@ class SpaceController extends Controller
|
|||
]);
|
||||
|
||||
$space = new Space;
|
||||
$space->name = $request->get('name');
|
||||
$space->domain = $request->get('domain');
|
||||
$space->host = $request->get('host');
|
||||
$this->setRequestBoolean($request, $space, 'super');
|
||||
$this->setRequestBoolean($request, $space, 'disable_chat_feature');
|
||||
$this->setRequestBoolean($request, $space, 'disable_meetings_feature');
|
||||
$this->setRequestBoolean($request, $space, 'disable_broadcast_feature');
|
||||
$this->setRequestBoolean($request, $space, 'hide_settings');
|
||||
$this->setRequestBoolean($request, $space, 'hide_account_settings');
|
||||
$this->setRequestBoolean($request, $space, 'disable_call_recordings_feature');
|
||||
$this->setRequestBoolean($request, $space, 'only_display_sip_uri_username');
|
||||
$this->setRequestBoolean($request, $space, 'assistant_hide_create_account');
|
||||
$this->setRequestBoolean($request, $space, 'assistant_disable_qr_code');
|
||||
$this->setRequestBoolean($request, $space, 'assistant_hide_third_party_account');
|
||||
$space->max_account = $request->get('max_account', 0);
|
||||
$space->max_accounts = $request->get('max_accounts', 0);
|
||||
$space->expire_at = $request->get('expire_at');
|
||||
|
||||
$space->copyright_text = $request->get('copyright_text');
|
||||
$space->intro_registration_text = $request->get('intro_registration_text');
|
||||
$space->newsletter_registration_address = $request->get('newsletter_registration_address');
|
||||
$space->account_proxy_registrar_address = $request->get('account_proxy_registrar_address');
|
||||
$space->account_realm = $request->get('account_realm');
|
||||
$space->copyright_text = $request->get('copyright_text');
|
||||
$space->custom_provisioning_entries = $request->get('custom_provisioning_entries');
|
||||
$space->domain = $request->get('domain');
|
||||
$space->expire_at = $request->get('expire_at');
|
||||
$space->host = $request->get('host');
|
||||
$space->intro_registration_text = $request->get('intro_registration_text');
|
||||
$space->max_account = $request->get('max_account', 0);
|
||||
$space->max_accounts = $request->get('max_accounts', 0);
|
||||
$space->name = $request->get('name');
|
||||
$space->newsletter_registration_address = $request->get('newsletter_registration_address');
|
||||
$this->setRequestBoolean($request, $space, 'assistant_disable_qr_code');
|
||||
$this->setRequestBoolean($request, $space, 'assistant_hide_create_account');
|
||||
$this->setRequestBoolean($request, $space, 'assistant_hide_third_party_account');
|
||||
$this->setRequestBoolean($request, $space, 'carddav_user_credentials');
|
||||
$this->setRequestBoolean($request, $space, 'custom_provisioning_overwrite_all');
|
||||
$this->setRequestBoolean($request, $space, 'provisioning_use_linphone_provisioning_header');
|
||||
$this->setRequestBoolean($request, $space, 'custom_theme');
|
||||
$this->setRequestBoolean($request, $space, 'web_panel');
|
||||
$this->setRequestBoolean($request, $space, 'public_registration');
|
||||
$this->setRequestBoolean($request, $space, 'phone_registration');
|
||||
$this->setRequestBoolean($request, $space, 'disable_broadcast_feature');
|
||||
$this->setRequestBoolean($request, $space, 'disable_call_recordings_feature');
|
||||
$this->setRequestBoolean($request, $space, 'disable_chat_feature');
|
||||
$this->setRequestBoolean($request, $space, 'disable_meetings_feature');
|
||||
$this->setRequestBoolean($request, $space, 'hide_account_settings');
|
||||
$this->setRequestBoolean($request, $space, 'hide_settings');
|
||||
$this->setRequestBoolean($request, $space, 'intercom_features');
|
||||
|
||||
$this->setRequestBoolean($request, $space, 'only_display_sip_uri_username');
|
||||
$this->setRequestBoolean($request, $space, 'phone_registration');
|
||||
$this->setRequestBoolean($request, $space, 'provisioning_use_linphone_provisioning_header');
|
||||
$this->setRequestBoolean($request, $space, 'public_registration');
|
||||
$this->setRequestBoolean($request, $space, 'super');
|
||||
$this->setRequestBoolean($request, $space, 'web_panel');
|
||||
$space->save();
|
||||
|
||||
return $space->refresh();
|
||||
|
|
@ -90,30 +89,30 @@ class SpaceController extends Controller
|
|||
public function update(Request $request, string $domain)
|
||||
{
|
||||
$request->validate([
|
||||
'super' => 'required|boolean',
|
||||
'disable_chat_feature' => 'required|boolean',
|
||||
'disable_meetings_feature' => 'required|boolean',
|
||||
'disable_broadcast_feature' => 'required|boolean',
|
||||
'hide_settings' => 'required|boolean',
|
||||
'hide_account_settings' => 'required|boolean',
|
||||
'disable_call_recordings_feature' => 'required|boolean',
|
||||
'only_display_sip_uri_username' => 'required|boolean',
|
||||
'assistant_hide_create_account' => 'required|boolean',
|
||||
'assistant_disable_qr_code' => 'required|boolean',
|
||||
'assistant_hide_third_party_account' => 'required|boolean',
|
||||
'max_account' => 'required|integer',
|
||||
'max_accounts' => 'required|integer',
|
||||
'expire_at' => 'nullable|date|after_or_equal:today',
|
||||
'account_realm' => ['nullable', new Domain()],
|
||||
|
||||
'assistant_disable_qr_code' => 'required|boolean',
|
||||
'assistant_hide_create_account' => 'required|boolean',
|
||||
'assistant_hide_third_party_account' => 'required|boolean',
|
||||
'carddav_user_credentials' => 'required|boolean',
|
||||
'custom_provisioning_entries' => ['nullable', new Ini(Space::FORBIDDEN_KEYS)],
|
||||
'custom_provisioning_overwrite_all' => 'required|boolean',
|
||||
'provisioning_use_linphone_provisioning_header' => 'required|boolean',
|
||||
'custom_theme' => 'required|boolean',
|
||||
'web_panel' => 'required|boolean',
|
||||
'public_registration' => 'required|boolean',
|
||||
'phone_registration' => 'required|boolean',
|
||||
'disable_broadcast_feature' => 'required|boolean',
|
||||
'disable_call_recordings_feature' => 'required|boolean',
|
||||
'disable_chat_feature' => 'required|boolean',
|
||||
'disable_meetings_feature' => 'required|boolean',
|
||||
'expire_at' => 'nullable|date|after_or_equal:today',
|
||||
'hide_account_settings' => 'required|boolean',
|
||||
'hide_settings' => 'required|boolean',
|
||||
'intercom_features' => 'required|boolean',
|
||||
'max_account' => 'required|integer',
|
||||
'max_accounts' => 'required|integer',
|
||||
'only_display_sip_uri_username' => 'required|boolean',
|
||||
'phone_registration' => 'required|boolean',
|
||||
'provisioning_use_linphone_provisioning_header' => 'required|boolean',
|
||||
'public_registration' => 'required|boolean',
|
||||
'super' => 'required|boolean',
|
||||
'web_panel' => 'required|boolean',
|
||||
]);
|
||||
|
||||
$space = Space::where('domain', $domain)->firstOrFail();
|
||||
|
|
@ -132,33 +131,33 @@ class SpaceController extends Controller
|
|||
$space->name = $request->get('name');
|
||||
$space->host = $request->get('host');
|
||||
$space->super = $request->get('super');
|
||||
$space->disable_chat_feature = $request->get('disable_chat_feature');
|
||||
$space->disable_meetings_feature = $request->get('disable_meetings_feature');
|
||||
$space->disable_broadcast_feature = $request->get('disable_broadcast_feature');
|
||||
$space->hide_settings = $request->get('hide_settings');
|
||||
$space->hide_account_settings = $request->get('hide_account_settings');
|
||||
$space->disable_call_recordings_feature = $request->get('disable_call_recordings_feature');
|
||||
$space->only_display_sip_uri_username = $request->get('only_display_sip_uri_username');
|
||||
$space->assistant_hide_create_account = $request->get('assistant_hide_create_account');
|
||||
$space->assistant_disable_qr_code = $request->get('assistant_disable_qr_code');
|
||||
$space->assistant_hide_third_party_account = $request->get('assistant_hide_third_party_account');
|
||||
$space->max_account = $request->get('max_account', 0);
|
||||
$space->max_accounts = $request->get('max_accounts', 0);
|
||||
$space->expire_at = $request->get('expire_at');
|
||||
|
||||
$space->copyright_text = $request->get('copyright_text');
|
||||
$space->intro_registration_text = $request->get('intro_registration_text');
|
||||
$space->newsletter_registration_address = $request->get('newsletter_registration_address');
|
||||
$space->account_proxy_registrar_address = $request->get('account_proxy_registrar_address');
|
||||
$space->account_realm = $request->get('account_realm');
|
||||
$space->assistant_disable_qr_code = $request->get('assistant_disable_qr_code');
|
||||
$space->assistant_hide_create_account = $request->get('assistant_hide_create_account');
|
||||
$space->assistant_hide_third_party_account = $request->get('assistant_hide_third_party_account');
|
||||
$space->copyright_text = $request->get('copyright_text');
|
||||
$space->carddav_user_credentials = $request->get('carddav_user_credentials');
|
||||
$space->custom_provisioning_entries = $request->get('custom_provisioning_entries');
|
||||
$space->custom_provisioning_overwrite_all = $request->get('custom_provisioning_overwrite_all');
|
||||
$space->provisioning_use_linphone_provisioning_header = $request->get('provisioning_use_linphone_provisioning_header');
|
||||
$space->custom_theme = $request->get('custom_theme');
|
||||
$space->web_panel = $request->get('web_panel');
|
||||
$space->public_registration = $request->get('public_registration');
|
||||
$space->phone_registration = $request->get('phone_registration');
|
||||
$space->disable_broadcast_feature = $request->get('disable_broadcast_feature');
|
||||
$space->disable_call_recordings_feature = $request->get('disable_call_recordings_feature');
|
||||
$space->disable_chat_feature = $request->get('disable_chat_feature');
|
||||
$space->disable_meetings_feature = $request->get('disable_meetings_feature');
|
||||
$space->expire_at = $request->get('expire_at');
|
||||
$space->hide_account_settings = $request->get('hide_account_settings');
|
||||
$space->hide_settings = $request->get('hide_settings');
|
||||
$space->intercom_features = $request->get('intercom_features');
|
||||
$space->intro_registration_text = $request->get('intro_registration_text');
|
||||
$space->max_account = $request->get('max_account', 0);
|
||||
$space->max_accounts = $request->get('max_accounts', 0);
|
||||
$space->newsletter_registration_address = $request->get('newsletter_registration_address');
|
||||
$space->only_display_sip_uri_username = $request->get('only_display_sip_uri_username');
|
||||
$space->phone_registration = $request->get('phone_registration');
|
||||
$space->provisioning_use_linphone_provisioning_header = $request->get('provisioning_use_linphone_provisioning_header');
|
||||
$space->public_registration = $request->get('public_registration');
|
||||
$space->web_panel = $request->get('web_panel');
|
||||
|
||||
$space->save();
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
namespace App\Http\Controllers\Api\Admin;
|
||||
|
||||
use App\Account;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Rules\Vcard;
|
||||
use App\VcardStorage;
|
||||
|
|
@ -30,15 +29,15 @@ use stdClass;
|
|||
|
||||
class VcardsStorageController extends Controller
|
||||
{
|
||||
public function index(int $accountId)
|
||||
public function index(Request $request, int $accountId)
|
||||
{
|
||||
$list = Account::findOrFail($accountId)->vcardsStorage()->get()->keyBy('uuid');
|
||||
$list = $request->space->accounts()->findOrFail($accountId)->vcardsStorage()->get()->keyBy('uuid');
|
||||
return $list->isEmpty() ? new stdClass : $list;
|
||||
}
|
||||
|
||||
public function show(int $accountId, string $uuid)
|
||||
public function show(Request $request, int $accountId, string $uuid)
|
||||
{
|
||||
return Account::findOrFail($accountId)->vcardsStorage()->where('uuid', $uuid)->firstOrFail();
|
||||
return $request->space->accounts()->findOrFail($accountId)->vcardsStorage()->where('uuid', $uuid)->firstOrFail();
|
||||
}
|
||||
|
||||
public function store(Request $request, int $accountId)
|
||||
|
|
@ -49,7 +48,7 @@ class VcardsStorageController extends Controller
|
|||
|
||||
$vcardo = VObject\Reader::read($request->get('vcard'));
|
||||
|
||||
if (Account::findOrFail($accountId)->vcardsStorage()->where('uuid', $vcardo->UID)->first()) {
|
||||
if ($request->space->accounts()->findOrFail($accountId)->vcardsStorage()->where('uuid', $vcardo->UID)->first()) {
|
||||
abort(409, 'Vcard already exists');
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +58,7 @@ class VcardsStorageController extends Controller
|
|||
$vcard->vcard = preg_replace('/\r\n?/', "\n", $vcardo->serialize());
|
||||
$vcard->save();
|
||||
|
||||
return $vcard->vcard;
|
||||
return $vcard;
|
||||
}
|
||||
|
||||
public function update(Request $request, int $accountId, string $uuid)
|
||||
|
|
@ -74,16 +73,16 @@ class VcardsStorageController extends Controller
|
|||
abort(422, 'UUID should be the same');
|
||||
}
|
||||
|
||||
$vcard = Account::findOrFail($accountId)->vcardsStorage()->where('uuid', $uuid)->firstOrFail();
|
||||
$vcard = $request->space->accounts()->findOrFail($accountId)->vcardsStorage()->where('uuid', $uuid)->firstOrFail();
|
||||
$vcard->vcard = preg_replace('/\r\n?/', "\n", $vcardo->serialize());
|
||||
$vcard->save();
|
||||
|
||||
return $vcard->vcard;
|
||||
return $vcard;
|
||||
}
|
||||
|
||||
public function destroy(int $accountId, string $uuid)
|
||||
public function destroy(Request $request, int $accountId, string $uuid)
|
||||
{
|
||||
$vcard = Account::findOrFail($accountId)->vcardsStorage()->where('uuid', $uuid)->firstOrFail();
|
||||
$vcard = $request->space->accounts()->findOrFail($accountId)->vcardsStorage()->where('uuid', $uuid)->firstOrFail();
|
||||
|
||||
return $vcard->delete();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,114 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
* These middleware are run during every request to your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\App\Http\Middleware\CheckForMaintenanceMode::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
\App\Http\Middleware\Localization::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
'throttle:600,1', // move to 600 instead of 60
|
||||
'bindings',
|
||||
'validate_json',
|
||||
'localization'
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware.
|
||||
*
|
||||
* These middleware may be assigned to groups or used individually.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middlewareAliases = [
|
||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||
'auth.admin' => \App\Http\Middleware\AuthenticateAdmin::class,
|
||||
'auth.super_admin' => \App\Http\Middleware\AuthenticateSuperAdmin::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'auth.digest_or_key' => \App\Http\Middleware\AuthenticateDigestOrKey::class,
|
||||
'auth.jwt' => \App\Http\Middleware\AuthenticateJWT::class,
|
||||
'auth.check_blocked' => \App\Http\Middleware\CheckBlocked::class,
|
||||
'validate_json' => \App\Http\Middleware\ValidateJSON::class,
|
||||
'web_panel_enabled' => \App\Http\Middleware\IsWebPanelEnabled::class,
|
||||
'public_registration' => \App\Http\Middleware\IsPublicRegistration::class,
|
||||
'phone_registration' => \App\Http\Middleware\IsPhoneRegistration::class,
|
||||
'intercom_features' => \App\Http\Middleware\IsIntercomFeatures::class,
|
||||
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'cookie' => \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
'cookie.encrypt' => \App\Http\Middleware\EncryptCookies::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
||||
'space.check' => \App\Http\Middleware\SpaceCheck::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
'localization' => \App\Http\Middleware\Localization::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The priority-sorted list of middleware.
|
||||
*
|
||||
* This forces non-global middleware to always be in the given order.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middlewarePriority = [
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
\Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
\Illuminate\Auth\Middleware\Authorize::class,
|
||||
];
|
||||
}
|
||||
|
|
@ -29,7 +29,9 @@ class AuthenticateAdmin
|
|||
return redirect()->route('account.login');
|
||||
}
|
||||
|
||||
if (!$request->user()->admin) {
|
||||
if (!$request->user()->admin
|
||||
|| (!$request->user()->superAdmin && $request->user()->domain != $request->space->domain)
|
||||
) {
|
||||
return abort(403, 'Unauthorized area');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,18 +76,20 @@ class AuthenticateJWT
|
|||
}
|
||||
|
||||
$account = null;
|
||||
$identifierKey = config('services.jwt.sip_identifier');
|
||||
if ($identifierKey == '') $identifierKey = 'sip_identity';
|
||||
|
||||
if ($token->claims()->has(config('services.jwt.sip_identifier'))) {
|
||||
list($username, $domain) = parseSIP($token->claims()->get(config('services.jwt.sip_identifier')));
|
||||
if ($token->claims()->has($identifierKey)) {
|
||||
list($username, $domain) = parseSIP($token->claims()->get($identifierKey));
|
||||
|
||||
$account = Account::withoutGlobalScopes()
|
||||
->where('username', $username)
|
||||
->where('domain', $domain)
|
||||
->first();
|
||||
->where('username', $username)
|
||||
->where('domain', $domain)
|
||||
->first();
|
||||
} elseif ($token->claims()->has('email')) {
|
||||
$account = Account::withoutGlobalScopes()
|
||||
->where('email', $token->claims()->get('email'))
|
||||
->first();
|
||||
->where('email', $token->claims()->get('email'))
|
||||
->first();
|
||||
}
|
||||
|
||||
if (!$account) {
|
||||
|
|
@ -99,8 +101,7 @@ class AuthenticateJWT
|
|||
return $next($request);
|
||||
}
|
||||
|
||||
if (
|
||||
!empty(config('app.account_authentication_bearer'))
|
||||
if (!empty(config('app.account_authentication_bearer'))
|
||||
// Bypass the JWT auth if we have an API Key
|
||||
&& !$request->header('x-api-key')
|
||||
&& !$request->cookie('x-api-key')
|
||||
|
|
@ -130,7 +131,7 @@ class AuthenticateJWT
|
|||
$response = new Response();
|
||||
$response->header(
|
||||
'WWW-Authenticate',
|
||||
$bearer . 'error="' . $error . '", error_description="'. $description . '"'
|
||||
$bearer . 'error="' . $error . '", error_description="' . $description . '"'
|
||||
);
|
||||
$response->setStatusCode(401);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as Middleware;
|
||||
|
||||
class CheckForMaintenanceMode extends Middleware
|
||||
{
|
||||
/**
|
||||
* The URIs that should be reachable while maintenance mode is enabled.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||
|
||||
class EncryptCookies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
19
flexiapi/app/Http/Middleware/IsCardDavCredentialsEnabled.php
Normal file
19
flexiapi/app/Http/Middleware/IsCardDavCredentialsEnabled.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class IsCardDavCredentialsEnabled
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if ($request->space->carddav_user_credentials) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
abort(403, 'CardDav Credentials features disabled');
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ class IsIntercomFeatures
|
|||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (space()?->intercom_features) {
|
||||
if ($request->space->intercom_features) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class IsPhoneRegistration
|
|||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (space()?->phone_registration) {
|
||||
if ($request->space->phone_registration) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ class IsPublicRegistration
|
|||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (space()?->public_registration) {
|
||||
if ($request->space?->public_registration) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return abort(404, 'Public registration disabled');
|
||||
abort(404, 'Public registration disabled');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class IsWebPanelEnabled
|
|||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (!$request->expectsJson() && space()?->web_panel) {
|
||||
if (!$request->expectsJson() && $request->space->web_panel) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use App\Space;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
|
@ -12,20 +13,21 @@ class SpaceCheck
|
|||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (empty(config('app.root_host'))) {
|
||||
return abort(503, 'APP_ROOT_HOST is not configured');
|
||||
abort(503, 'APP_ROOT_HOST is not configured');
|
||||
}
|
||||
|
||||
$space = space();
|
||||
$space = Space::where('host', config('app.sip_domain') ?? request()->host())->first();
|
||||
|
||||
if ($space != null) {
|
||||
if (!str_ends_with($space->host, config('app.root_host'))) {
|
||||
return abort(503, 'The APP_ROOT_HOST configured does not match with the current root domain');
|
||||
abort(503, 'The APP_ROOT_HOST configured does not match with the current root domain');
|
||||
}
|
||||
|
||||
Config::set('app.url', '://' . $space->host);
|
||||
Config::set('app.sip_domain', $space->domain);
|
||||
$request->merge(['space' => $space]);
|
||||
|
||||
if ($request->user() && !$request->user()->superAdmin && $space?->isExpired()) {
|
||||
Config::set('app.url', '://' . $space->host);
|
||||
|
||||
if ($space->isExpired()) {
|
||||
abort($request->expectsJson() ? 403 : 490, 'The related Space has expired');
|
||||
}
|
||||
|
||||
|
|
@ -51,6 +53,6 @@ class SpaceCheck
|
|||
return $next($request);
|
||||
}
|
||||
|
||||
return abort(404, 'Host not configured');
|
||||
abort(404, 'Host not configured');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||
|
||||
class TrimStrings extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The trusted proxies for this application.
|
||||
*
|
||||
* @var array|string
|
||||
*/
|
||||
protected $proxies;
|
||||
|
||||
/**
|
||||
* The headers that should be used to detect proxies.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $headers =
|
||||
Request::HEADER_X_FORWARDED_FOR |
|
||||
Request::HEADER_X_FORWARDED_HOST |
|
||||
Request::HEADER_X_FORWARDED_PORT |
|
||||
Request::HEADER_X_FORWARDED_PROTO |
|
||||
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||
|
||||
class VerifyCsrfToken extends Middleware
|
||||
{
|
||||
/**
|
||||
* Indicates whether the XSRF-TOKEN cookie should be set on the response.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $addHttpCookie = true;
|
||||
|
||||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
|
||||
Copyright (C) 2023 Belledonne Communications SARL, All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
|
|
@ -17,21 +17,23 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Tests;
|
||||
namespace App\Http\Requests\Account;
|
||||
|
||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
abstract class TestCaseWithSpaceMiddleware extends BaseTestCase
|
||||
use App\Rules\Domain;
|
||||
use App\Rules\PasswordAlgorithm;
|
||||
|
||||
class CardDavCredentials extends FormRequest
|
||||
{
|
||||
use CreatesApplication;
|
||||
use RefreshDatabase;
|
||||
use TestUtilsTrait;
|
||||
|
||||
public function setUp(): void
|
||||
public function rules()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
config()->set('app.sip_domain', 'sip.example.com');
|
||||
return [
|
||||
'username' => 'required',
|
||||
'password' => 'required',
|
||||
'algorithm' => ['required', new PasswordAlgorithm],
|
||||
'realm' => 'required',
|
||||
];
|
||||
}
|
||||
}
|
||||
42
flexiapi/app/Http/Requests/Space/CardDavServer.php
Normal file
42
flexiapi/app/Http/Requests/Space/CardDavServer.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
/*
|
||||
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||
Copyright (C) 2023 Belledonne Communications SARL, All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Space;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
use App\EmailServer;
|
||||
use App\Rules\CommaList;
|
||||
|
||||
class CardDavServer extends FormRequest
|
||||
{
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'uri' => 'required|url',
|
||||
'min_characters' => 'integer|min:1',
|
||||
'results_limit' => 'integer|min:0',
|
||||
'timeout' => 'integer|min:1',
|
||||
'delay' => 'integer|min:100',
|
||||
'fields_for_user_input' => [new CommaList],
|
||||
'fields_for_domain' => [new CommaList],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -57,9 +57,9 @@ class StatisticsGraphFactory
|
|||
$fromQuery = StatisticsMessage::query();
|
||||
$toQuery = StatisticsMessage::query();
|
||||
|
||||
if (!Auth::user()?->admin) {
|
||||
$fromQuery->where('from_domain', space()->domain);
|
||||
$toQuery->toDomain($this->domain);
|
||||
if (!Auth::user()?->superAdmin) {
|
||||
$fromQuery->where('from_domain', $this->request->space->domain);
|
||||
$toQuery->toDomain(space()->domain);
|
||||
} elseif ($this->domain) {
|
||||
$fromQuery->where('from_domain', $this->domain);
|
||||
$toQuery->toDomain($this->domain);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ namespace App\Mail;
|
|||
use App\Space;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ namespace App\Mail;
|
|||
use App\Account;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ namespace App\Mail;
|
|||
use App\Account;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ namespace App\Mail;
|
|||
use App\Account;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
|
|
|
|||
|
|
@ -20,10 +20,8 @@
|
|||
namespace App\Mail;
|
||||
|
||||
use App\Account;
|
||||
use App\ResetPasswordEmailToken;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
|
|
|
|||
44
flexiapi/app/Mail/Voicemail.php
Normal file
44
flexiapi/app/Mail/Voicemail.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\AccountFile;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Mail\Mailables\Attachment;
|
||||
|
||||
class Voicemail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public function __construct(public AccountFile $accountFile)
|
||||
{
|
||||
}
|
||||
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: $this->accountFile->account->space->name .
|
||||
': ' .
|
||||
__('New voice message from :sipfrom', ['sipfrom' => $this->accountFile->sip_from]),
|
||||
);
|
||||
}
|
||||
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'mails.voicemail',
|
||||
);
|
||||
}
|
||||
|
||||
public function attachments(): array
|
||||
{
|
||||
return [
|
||||
Attachment::fromStorage($this->accountFile->path)
|
||||
->withMime($this->accountFile->content_type)
|
||||
];
|
||||
}
|
||||
}
|
||||
38
flexiapi/app/Rules/AudioMime.php
Normal file
38
flexiapi/app/Rules/AudioMime.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use App\AccountFile;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
|
||||
class AudioMime implements Rule
|
||||
{
|
||||
public function __construct(private AccountFile $accountFile)
|
||||
{
|
||||
}
|
||||
|
||||
public function passes($attribute, $file): bool
|
||||
{
|
||||
$mimeType = null;
|
||||
switch ($file->getMimeType()) {
|
||||
case 'audio/opus':
|
||||
$mimeType = 'audio/opus';
|
||||
break;
|
||||
|
||||
case 'audio/vnd.wave':
|
||||
case 'audio/wav':
|
||||
case 'audio/wave':
|
||||
case 'audio/x-wav':
|
||||
case 'audio/x-pn-wav':
|
||||
$mimeType = 'audio/wav';
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->accountFile->content_type == $mimeType;
|
||||
}
|
||||
|
||||
public function message()
|
||||
{
|
||||
return __('The file should have the declared mime-type');
|
||||
}
|
||||
}
|
||||
21
flexiapi/app/Rules/CommaList.php
Normal file
21
flexiapi/app/Rules/CommaList.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
use Respect\Validation\Validator;
|
||||
|
||||
class CommaList implements Rule
|
||||
{
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
preg_match_all('/[^, ]+/', $value, $matches);
|
||||
|
||||
return $value == null || (!empty($matches) && (implode(',', $matches[0]) == $value));
|
||||
}
|
||||
|
||||
public function message()
|
||||
{
|
||||
return 'The :attribute should be null or contain a list of words separated by commas without spaces';
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
namespace App\Rules;
|
||||
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
use Respect\Validation\Validator;
|
||||
use Propaganistas\LaravelPhone\PhoneNumber;
|
||||
|
||||
class IsNotPhoneNumber implements Rule
|
||||
|
|
|
|||
|
|
@ -23,11 +23,6 @@ use Illuminate\Contracts\Validation\Rule;
|
|||
|
||||
class WithoutSpaces implements Rule
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
return preg_match('/^\S*$/u', $value);
|
||||
|
|
|
|||
|
|
@ -62,10 +62,10 @@ class AccountService
|
|||
$account = new Account();
|
||||
$account->username = $request->get('username');
|
||||
$account->activated = false;
|
||||
$account->domain = space()->domain;
|
||||
$account->domain = $request->space->domain;
|
||||
$account->ip_address = $request->ip();
|
||||
$account->created_at = Carbon::now();
|
||||
$account->user_agent = space()->name;
|
||||
$account->user_agent = $request->space->name;
|
||||
$account->dtmf_protocol = $request->get('dtmf_protocol');
|
||||
|
||||
if ($request->asAdmin) {
|
||||
|
|
@ -73,7 +73,7 @@ class AccountService
|
|||
$account->display_name = $request->get('display_name');
|
||||
$account->activated = $request->has('activated') ? (bool)$request->get('activated') : false;
|
||||
$account->domain = resolveDomain($request);
|
||||
$account->user_agent = $request->header('User-Agent') ?? space()->name;
|
||||
$account->user_agent = $request->header('User-Agent') ?? $request->space->name;
|
||||
$account->admin = $request->has('admin') && (bool)$request->get('admin');
|
||||
|
||||
if (!$request->api && $request->has('role')) {
|
||||
|
|
@ -113,8 +113,8 @@ class AccountService
|
|||
);
|
||||
|
||||
if (!$request->api) {
|
||||
if (!empty(space()?->newsletter_registration_address) && $request->has('newsletter')) {
|
||||
Mail::to(space()->newsletter_registration_address)->send(new NewsletterRegistration($account));
|
||||
if (!empty($request->space?->newsletter_registration_address) && $request->has('newsletter')) {
|
||||
Mail::to($request->space->newsletter_registration_address)->send(new NewsletterRegistration($account));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,52 +21,67 @@ namespace App;
|
|||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class Space extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $with = ['emailServer'];
|
||||
protected $with = ['emailServer', 'carddavServers'];
|
||||
|
||||
public const FORBIDDEN_KEYS = [
|
||||
'disable_chat_feature',
|
||||
'disable_meetings_feature',
|
||||
'disable_broadcast_feature',
|
||||
'max_account',
|
||||
'hide_settings',
|
||||
'hide_account_settings',
|
||||
'disable_call_recordings_feature',
|
||||
'only_display_sip_uri_username',
|
||||
'assistant_hide_create_account',
|
||||
'account_proxy_registrar_address',
|
||||
'account_realm',
|
||||
'assistant_disable_qr_code',
|
||||
'assistant_hide_create_account',
|
||||
'assistant_hide_third_party_account',
|
||||
'copyright_text',
|
||||
'disable_broadcast_feature',
|
||||
'disable_call_recordings_feature',
|
||||
'disable_chat_feature',
|
||||
'disable_meetings_feature',
|
||||
'hide_account_settings',
|
||||
'hide_settings',
|
||||
'intro_registration_text',
|
||||
'max_account',
|
||||
'newsletter_registration_address',
|
||||
'account_proxy_registrar_address',
|
||||
'account_realm'
|
||||
'only_display_sip_uri_username',
|
||||
];
|
||||
|
||||
protected $hidden = ['id'];
|
||||
protected $casts = [
|
||||
'super' => 'boolean',
|
||||
'assistant_disable_qr_code' => 'boolean',
|
||||
'assistant_hide_create_account' => 'boolean',
|
||||
'assistant_hide_third_party_account' => 'boolean',
|
||||
'carddav_user_credentials' => 'boolean',
|
||||
'disable_broadcast_feature' => 'boolean',
|
||||
'disable_call_recordings_feature' => 'boolean',
|
||||
'disable_chat_feature' => 'boolean',
|
||||
'disable_meetings_feature' => 'boolean',
|
||||
'disable_broadcast_feature' => 'boolean',
|
||||
'hide_settings' => 'boolean',
|
||||
'hide_account_settings' => 'boolean',
|
||||
'disable_call_recordings_feature' => 'boolean',
|
||||
'only_display_sip_uri_username' => 'boolean',
|
||||
'assistant_hide_create_account' => 'boolean',
|
||||
'assistant_disable_qr_code' => 'boolean',
|
||||
'assistant_hide_third_party_account' => 'boolean',
|
||||
'expire_at' => 'date',
|
||||
'hide_account_settings' => 'boolean',
|
||||
'hide_settings' => 'boolean',
|
||||
'only_display_sip_uri_username' => 'boolean',
|
||||
'super' => 'boolean',
|
||||
];
|
||||
|
||||
public const HOST_REGEX = '[\w\-]+';
|
||||
public const DOMAIN_REGEX = '(?=^.{4,253}$)(^((?!-)[a-z0-9-]{1,63}(?<!-)\.)+[a-z]{2,63}$)';
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::addGlobalScope('domain', function (Builder $builder) {
|
||||
if (!Auth::hasUser()) return;
|
||||
|
||||
if (Auth::hasUser() || Auth::user()->superAdmin) {
|
||||
return;
|
||||
}
|
||||
|
||||
$builder->where('domain', Auth::user()->domain);
|
||||
});
|
||||
}
|
||||
|
||||
public function accounts()
|
||||
{
|
||||
return $this->hasMany(Account::class, 'domain', 'domain');
|
||||
|
|
@ -82,6 +97,16 @@ class Space extends Model
|
|||
return $this->hasOne(SpaceEmailServer::class);
|
||||
}
|
||||
|
||||
public function carddavServers()
|
||||
{
|
||||
return $this->hasMany(SpaceCardDavServer::class);
|
||||
}
|
||||
|
||||
public function contactsLists()
|
||||
{
|
||||
return $this->hasMany(ContactsList::class);
|
||||
}
|
||||
|
||||
public function scopeNotFull(Builder $query)
|
||||
{
|
||||
return $query->where('max_accounts', 0)
|
||||
|
|
|
|||
82
flexiapi/app/SpaceCardDavServer.php
Normal file
82
flexiapi/app/SpaceCardDavServer.php
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SpaceCardDavServer extends Model
|
||||
{
|
||||
protected $hidden = ['space_id'];
|
||||
protected $table = 'space_carddav_servers';
|
||||
protected $fillable = ['uri', 'enabled', 'min_characters', 'results_limist', 'use_exact_match_policy', 'timeout', 'delay', 'fields_for_user_input', 'fields_for_domain'];
|
||||
|
||||
protected $casts = [
|
||||
'enabled' => 'boolean',
|
||||
'use_exact_match_policy' => 'boolean',
|
||||
];
|
||||
|
||||
public function space()
|
||||
{
|
||||
return $this->belongsTo(Space::class);
|
||||
}
|
||||
|
||||
public function accounts()
|
||||
{
|
||||
return $this->belongsToMany(Account::class, 'account_carddav_credentials', 'space_carddav_server_id', 'account_id');
|
||||
}
|
||||
|
||||
public function getNameAttribute()
|
||||
{
|
||||
return __('CardDav Server') . ' ' . $this->id;
|
||||
}
|
||||
|
||||
public function getProvisioningSection($config, int $remoteContactDirectoryCounter)
|
||||
{
|
||||
$dom = $config->ownerDocument;
|
||||
|
||||
$section = $dom->createElement('section');
|
||||
$section->setAttribute('name', 'remote_contact_directory_' . $remoteContactDirectoryCounter);
|
||||
|
||||
$entry = $dom->createElement('entry', $this->enabled ? '1': '0');
|
||||
$entry->setAttribute('name', 'enabled');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$entry = $dom->createElement('entry', 'carddav');
|
||||
$entry->setAttribute('name', 'type');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$entry = $dom->createElement('entry', $this->uri);
|
||||
$entry->setAttribute('name', 'uri');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$entry = $dom->createElement('entry', $this->min_characters);
|
||||
$entry->setAttribute('name', 'min_characters');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$entry = $dom->createElement('entry', $this->results_limit);
|
||||
$entry->setAttribute('name', 'results_limit');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$entry = $dom->createElement('entry', $this->timeout);
|
||||
$entry->setAttribute('name', 'timeout');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$entry = $dom->createElement('entry', $this->delay);
|
||||
$entry->setAttribute('name', 'delay');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$entry = $dom->createElement('entry', $this->fields_for_user_input);
|
||||
$entry->setAttribute('name', 'carddav_fields_for_user_input');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$entry = $dom->createElement('entry', $this->fields_for_domain);
|
||||
$entry->setAttribute('name', 'carddav_fields_for_domain');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$entry = $dom->createElement('entry', $this->use_exact_match_policy ? '1': '0');
|
||||
$entry->setAttribute('name', 'carddav_use_exact_match_policy');
|
||||
$section->appendChild($entry);
|
||||
|
||||
$config->appendChild($section);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +1,18 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Auto Loader
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Composer provides a convenient, automatically generated class loader
|
||||
| for our application. We just need to utilize it! We'll require it
|
||||
| into the script here so that we do not have to worry about the
|
||||
| loading of any our classes "manually". Feels great to relax.
|
||||
|
|
||||
*/
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
|
||||
// Bootstrap Laravel and handle the command...
|
||||
/** @var Application $app */
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Run The Artisan Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When we run the console application, the current CLI command will be
|
||||
| executed in this console and the response sent back to a terminal
|
||||
| or another output device for the developers. Here goes nothing!
|
||||
|
|
||||
*/
|
||||
$status = $app->handleCommand(new ArgvInput);
|
||||
|
||||
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
|
||||
|
||||
$status = $kernel->handle(
|
||||
$input = new Symfony\Component\Console\Input\ArgvInput,
|
||||
new Symfony\Component\Console\Output\ConsoleOutput
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Shutdown The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Once Artisan has finished running, we will fire off the shutdown events
|
||||
| so that any final work may be done by the application before we shut
|
||||
| down the process. This is the last thing to happen to the request.
|
||||
|
|
||||
*/
|
||||
|
||||
$kernel->terminate($input, $status);
|
||||
|
||||
exit($status);
|
||||
exit($status);
|
||||
|
|
@ -1,55 +1,50 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Create The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The first thing we will do is create a new Laravel application instance
|
||||
| which serves as the "glue" for all the components of Laravel, and is
|
||||
| the IoC container for the system binding all of the various parts.
|
||||
|
|
||||
*/
|
||||
use App\Http\Middleware\Authenticate;
|
||||
use App\Http\Middleware\AuthenticateAdmin;
|
||||
use App\Http\Middleware\AuthenticateDigestOrKey;
|
||||
use App\Http\Middleware\AuthenticateJWT;
|
||||
use App\Http\Middleware\AuthenticateSuperAdmin;
|
||||
use App\Http\Middleware\CheckBlocked;
|
||||
use App\Http\Middleware\IsCardDavCredentialsEnabled;
|
||||
use App\Http\Middleware\IsIntercomFeatures;
|
||||
use App\Http\Middleware\IsPhoneRegistration;
|
||||
use App\Http\Middleware\IsPublicRegistration;
|
||||
use App\Http\Middleware\IsWebPanelEnabled;
|
||||
use App\Http\Middleware\Localization;
|
||||
use App\Http\Middleware\SpaceCheck;
|
||||
use App\Http\Middleware\ValidateJSON;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
|
||||
$app = new Illuminate\Foundation\Application(
|
||||
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
|
||||
);
|
||||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
web: __DIR__ . '/../routes/web.php',
|
||||
commands: __DIR__ . '/../routes/console.php',
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
$middleware->append(SpaceCheck::class);
|
||||
$middleware->append(Localization::class);
|
||||
$middleware->api(append: [ValidateJSON::class]);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Bind Important Interfaces
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, we need to bind some important interfaces into the container so
|
||||
| we will be able to resolve them when needed. The kernels serve the
|
||||
| incoming requests to this application from both the web and CLI.
|
||||
|
|
||||
*/
|
||||
$middleware->alias([
|
||||
'auth.admin' => AuthenticateAdmin::class,
|
||||
'auth.check_blocked' => CheckBlocked::class,
|
||||
'auth.digest_or_key' => AuthenticateDigestOrKey::class,
|
||||
'auth.jwt' => AuthenticateJWT::class,
|
||||
'auth.super_admin' => AuthenticateSuperAdmin::class,
|
||||
'auth' => Authenticate::class,
|
||||
'feature.carddav_user_credentials' => IsCardDavCredentialsEnabled::class,
|
||||
'feature.intercom' => IsIntercomFeatures::class,
|
||||
'feature.phone_registration' => IsPhoneRegistration::class,
|
||||
'feature.public_registration' => IsPublicRegistration::class,
|
||||
'feature.web_panel_enabled' => IsWebPanelEnabled::class,
|
||||
]);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions) {
|
||||
//
|
||||
})->create();
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Http\Kernel::class,
|
||||
App\Http\Kernel::class
|
||||
);
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Console\Kernel::class,
|
||||
App\Console\Kernel::class
|
||||
);
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Debug\ExceptionHandler::class,
|
||||
App\Exceptions\Handler::class
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Return The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This script returns the application instance. The instance is given to
|
||||
| the calling script so we can separate the building of the instances
|
||||
| from the actual running of the application and sending responses.
|
||||
|
|
||||
*/
|
||||
|
||||
return $app;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue