Fix FLEXIAPI-272 Add Space based email server integration

This commit is contained in:
Timothée Jaussoin 2025-04-08 09:02:35 +00:00
parent fd0fcd7045
commit 672d6291b7
28 changed files with 652 additions and 29 deletions

View file

@ -28,6 +28,7 @@ v1.7
- 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
v1.6
----

View file

@ -1,5 +1,21 @@
<?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;
use Illuminate\Database\Eloquent\Factories\HasFactory;

View file

@ -0,0 +1,60 @@
<?php
namespace App\Http\Controllers\Admin\Space;
use App\Space;
use App\SpaceEmailServer;
use App\Http\Requests\EmailServer\CreateUpdate;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class EmailServerController extends Controller
{
public function show(int $spaceId)
{
$space = Space::findOrFail($spaceId);
return view('admin.space.email_server.show', [
'space' => $space,
'emailServer' => $space->emailServer ?? new SpaceEmailServer
]);
}
public function store(CreateUpdate $request, int $spaceId)
{
$space = Space::findOrFail($spaceId);
$emailServer = $space->emailServer ?? new SpaceEmailServer;
$emailServer->space_id = $space->id;
$emailServer->host = $request->get('host');
$emailServer->port = $request->get('port');
$emailServer->username = $request->get('username');
$emailServer->password = $request->get('password');
$emailServer->from_address = $request->get('from_address') ?? null;
$emailServer->from_name = $request->get('from_name') ?? null;
$emailServer->signature = $request->get('signature') ?? null;
$emailServer->save();
return redirect()->route('admin.spaces.integration', $spaceId);
}
public function delete(int $spaceId)
{
$space = Space::findOrFail($spaceId);
return view('admin.space.email_server.delete', [
'space' => $space
]);
}
public function destroy(int $spaceId)
{
$space = Space::findOrFail($spaceId);
$space->emailServer->delete();
return redirect()->route('admin.spaces.integration', $spaceId);
}
}

View file

@ -104,6 +104,13 @@ class SpaceController extends Controller
]);
}
public function integration(Space $space)
{
return view('admin.space.integration', [
'space' => $space
]);
}
public function configurationUpdate(Request $request, Space $space)
{
$space = $this->setConfiguration($request, $space);

View file

@ -0,0 +1,43 @@
<?php
namespace App\Http\Controllers\Api\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\EmailServer\CreateUpdate;
use App\Space;
use App\SpaceEmailServer;
use Illuminate\Http\Request;
class EmailServerController extends Controller
{
public function show(string $host)
{
return Space::where('host', $host)->firstOrFail()->emailServer()->firstOrFail();
}
public function store(CreateUpdate $request, string $host)
{
$space = Space::where('host', $host)->firstOrFail();
$emailServer = $space->emailServer ?? new SpaceEmailServer;
$emailServer->space_id = $space->id;
$emailServer->host = $request->get('host');
$emailServer->port = $request->get('port');
$emailServer->username = $request->get('username');
$emailServer->password = $request->get('password');
$emailServer->from_address = $request->get('from_address') ?? null;
$emailServer->from_name = $request->get('from_name') ?? null;
$emailServer->signature = $request->get('signature') ?? null;
$emailServer->save();
return $emailServer;
}
public function destroy(string $host)
{
$space = Space::where('host', $host)->firstOrFail();
return $space->emailServer->delete();
}
}

View file

@ -90,7 +90,7 @@ class Kernel extends HttpKernel
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'space.expired' => \App\Http\Middleware\IsSpaceExpired::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,

View file

@ -7,7 +7,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use Symfony\Component\HttpFoundation\Response;
class IsSpaceExpired
class SpaceCheck
{
public function handle(Request $request, Closure $next): Response
{
@ -29,6 +29,25 @@ class IsSpaceExpired
abort(403, 'The related Space has expired');
}
// Custom email integration
if ($space->emailServer) {
$config = [
'driver' => config('mail.driver'),
'encryption' => config('mail.encryption'),
'host' => $space->emailServer->host,
'port' => $space->emailServer->port,
'from' => [
'address' => $space->emailServer->from_address,
'name' => $space->emailServer->from_name
],
'username' => $space->emailServer->username,
'password' => $space->emailServer->password,
'signature' => $space->emailServer->signature ?? config('mail.signature')
];
Config::set('mail', $config);
}
return $next($request);
}

View file

@ -0,0 +1,38 @@
<?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\EmailServer;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use App\EmailServer;
use App\Rules\Domain;
class CreateUpdate extends FormRequest
{
public function rules()
{
return [
'host' => ['required', new Domain()],
'port' => 'required|integer',
'from_address' => 'nullable|email',
];
}
}

View file

@ -1,5 +1,21 @@
<?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;
use Illuminate\Database\Eloquent\Factories\HasFactory;

View file

@ -1,5 +1,21 @@
<?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;
use Illuminate\Database\Eloquent\Factories\HasFactory;

View file

@ -1,5 +1,21 @@
<?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;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@ -11,6 +27,8 @@ class Space extends Model
{
use HasFactory;
protected $with = ['emailServer'];
public const FORBIDDEN_KEYS = [
'disable_chat_feature',
'disable_meetings_feature',
@ -60,6 +78,11 @@ class Space extends Model
return $this->accounts()->where('admin', true);
}
public function emailServer()
{
return $this->hasOne(SpaceEmailServer::class);
}
public function scopeNotFull(Builder $query)
{
return $query->where('max_accounts', 0)

View file

@ -0,0 +1,34 @@
<?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;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class SpaceEmailServer extends Model
{
use HasFactory;
protected $hidden = ['space_id'];
public function space()
{
return $this->belongsTo(Space::class);
}
}

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('space_email_servers', function (Blueprint $table) {
$table->id();
$table->string('host', 64);
$table->integer('port');
$table->string('username', 128)->nullable();
$table->string('password', 128)->nullable();
$table->string('from_address', 128)->nullable();
$table->string('from_name', 128)->nullable();
$table->string('signature', 256)->nullable();
$table->bigInteger('space_id')->unsigned();
$table->foreign('space_id')->references('id')
->on('spaces')->onDelete('cascade');
$table->unique('space_id');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('space_email_servers');
}
};

View file

@ -63,6 +63,7 @@
"Edit": "Éditer",
"Email registration": "Inscription par email",
"Email": "Email",
"Email Server": "Serveur Mail",
"Empty": "Vide",
"Enable the web interface": "Activer l'interface web",
"Enabled": "Activé",
@ -89,6 +90,7 @@
"In ini format, will complete the other settings": "Au format ini, complètera les autres paramètres",
"In lowercase letters": "En minuscules",
"Information": "Informations",
"Integration": "Intégration",
"Intercom features": "Fonctionnalités d'interphonie",
"It might actually disable this page, be careful": "Cette page pourrait être désactivée, faites attention",
"Key": "Clef",

View file

@ -35,6 +35,10 @@ p .btn {
text-align: right;
}
p .btn.oppose {
margin-right: 0;
}
.btn[disabled] {
color: var(--main-5);
border-color: var(--main-5);

View file

@ -342,6 +342,23 @@ content section {
box-sizing: border-box;
}
content:has(section.documentation) {
margin: 0;
}
content section.documentation {
max-width: calc(100% - 35rem);
margin: 0;
margin-left: 35rem;
}
@media screen and (max-width: 800px) {
content section.documentation {
max-width: 100%;
margin-left: 0;
}
}
form section {
margin: initial;
max-width: initial;
@ -787,14 +804,29 @@ select.list_toggle {
/** Specific elements */
.table-of-contents {
max-width: 40%;
float: right;
max-width: 33rem;
max-height: calc(100vh - 19rem);
overflow-y: scroll;
position: fixed;
top: 11rem;
left: 0;
padding: 2rem;
box-sizing: border-box;
background-color: var(--grey-1);
border-radius: 0 1rem 1rem 0;
}
@media screen and (max-width: 800px) {
.table-of-contents {
display: none;
}
}
.card {
background-color: var(--grey-1);
border-radius: 1rem;
padding: 1.5rem;
box-sizing: border-box;
margin-bottom: 1rem;
overflow: hidden;
}

View file

@ -1,8 +1,8 @@
@extends('layouts.main', ['welcome' => true])
@section('content')
<div>
{{-- This view is only a wrapper for the markdown page --}}
{!! $documentation !!}
</div>
<section class="documentation">
{{-- This view is only a wrapper for the markdown page --}}
{!! $documentation !!}
</section>
@endsection

View file

@ -0,0 +1,38 @@
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item">
<a href="{{ route('admin.spaces.index') }}">{{ __('Spaces') }}</a>
</li>
<li class="breadcrumb-item">
<a href="{{ route('admin.spaces.show', $space) }}">
{{ $space->name }}
</a>
</li>
<li class="breadcrumb-item">
<a href="{{ route('admin.spaces.integration', $space) }}">{{ __('Integration') }}</a>
</li>
<li class="breadcrumb-item active" aria-current="page">{{ __('Email Server') }} - {{ __('Delete' )}}</li>
@endsection
@section('content')
<header>
<h1><i class="ph">trash</i> {{ __('Delete') }}</h1>
<a href="{{ route('admin.spaces.integration', ['space' => $space]) }}" class="btn btn-secondary oppose">{{ __('Cancel') }}</a>
<input form="delete" class="btn" type="submit" value="{{ __('Delete') }}">
</header>
<form id="delete" method="POST" action="{{ route('admin.spaces.email.destroy', $space->id) }}" accept-charset="UTF-8">
@csrf
@method('delete')
<div class="large">
<p>{{ __('You are going to permanently delete the following element. Please confirm your action.') }}<br />
<b><i class="ph">envelope</i> {{ $space->emailServer->host }}</b>
</p>
<input name="account_id" type="hidden" value="{{ $space->id }}">
</div>
<div>
</div>
</form>
@endsection

View file

@ -0,0 +1,79 @@
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item">
<a href="{{ route('admin.spaces.index') }}">{{ __('Spaces') }}</a>
</li>
<li class="breadcrumb-item">
<a href="{{ route('admin.spaces.show', $space) }}">
{{ $space->name }}
</a>
</li>
<li class="breadcrumb-item">
<a href="{{ route('admin.spaces.integration', $space) }}">{{ __('Integration') }}</a>
</li>
<li class="breadcrumb-item active" aria-current="page">{{ __('Email Server') }}</li>
@endsection
@section('content')
<header>
<h1><i class="ph">envelope</i> {{ $space->name }}</h1>
</header>
<form method="POST"
action="{{ route('admin.spaces.email.store', $space->id) }}"
id="show" accept-charset="UTF-8">
@csrf
@method('post')
<div>
<input placeholder="hostname.tld" required="required" name="host" type="text"
value="@if($emailServer->id){{ $emailServer->host }}@else{{ old('host') }}@endif">
<label for="host">{{ __('Hostname') }}</label>
@include('parts.errors', ['name' => 'host'])
</div>
<div>
<input placeholder="25" name="port" type="number" min="0"
@if($emailServer->id)value="{{ $emailServer->port }}"@else value="25"@endif">
<label for="port">{{ __('Port') }}</label>
@include('parts.errors', ['name' => 'port'])
</div>
<div>
<input placeholder="username" name="username" type="text"
value="@if($emailServer->id){{ $emailServer->username }}@else{{ old('username') }}@endif">
<label for="username">{{ __('Username') }}</label>
@include('parts.errors', ['name' => 'username'])
</div>
<div>
<input placeholder="password" name="password" type="text"
value="@if($emailServer->id){{ $emailServer->password }}@else{{ old('password') }}@endif">
<label for="password">{{ __('Password') }}</label>
@include('parts.errors', ['name' => 'password'])
</div>
<div>
<input placeholder="username@domain.tld" name="from_address" type="email"
value="@if($emailServer->id){{ $emailServer->from_address }}@else{{ old('from_address') }}@endif">
<label for="from_address">{{ __('From Address') }}</label>
@include('parts.errors', ['name' => 'from_address'])
</div>
<div>
<input placeholder="John Doe" name="from_name" type="text"
value="@if($emailServer->id){{ $emailServer->from_name }}@else{{ old('from_name') }}@endif">
<label for="from_name">{{ __('From Name') }}</label>
@include('parts.errors', ['name' => 'from_name'])
</div>
<div>
<input placeholder="The Company Team" name="signature" type="text"
value="@if($emailServer->id){{ $emailServer->signature }}@else{{ old('signature') }}@endif">
<label for="signature">{{ __('Signature') }}</label>
@include('parts.errors', ['name' => 'signature'])
</div>
</form>
<br />
<input form="show" class="btn" type="submit" value="@if($emailServer->id){{ __('Update') }}@else{{ __('Create') }}@endif">
@endsection

View file

@ -0,0 +1,39 @@
@extends('layouts.main')
@section('breadcrumb')
<li class="breadcrumb-item">
<a href="{{ route('admin.spaces.index') }}">{{ __('Spaces') }}</a>
</li>
<li class="breadcrumb-item">
<a href="{{ route('admin.spaces.show', $space->id) }}">
{{ $space->name }}
</a>
</li>
<li class="breadcrumb-item active" aria-current="page">{{ __('Integration') }}</li>
@endsection
@section('content')
<header>
<h1><i class="ph">globe-hemisphere-west</i> {{ $space->name }}</h1>
</header>
@include('admin.space.tabs')
<div class="grid third">
<div class="card">
<span class="icon"><i class="ph">envelope</i></span>
<h3>{{ __('Email Server') }}</h3>
<p>
@if ($space->emailServer)
{{ $space->emailServer->host}}<br /><br />
@endif
@if ($space->emailServer)
<a class="btn oppose" href="{{ route('admin.spaces.email.show', $space) }}">{{ __('Edit') }}</a>
<a class="btn oppose btn-tertiary" href="{{ route('admin.spaces.email.delete', $space) }}">{{ __('Delete') }}</a>
@else
<a class="btn oppose btn-secondary" href="{{ route('admin.spaces.email.show', $space) }}">{{ __('Configure') }}</a>
@endif
</p>
</div>
</div>
@endsection

View file

@ -5,8 +5,10 @@
$items[route('admin.spaces.show', $space->id)] = __('Information');
$items[route('admin.spaces.administration', $space->id)] = __('Administration');
$items[route('admin.spaces.edit', $space->id)] = __('App Configuration');
$items[route('admin.spaces.integration', $space->id)] = __('Integration');
} else if (auth()->user()->admin) {
$items[route('admin.spaces.me')] = __('Information');
$items[route('admin.spaces.integration', $space->id)] = __('Integration');
}
$items[route('admin.spaces.configuration', $space->id)] = __('Configuration');

View file

@ -1,8 +1,8 @@
@extends('layouts.main', ['welcome' => true])
@section('content')
<div>
{{-- This view is only a wrapper for the markdown page --}}
{!! $documentation !!}
</div>
<section class="documentation">
{{-- This view is only a wrapper for the markdown page --}}
{!! $documentation !!}
</section>
@endsection

View file

@ -12,8 +12,7 @@ A `content-type` and `accept` HTTP headers are REQUIRED to use the API properly
> accept: application/json
```
<div class="card bg-light mb-3">
<div class="card-body">
<div class="card">
Restricted endpoints are protected using a DIGEST authentication or an API Key mechanisms.
@ -111,7 +110,6 @@ A `from` (consisting of the user SIP address, prefixed with `sip:`) header is re
You can find more documentation on the related [IETF RFC-7616](https://tools.ietf.org/html/rfc7616).
</div>
</div>
# Endpoints
@ -226,6 +224,31 @@ JSON parameters:
Delete a domain, **be careful, all the related accounts will also be destroyed**.
### `GET /spaces/{domain}/email`
<span class="badge badge-error">Super Admin</span>
Get a space email server configuration
### `POST /spaces/{domain}/email`
<span class="badge badge-error">Super Admin</span>
Update an existing a space email server configuration.
JSON parameters:
* `host` **required**, the email server hostname
* `port` **required**, integer, the port
* `username`, the username
* `password`, the password
* `from_address`, email address, the sender email address
* `from_name`, the sender name
* `signature`, a text that will end every emails sent
### `DELETE /spaces/{domain}/email`
<span class="badge badge-error">Super Admin</span>
Delete the a space email server configuration.
## Account Creation Tokens
An `account_creation_token` is a unique token that allow the creation or the validation of a unique account.
@ -860,7 +883,7 @@ JSON parameters:
## Push Notifications
### `POST /push_notification`
<span class="badge badge-warning">User</span>
<span class="badge badge-info">User</span>
Send a push notification using the Flexisip Pusher.

View file

@ -1,8 +1,8 @@
@extends('layouts.main', ['welcome' => true])
@section('content')
<div>
{{-- This view is only a wrapper for the markdown page --}}
{!! $documentation !!}
</div>
<section class="documentation">
{{-- This view is only a wrapper for the markdown page --}}
{!! $documentation !!}
</section>
@endsection

View file

@ -26,6 +26,7 @@ use App\Http\Controllers\Api\Admin\AccountTypeController;
use App\Http\Controllers\Api\Admin\ContactsListController;
use App\Http\Controllers\Api\Admin\ExternalAccountController;
use App\Http\Controllers\Api\Admin\SpaceController;
use App\Http\Controllers\Api\Admin\EmailServerController;
use App\Http\Controllers\Api\Admin\VcardsStorageController as AdminVcardsStorageController;
use App\Http\Controllers\Api\StatisticsMessageController;
use App\Http\Controllers\Api\StatisticsCallController;
@ -62,7 +63,7 @@ Route::get('accounts/me/api_key/{auth_token}', 'Api\Account\ApiKeyController@gen
Route::get('phone_countries', 'Api\PhoneCountryController@index');
Route::group(['middleware' => ['auth.jwt', 'auth.digest_or_key', 'auth.check_blocked', 'space.expired']], function () {
Route::group(['middleware' => ['auth.jwt', 'auth.digest_or_key', 'auth.check_blocked', 'space.check']], function () {
Route::get('accounts/auth_token/{auth_token}/attach', 'Api\Account\AuthTokenController@attach');
Route::post('account_creation_tokens/consume', 'Api\Account\CreationTokenController@consume');
@ -108,6 +109,12 @@ Route::group(['middleware' => ['auth.jwt', 'auth.digest_or_key', 'auth.check_blo
Route::put('{domain}', 'update');
Route::delete('{domain}', 'destroy');
});
Route::prefix('spaces/{domain}/email')->controller(EmailServerController::class)->group(function () {
Route::get('/', 'show');
Route::post('/', 'store');
Route::delete('/', 'destroy');
});
});
// Account creation token

View file

@ -41,15 +41,16 @@ use App\Http\Controllers\Admin\ContactsListContactController;
use App\Http\Controllers\Admin\ExternalAccountController;
use App\Http\Controllers\Admin\PhoneCountryController;
use App\Http\Controllers\Admin\ResetPasswordEmailController;
use App\Http\Controllers\Admin\SpaceController;
use App\Http\Controllers\Admin\StatisticsController;
use App\Http\Controllers\Admin\SpaceController;
use App\Http\Controllers\Admin\Space\EmailServerController;
use Illuminate\Support\Facades\Route;
Route::redirect('/', 'login')->name('account.home');
Route::get('documentation', 'Account\AccountController@documentation')->name('account.documentation');
Route::get('about', 'AboutController@about')->name('about');
Route::middleware(['web_panel_enabled', 'space.expired'])->group(function () {
Route::middleware(['web_panel_enabled', 'space.check'])->group(function () {
Route::get('login', 'Account\AuthenticateController@login')->name('account.login');
Route::post('authenticate', 'Account\AuthenticateController@authenticate')->name('account.authenticate');
Route::get('authenticate/qrcode/{token?}', 'Account\AuthenticateController@loginAuthToken')->name('account.authenticate.auth_token');
@ -87,7 +88,7 @@ Route::name('provisioning.')->prefix('provisioning')->controller(ProvisioningCon
Route::get('/', 'show')->name('show');
});
Route::middleware(['web_panel_enabled', 'space.expired'])->group(function () {
Route::middleware(['web_panel_enabled', 'space.check'])->group(function () {
Route::middleware(['public_registration'])->group(function () {
Route::redirect('register', 'register/email')->name('account.register');
@ -156,9 +157,19 @@ Route::middleware(['web_panel_enabled', 'space.expired'])->group(function () {
Route::get('auth_tokens/auth/{token}', 'Account\AuthTokenController@auth')->name('auth_tokens.auth');
Route::name('admin.')->prefix('admin')->middleware(['auth.admin', 'auth.check_blocked'])->group(function () {
Route::get('space', 'Admin\SpaceController@me')->name('spaces.me');
Route::get('spaces/{space}/configuration', 'Admin\SpaceController@configuration')->name('spaces.configuration');
Route::put('spaces/{space}/configuration', 'Admin\SpaceController@configurationUpdate')->name('spaces.configuration.update');
Route::name('spaces.')->prefix('spaces')->group(function () {
Route::get('me', 'Admin\SpaceController@me')->name('me');
Route::get('{space}/configuration', 'Admin\SpaceController@configuration')->name('configuration');
Route::put('{space}/configuration', 'Admin\SpaceController@configurationUpdate')->name('configuration.update');
Route::get('{space}/integration', 'Admin\SpaceController@integration')->name('integration');
Route::name('email.')->prefix('{space}/email')->controller(EmailServerController::class)->group(function () {
Route::get('/', 'show')->name('show');
Route::post('/', 'store')->name('store');
Route::get('delete', 'delete')->name('delete');
Route::delete('/', 'destroy')->name('destroy');
});
});
Route::middleware(['auth.super_admin'])->group(function () {
Route::resource('spaces', SpaceController::class);

View file

@ -0,0 +1,77 @@
<?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 Tests\Feature;
use App\Account;
use App\Space;
use Carbon\Carbon;
use Tests\TestCase;
class ApiSpaceEmailServerTest extends TestCase
{
protected $method = 'POST';
protected $route = '/api/spaces';
public function testEmailServer()
{
$admin = Account::factory()->superAdmin()->create();
$admin->generateApiKey();
$emailHost = 'email.domain';
$route = $this->route . '/' . $admin->space->host . '/email';
$this->keyAuthenticated($admin)
->json($this->method, $route, [
'host' => $emailHost,
'port' => 22
])
->assertStatus(201);
$this->keyAuthenticated($admin)
->json('GET', $route)
->assertJsonFragment([
'host' => $emailHost,
'port' => 22
])
->assertStatus(200);
$this->keyAuthenticated($admin)
->json($this->method, $route, [
'host' => $emailHost,
'port' => 23
])
->assertStatus(200);
$this->keyAuthenticated($admin)
->json('GET', $route)
->assertJsonFragment([
'port' => 23
])
->assertStatus(200);
$this->keyAuthenticated($admin)
->json('DELETE', $route)
->assertStatus(200);
$this->keyAuthenticated($admin)
->json('GET', $route)
->assertStatus(404);
}
}

View file

@ -20,7 +20,7 @@
namespace Tests;
use App\PhoneCountry;
use App\Http\Middleware\IsSpaceExpired;
use App\Http\Middleware\SpaceCheck;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
@ -37,7 +37,7 @@ abstract class TestCase extends BaseTestCase
{
parent::setUp();
$this->withoutMiddleware([IsSpaceExpired::class]);
$this->withoutMiddleware([SpaceCheck::class]);
config()->set('app.sip_domain', 'sip.example.com');