Add a create and edit feature for accounts in the Admin panel

This commit is contained in:
Timothée Jaussoin 2021-07-29 08:48:11 +00:00
parent 54ada09169
commit 09e44f67db
15 changed files with 345 additions and 464 deletions

View file

@ -136,6 +136,11 @@ class Account extends Authenticatable
return null;
}
public function getSha256PasswordAttribute()
{
return $this->passwords()->where('algorithm', 'SHA-256')->exists();
}
public function activationExpired(): bool
{
return ($this->activationExpiration && $this->activationExpiration->isExpired());

View file

@ -24,7 +24,7 @@ use Illuminate\Support\Str;
use App\Account;
use App\DigestNonce;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Environment;
use League\CommonMark\Extension\HeadingPermalink\HeadingPermalinkExtension;
use League\CommonMark\Extension\TableOfContents\TableOfContentsExtension;
use League\CommonMark\MarkdownConverter;

View file

@ -21,9 +21,15 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Carbon\Carbon;
use App\Account;
use App\Admin;
use App\Alias;
use App\Http\Requests\CreateAccountRequest;
use App\Http\Requests\UpdateAccountRequest;
use App\Http\Controllers\Account\AuthenticateController as WebAuthenticateController;
class AccountController extends Controller
{
@ -41,40 +47,88 @@ class AccountController extends Controller
]);
}
public function show(Account $account)
{
return view('admin.account.show', [
'account' => $account
]);
}
public function create(Request $request)
{
return view('admin.account.create_edit', [
'account' => new Account
]);
}
public function store(CreateAccountRequest $request)
{
$account = new Account;
$account->username = $request->get('username');
$account->email = $request->get('email');
$account->domain = config('app.sip_domain');
$account->ip_address = $request->ip();
$account->creation_time = Carbon::now();
$account->user_agent = config('app.name');
$account->save();
$this->fillPassword($request, $account);
$this->fillPhone($request, $account);
return redirect()->route('admin.account.show', $account->id);
}
public function edit(Account $account)
{
return view('admin.account.create_edit', [
'account' => $account
]);
}
public function update(UpdateAccountRequest $request, $id)
{
$account = Account::findOrFail($id);
$account->username = $request->get('username');
$account->email = $request->get('email');
$account->save();
$this->fillPassword($request, $account);
$this->fillPhone($request, $account);
return redirect()->route('admin.account.show', $id);
}
public function search(Request $request)
{
return redirect()->route('admin.account.index', $request->get('search'));
}
public function show(Request $request, $id)
public function activate(Account $account)
{
return view('admin.account.show', [
'account' => Account::findOrFail($id)
]);
}
public function activate(Request $request, $id)
{
$account = Account::findOrFail($id);
$account->activated = true;
$account->save();
return redirect()->back();
}
public function deactivate(Request $request, $id)
public function deactivate(Account $account)
{
$account = Account::findOrFail($id);
$account->activated = false;
$account->save();
return redirect()->back();
}
public function admin(Request $request, $id)
public function provision(Account $account)
{
$account = Account::findOrFail($id);
$account->confirmation_key = Str::random(WebAuthenticateController::$emailCodeSize);
$account->save();
return redirect()->back();
}
public function admin(Account $account)
{
$admin = new Admin;
$admin->account_id = $account->id;
$admin->save();
@ -94,10 +148,10 @@ class AccountController extends Controller
return redirect()->back();
}
public function delete(Request $request, $id)
public function delete(Account $account)
{
return view('admin.account.delete', [
'account' => Account::findOrFail($id)
'account' => $account
]);
}
@ -118,4 +172,25 @@ class AccountController extends Controller
return redirect()->back();
}
private function fillPassword(Request $request, Account $account)
{
if ($request->filled('password')) {
$algorithm = $request->has('password_sha256') ? 'SHA-256' : 'MD5';
$account->updatePassword($request->get('password'), $algorithm);
}
}
private function fillPhone(Request $request, Account $account)
{
if ($request->filled('phone')) {
$account->alias()->delete();
$alias = new Alias;
$alias->alias = $request->get('phone');
$alias->domain = config('app.sip_domain');
$alias->account_id = $account->id;
$alias->save();
}
}
}

View file

@ -74,14 +74,13 @@ class AccountController extends Controller
$request->validate([
'username' => [
'required',
Rule::unique('accounts', 'username')->where(function ($query) use ($request) {
Rule::unique('accounts', 'username')->where(function ($query) {
$query->where('domain', config('app.sip_domain'));
}),
'filled',
],
'algorithm' => 'required|in:SHA-256,MD5',
'password' => 'required|filled',
'domain' => 'min:3',
'email' => 'email',
'admin' => 'boolean|nullable',
'activated' => 'boolean|nullable',
@ -102,9 +101,7 @@ class AccountController extends Controller
$account->activated = $request->has('activated')
? (bool)$request->get('activated')
: false;
$account->domain = $request->has('domain')
? $request->get('domain')
: config('app.sip_domain');
$account->domain = config('app.sip_domain');
$account->ip_address = $request->ip();
$account->creation_time = Carbon::now();
$account->user_agent = config('app.name');

View file

@ -8,7 +8,7 @@
],
"license": "MIT",
"require": {
"php": "^7.3",
"php": ">=7.3.0",
"anhskohbo/no-captcha": "^3.3",
"endroid/qr-code": "^4.1",
"fideloper/proxy": "^4.4",
@ -27,6 +27,9 @@
"phpunit/phpunit": "^9.0"
},
"config": {
"platform": {
"php": "7.3"
},
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true

486
flexiapi/composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "3587c34f2b45c6c30d140797a60d50b4",
"content-hash": "00ccabe18c0107ac493aabb5e82aadba",
"packages": [
{
"name": "anhskohbo/no-captcha",
@ -226,81 +226,6 @@
},
"time": "2020-10-02T16:03:48+00:00"
},
{
"name": "dflydev/dot-access-data",
"version": "v3.0.0",
"source": {
"type": "git",
"url": "https://github.com/dflydev/dflydev-dot-access-data.git",
"reference": "e04ff030d24a33edc2421bef305e32919dd78fc3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/e04ff030d24a33edc2421bef305e32919dd78fc3",
"reference": "e04ff030d24a33edc2421bef305e32919dd78fc3",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "^0.12.42",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.3",
"scrutinizer/ocular": "1.6.0",
"squizlabs/php_codesniffer": "^3.5",
"vimeo/psalm": "^3.14"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Dflydev\\DotAccessData\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Dragonfly Development Inc.",
"email": "info@dflydev.com",
"homepage": "http://dflydev.com"
},
{
"name": "Beau Simensen",
"email": "beau@dflydev.com",
"homepage": "http://beausimensen.com"
},
{
"name": "Carlos Frutos",
"email": "carlos@kiwing.it",
"homepage": "https://github.com/cfrutos"
},
{
"name": "Colin O'Dell",
"email": "colinodell@gmail.com",
"homepage": "https://www.colinodell.com"
}
],
"description": "Given a deep data structure, access data by dot notation.",
"homepage": "https://github.com/dflydev/dflydev-dot-access-data",
"keywords": [
"access",
"data",
"dot",
"notation"
],
"support": {
"issues": "https://github.com/dflydev/dflydev-dot-access-data/issues",
"source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.0"
},
"time": "2021-01-01T22:08:42+00:00"
},
{
"name": "doctrine/inflector",
"version": "2.0.3",
@ -1054,16 +979,16 @@
},
{
"name": "laravel/framework",
"version": "v8.51.0",
"version": "v8.52.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "208d9c0043b4c192a9bb9b15782cc4ec37f28bb0"
"reference": "8fe9877d52e25f8aed36c51734e5a8510be967e6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/208d9c0043b4c192a9bb9b15782cc4ec37f28bb0",
"reference": "208d9c0043b4c192a9bb9b15782cc4ec37f28bb0",
"url": "https://api.github.com/repos/laravel/framework/zipball/8fe9877d52e25f8aed36c51734e5a8510be967e6",
"reference": "8fe9877d52e25f8aed36c51734e5a8510be967e6",
"shasum": ""
},
"require": {
@ -1218,7 +1143,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2021-07-20T14:38:36+00:00"
"time": "2021-07-27T13:03:29+00:00"
},
{
"name": "laravel/tinker",
@ -1362,51 +1287,42 @@
},
{
"name": "league/commonmark",
"version": "2.0.0",
"version": "1.6.6",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
"reference": "167142baf9a6b946f99ad9325b06028606f8238e"
"reference": "c4228d11e30d7493c6836d20872f9582d8ba6dcf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/167142baf9a6b946f99ad9325b06028606f8238e",
"reference": "167142baf9a6b946f99ad9325b06028606f8238e",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c4228d11e30d7493c6836d20872f9582d8ba6dcf",
"reference": "c4228d11e30d7493c6836d20872f9582d8ba6dcf",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"league/config": "^1.1",
"php": "^7.4 || ^8.0",
"psr/event-dispatcher": "^1.0",
"symfony/polyfill-php80": "^1.15"
"php": "^7.1 || ^8.0"
},
"conflict": {
"scrutinizer/ocular": "1.7.*"
},
"require-dev": {
"cebe/markdown": "^1.0",
"commonmark/cmark": "0.30.0",
"commonmark/commonmark.js": "0.30.0",
"composer/package-versions-deprecated": "^1.8",
"erusev/parsedown": "^1.0",
"cebe/markdown": "~1.0",
"commonmark/commonmark.js": "0.29.2",
"erusev/parsedown": "~1.0",
"ext-json": "*",
"github/gfm": "0.29.0",
"michelf/php-markdown": "^1.4",
"phpstan/phpstan": "^0.12.88",
"phpunit/phpunit": "^9.5.5",
"scrutinizer/ocular": "^1.8.1",
"symfony/finder": "^5.3",
"symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0",
"unleashedtech/php-coding-standard": "^3.1",
"vimeo/psalm": "^4.7.3"
},
"suggest": {
"symfony/yaml": "v2.3+ required if using the Front Matter extension"
"michelf/php-markdown": "~1.4",
"mikehaertl/php-shellcommand": "^1.4",
"phpstan/phpstan": "^0.12.90",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.2",
"scrutinizer/ocular": "^1.5",
"symfony/finder": "^4.2"
},
"bin": [
"bin/commonmark"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.1-dev"
}
},
"autoload": {
"psr-4": {
"League\\CommonMark\\": "src"
@ -1424,7 +1340,7 @@
"role": "Lead Developer"
}
],
"description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)",
"description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and Github-Flavored Markdown (GFM)",
"homepage": "https://commonmark.thephpleague.com",
"keywords": [
"commonmark",
@ -1438,7 +1354,6 @@
],
"support": {
"docs": "https://commonmark.thephpleague.com/",
"forum": "https://github.com/thephpleague/commonmark/discussions",
"issues": "https://github.com/thephpleague/commonmark/issues",
"rss": "https://github.com/thephpleague/commonmark/releases.atom",
"source": "https://github.com/thephpleague/commonmark"
@ -1469,89 +1384,7 @@
"type": "tidelift"
}
],
"time": "2021-07-24T20:12:58+00:00"
},
{
"name": "league/config",
"version": "v1.1.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/config.git",
"reference": "20d42d88f12a76ff862e17af4f14a5a4bbfd0925"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/config/zipball/20d42d88f12a76ff862e17af4f14a5a4bbfd0925",
"reference": "20d42d88f12a76ff862e17af4f14a5a4bbfd0925",
"shasum": ""
},
"require": {
"dflydev/dot-access-data": "^3.0",
"nette/schema": "^1.2",
"php": "^7.4 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "^0.12.90",
"phpunit/phpunit": "^9.5.5",
"scrutinizer/ocular": "^1.8.1",
"unleashedtech/php-coding-standard": "^3.1",
"vimeo/psalm": "^4.7.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.2-dev"
}
},
"autoload": {
"psr-4": {
"League\\Config\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Colin O'Dell",
"email": "colinodell@gmail.com",
"homepage": "https://www.colinodell.com",
"role": "Lead Developer"
}
],
"description": "Define configuration arrays with strict schemas and access values with dot notation",
"homepage": "https://config.thephpleague.com",
"keywords": [
"array",
"config",
"configuration",
"dot",
"dot-access",
"nested",
"schema"
],
"support": {
"docs": "https://config.thephpleague.com/",
"issues": "https://github.com/thephpleague/config/issues",
"rss": "https://github.com/thephpleague/config/releases.atom",
"source": "https://github.com/thephpleague/config"
},
"funding": [
{
"url": "https://www.colinodell.com/sponsor",
"type": "custom"
},
{
"url": "https://www.paypal.me/colinpodell/10.00",
"type": "custom"
},
{
"url": "https://github.com/colinodell",
"type": "github"
}
],
"time": "2021-06-19T15:52:37+00:00"
"time": "2021-07-17T17:13:23+00:00"
},
{
"name": "league/flysystem",
@ -1801,22 +1634,23 @@
},
{
"name": "nesbot/carbon",
"version": "2.50.0",
"version": "2.51.1",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
"reference": "f47f17d17602b2243414a44ad53d9f8b9ada5fdb"
"reference": "8619c299d1e0d4b344e1f98ca07a1ce2cfbf1922"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/f47f17d17602b2243414a44ad53d9f8b9ada5fdb",
"reference": "f47f17d17602b2243414a44ad53d9f8b9ada5fdb",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/8619c299d1e0d4b344e1f98ca07a1ce2cfbf1922",
"reference": "8619c299d1e0d4b344e1f98ca07a1ce2cfbf1922",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^7.1.8 || ^8.0",
"symfony/polyfill-mbstring": "^1.0",
"symfony/polyfill-php80": "^1.16",
"symfony/translation": "^3.4 || ^4.0 || ^5.0"
},
"require-dev": {
@ -1835,8 +1669,8 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.x-dev",
"dev-3.x": "3.x-dev"
"dev-3.x": "3.x-dev",
"dev-master": "2.x-dev"
},
"laravel": {
"providers": [
@ -1890,154 +1724,7 @@
"type": "tidelift"
}
],
"time": "2021-06-28T22:38:45+00:00"
},
{
"name": "nette/schema",
"version": "v1.2.1",
"source": {
"type": "git",
"url": "https://github.com/nette/schema.git",
"reference": "f5ed39fc96358f922cedfd1e516f0dadf5d2be0d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/schema/zipball/f5ed39fc96358f922cedfd1e516f0dadf5d2be0d",
"reference": "f5ed39fc96358f922cedfd1e516f0dadf5d2be0d",
"shasum": ""
},
"require": {
"nette/utils": "^3.1.4 || ^4.0",
"php": ">=7.1 <8.1"
},
"require-dev": {
"nette/tester": "^2.3 || ^2.4",
"phpstan/phpstan-nette": "^0.12",
"tracy/tracy": "^2.7"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause",
"GPL-2.0-only",
"GPL-3.0-only"
],
"authors": [
{
"name": "David Grudl",
"homepage": "https://davidgrudl.com"
},
{
"name": "Nette Community",
"homepage": "https://nette.org/contributors"
}
],
"description": "📐 Nette Schema: validating data structures against a given Schema.",
"homepage": "https://nette.org",
"keywords": [
"config",
"nette"
],
"support": {
"issues": "https://github.com/nette/schema/issues",
"source": "https://github.com/nette/schema/tree/v1.2.1"
},
"time": "2021-03-04T17:51:11+00:00"
},
{
"name": "nette/utils",
"version": "v3.2.2",
"source": {
"type": "git",
"url": "https://github.com/nette/utils.git",
"reference": "967cfc4f9a1acd5f1058d76715a424c53343c20c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/utils/zipball/967cfc4f9a1acd5f1058d76715a424c53343c20c",
"reference": "967cfc4f9a1acd5f1058d76715a424c53343c20c",
"shasum": ""
},
"require": {
"php": ">=7.2 <8.1"
},
"conflict": {
"nette/di": "<3.0.6"
},
"require-dev": {
"nette/tester": "~2.0",
"phpstan/phpstan": "^0.12",
"tracy/tracy": "^2.3"
},
"suggest": {
"ext-gd": "to use Image",
"ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
"ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
"ext-json": "to use Nette\\Utils\\Json",
"ext-mbstring": "to use Strings::lower() etc...",
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()",
"ext-xml": "to use Strings::length() etc. when mbstring is not available"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause",
"GPL-2.0-only",
"GPL-3.0-only"
],
"authors": [
{
"name": "David Grudl",
"homepage": "https://davidgrudl.com"
},
{
"name": "Nette Community",
"homepage": "https://nette.org/contributors"
}
],
"description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
"homepage": "https://nette.org",
"keywords": [
"array",
"core",
"datetime",
"images",
"json",
"nette",
"paginator",
"password",
"slugify",
"string",
"unicode",
"utf-8",
"utility",
"validation"
],
"support": {
"issues": "https://github.com/nette/utils/issues",
"source": "https://github.com/nette/utils/tree/v3.2.2"
},
"time": "2021-03-03T22:53:25+00:00"
"time": "2021-07-28T13:16:28+00:00"
},
{
"name": "nikic/php-parser",
@ -2947,16 +2634,16 @@
},
{
"name": "symfony/console",
"version": "v5.3.4",
"version": "v5.3.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "ebd610dacd40d75b6a12bf64b5ccd494fc7d6ab1"
"reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/ebd610dacd40d75b6a12bf64b5ccd494fc7d6ab1",
"reference": "ebd610dacd40d75b6a12bf64b5ccd494fc7d6ab1",
"url": "https://api.github.com/repos/symfony/console/zipball/51b71afd6d2dc8f5063199357b9880cea8d8bfe2",
"reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2",
"shasum": ""
},
"require": {
@ -3026,7 +2713,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v5.3.4"
"source": "https://github.com/symfony/console/tree/v5.3.6"
},
"funding": [
{
@ -3042,7 +2729,7 @@
"type": "tidelift"
}
],
"time": "2021-07-26T16:33:26+00:00"
"time": "2021-07-27T19:10:22+00:00"
},
{
"name": "symfony/css-selector",
@ -3551,16 +3238,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v5.3.4",
"version": "v5.3.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "d6602aca7d3e11f401a0b24f43b611c530abfad3"
"reference": "a8388f7b7054a7401997008ce9cd8c6b0ab7ac75"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/d6602aca7d3e11f401a0b24f43b611c530abfad3",
"reference": "d6602aca7d3e11f401a0b24f43b611c530abfad3",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/a8388f7b7054a7401997008ce9cd8c6b0ab7ac75",
"reference": "a8388f7b7054a7401997008ce9cd8c6b0ab7ac75",
"shasum": ""
},
"require": {
@ -3604,7 +3291,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v5.3.4"
"source": "https://github.com/symfony/http-foundation/tree/v5.3.6"
},
"funding": [
{
@ -3620,20 +3307,20 @@
"type": "tidelift"
}
],
"time": "2021-07-23T15:55:36+00:00"
"time": "2021-07-27T17:08:17+00:00"
},
{
"name": "symfony/http-kernel",
"version": "v5.3.5",
"version": "v5.3.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "9eb3ee3cb6c69e12ba5770665b89fe82f0b99033"
"reference": "60030f209018356b3b553b9dbd84ad2071c1b7e0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/9eb3ee3cb6c69e12ba5770665b89fe82f0b99033",
"reference": "9eb3ee3cb6c69e12ba5770665b89fe82f0b99033",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/60030f209018356b3b553b9dbd84ad2071c1b7e0",
"reference": "60030f209018356b3b553b9dbd84ad2071c1b7e0",
"shasum": ""
},
"require": {
@ -3716,7 +3403,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-kernel/tree/v5.3.5"
"source": "https://github.com/symfony/http-kernel/tree/v5.3.6"
},
"funding": [
{
@ -3732,7 +3419,7 @@
"type": "tidelift"
}
],
"time": "2021-07-27T04:39:22+00:00"
"time": "2021-07-29T07:06:27+00:00"
},
{
"name": "symfony/mime",
@ -3978,16 +3665,16 @@
},
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.23.0",
"version": "v1.23.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab"
"reference": "16880ba9c5ebe3642d1995ab866db29270b36535"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/24b72c6baa32c746a4d0840147c9715e42bb68ab",
"reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/16880ba9c5ebe3642d1995ab866db29270b36535",
"reference": "16880ba9c5ebe3642d1995ab866db29270b36535",
"shasum": ""
},
"require": {
@ -4039,7 +3726,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.0"
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.1"
},
"funding": [
{
@ -4055,7 +3742,7 @@
"type": "tidelift"
}
],
"time": "2021-05-27T09:17:38+00:00"
"time": "2021-05-27T12:26:48+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
@ -4230,16 +3917,16 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.23.0",
"version": "v1.23.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "2df51500adbaebdc4c38dea4c89a2e131c45c8a1"
"reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2df51500adbaebdc4c38dea4c89a2e131c45c8a1",
"reference": "2df51500adbaebdc4c38dea4c89a2e131c45c8a1",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6",
"reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6",
"shasum": ""
},
"require": {
@ -4290,7 +3977,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1"
},
"funding": [
{
@ -4306,7 +3993,7 @@
"type": "tidelift"
}
],
"time": "2021-05-27T09:27:20+00:00"
"time": "2021-05-27T12:26:48+00:00"
},
{
"name": "symfony/polyfill-php72",
@ -4465,16 +4152,16 @@
},
{
"name": "symfony/polyfill-php80",
"version": "v1.23.0",
"version": "v1.23.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0"
"reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/eca0bf41ed421bed1b57c4958bab16aa86b757d0",
"reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be",
"reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be",
"shasum": ""
},
"require": {
@ -4528,7 +4215,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.23.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1"
},
"funding": [
{
@ -4544,7 +4231,7 @@
"type": "tidelift"
}
],
"time": "2021-02-19T12:13:01+00:00"
"time": "2021-07-28T13:41:28+00:00"
},
{
"name": "symfony/process",
@ -5035,16 +4722,16 @@
},
{
"name": "symfony/var-dumper",
"version": "v5.3.4",
"version": "v5.3.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "a895407f7cf55da42aa1480935d707684b690bfc"
"reference": "3dd8ddd1e260e58ecc61bb78da3b6584b3bfcba0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/a895407f7cf55da42aa1480935d707684b690bfc",
"reference": "a895407f7cf55da42aa1480935d707684b690bfc",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/3dd8ddd1e260e58ecc61bb78da3b6584b3bfcba0",
"reference": "3dd8ddd1e260e58ecc61bb78da3b6584b3bfcba0",
"shasum": ""
},
"require": {
@ -5103,7 +4790,7 @@
"dump"
],
"support": {
"source": "https://github.com/symfony/var-dumper/tree/v5.3.4"
"source": "https://github.com/symfony/var-dumper/tree/v5.3.6"
},
"funding": [
{
@ -5119,7 +4806,7 @@
"type": "tidelift"
}
],
"time": "2021-07-23T15:55:36+00:00"
"time": "2021-07-27T01:56:02+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
@ -7988,16 +7675,16 @@
},
{
"name": "theseer/tokenizer",
"version": "1.2.0",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
"reference": "75a63c33a8577608444246075ea0af0d052e452a"
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a",
"reference": "75a63c33a8577608444246075ea0af0d052e452a",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
"shasum": ""
},
"require": {
@ -8026,7 +7713,7 @@
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"support": {
"issues": "https://github.com/theseer/tokenizer/issues",
"source": "https://github.com/theseer/tokenizer/tree/master"
"source": "https://github.com/theseer/tokenizer/tree/1.2.1"
},
"funding": [
{
@ -8034,7 +7721,7 @@
"type": "github"
}
],
"time": "2020-07-12T23:59:07+00:00"
"time": "2021-07-28T10:34:58+00:00"
}
],
"aliases": [],
@ -8043,8 +7730,11 @@
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
"php": "^7.3"
"php": ">=7.3.0"
},
"platform-dev": [],
"platform-overrides": {
"php": "7.3"
},
"plugin-api-version": "2.1.0"
}

View file

@ -68,7 +68,7 @@ As an administrator, you will be able to generate an API Key allowing you to use
## Accounts administration
From the accounts administration panel an administrator will be able to list, show and delete accounts from the attached Flexisip server.
From the accounts administration panel an administrator will be able to list, create, show, edit and and delete accounts from the attached Flexisip server.
### Display a user account
@ -78,6 +78,8 @@ You can also set an account as an administrator. The account will then have the
Finally the account page allows you to provision the account, using a QR Code or a unique link that can be shared with the contact.
The provisioning link can be generated and refreshed from this page as well.
### Delete an account
The deletion of an account is definitive, all the database related data (password, aliases…) will be destroyed after the deletion.

View file

@ -0,0 +1,80 @@
@extends('layouts.account')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">
<a href="{{ route('admin.account.index') }}">Accounts</a>
</li>
@if ($account->id)
<li class="breadcrumb-item" aria-current="page">
<a href="{{ route('admin.account.show', $account->id) }}">{{ $account->identifier }}</a>
</li>
<li class="breadcrumb-item active" aria-current="page">
Edit
</li>
@else
<li class="breadcrumb-item active" aria-current="page">
Create
</li>
@endif
@endsection
@section('content')
@if ($account->id)
<h2>Edit an account</h2>
@else
<h2>Create an account</h2>
@endif
{!! Form::model($account, [
'route' => $account->id
? ['admin.account.update', $account->id]
: ['admin.account.store'],
'method' => $account->id
? 'put'
: 'post'
]) !!}
<div class="form-row">
<div class="form-group col-md-12">
{!! Form::label('username', 'Username') !!}
<div class="input-group">
{!! Form::text('username', $account->username, ['class' => 'form-control', 'placeholder' => 'Username']); !!}
<div class="input-group-append">
<span class="input-group-text" id="basic-addon1">@ {{ config('app.sip_domain') }}</span>
</div>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
{!! Form::label('password', ($account->id) ? 'Password (fill to change)' : 'Password') !!}
{!! Form::password('password', ['class' => 'form-control', 'placeholder' => 'Password']); !!}
<div class="form-check mt-3">
{!! Form::checkbox('password_sha256', 'checked', $account->sha256Password, ['class' => 'form-check-input']) !!}
{!! Form::label('password_sha256', 'Use a SHA-256 encrypted password', ['class' => 'form-check-label']) !!}
</div>
</div>
</div>
<hr />
<div class="form-row">
<div class="form-group col-md-12 mb-0">
<h4>Optional</h4>
</div>
<div class="form-group col-md-6">
{!! Form::label('email', 'Email') !!}
{!! Form::email('email', $account->email, ['class' => 'form-control', 'placeholder' => 'Email']); !!}
</div>
<div class="form-group col-md-6">
{!! Form::label('phone', 'Phone') !!}
{!! Form::text('phone', $account->phone, ['class' => 'form-control', 'placeholder' => '+12123123']); !!}
</div>
</div>
{!! Form::submit(($account->id) ? 'Update' : 'Create', ['class' => 'btn btn-danger btn-centered']) !!}
{!! Form::close() !!}
@endsection

View file

@ -4,6 +4,9 @@
<li class="breadcrumb-item" aria-current="page">
<a href="{{ route('admin.account.index') }}">Accounts</a>
</li>
<li class="breadcrumb-item" aria-current="page">
<a href="{{ route('admin.account.show', $account->id) }}">{{ $account->identifier }}</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Delete</li>
@endsection

View file

@ -8,6 +8,7 @@
<div class="row mb-2">
<div class="col-sm">
<a class="btn float-right" href="{{ route('admin.account.create') }}">Create</a>
<h2>Accounts</h2>
</div>
<div class="col-sm">
@ -28,29 +29,31 @@
<table class="table table-responsive-md">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Identifier (email)</th>
<th scope="col"></th>
<th scope="col">Created</th>
</tr>
</thead>
<tbody>
@foreach ($accounts as $account)
<tr>
<th scope="row">
<a href="{{ route('admin.account.show', $account->id) }}">{{ $account->id }}</a>
<br />
<td>
<a href="{{ route('admin.account.show', $account->id) }}">
{{ $account->identifier }}
</a>
</td>
<td>
@if ($account->email)
<span class="badge badge-info">Email</span>
@endif
@if ($account->activated)
<span class="badge badge-success">Activated</span>
@else
<span class="badge badge-danger">Unactivated</span>
@endif
@if ($account->admin)
<br /><span class="badge badge-primary">Admin</span>
<span class="badge badge-primary">Admin</span>
@endif
</th>
<td>{{ $account->identifier }}
@if ($account->email)
(<a href="mailto:{{ $account->email }}">{{ $account->email }}</a>)
@if ($account->sha256Password)
<span class="badge badge-info">SHA256</span>
@endif
</td>
<td>{{ $account->creation_time}}</td>

View file

@ -4,42 +4,44 @@
<li class="breadcrumb-item" aria-current="page">
<a href="{{ route('admin.account.index') }}">Accounts</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Show</li>
<li class="breadcrumb-item" aria-current="page">
<a href="{{ route('admin.account.show', $account->id) }}">{{ $account->identifier }}</a>
</li>
@endsection
@section('content')
<a class="btn btn-danger float-right" href="{{ route('admin.account.delete', $account->id) }}">Delete</a>
<a class="btn float-right mr-2" href="{{ route('admin.account.edit', $account->id) }}">Edit</a>
<h2>Account</h2>
<p>
<b>Id:</b> {{ $account->id }}<br />
<b>Identifier:</b> {{ $account->identifier }}<br />
<b>Email:</b> {{ $account->email }}</p>
<b>Email:</b> <a href="mailto:{{ $account->email }}">{{ $account->email }}</a><br />
<b>Phone number:</b>@if ($account->alias) {{ $account->phone }} @else No number @endif
</p>
@if ($account->alias)
<p><b>Alias:</b> {{ $account->alias->alias }}</p>
@else
<p>No alias</p>
@if ($account->sha256Password)
<span class="badge badge-info">SHA256</span>
@endif
<p>
@if ($account->activated)
<span class="badge badge-success">Activated</span> <a href="{{ route('admin.account.deactivate', $account->id) }}">Deactivate</a>
@else
<span class="badge badge-danger">Unactivated</span> <a href="{{ route('admin.account.activate', $account->id) }}">Activate</a>
@endif
</p>
<br />
<p>
@if ($account->admin)
<span class="badge badge-success">Admin</span> <a href="{{ route('admin.account.unadmin', $account->id) }}">Remove admin role</a>
@else
<span class="badge badge-danger">Not Admin</span> <a href="{{ route('admin.account.admin', $account->id) }}">Add admin role</a>
@endif
</p>
@if ($account->activated)
<span class="badge badge-success">Activated</span> <a href="{{ route('admin.account.deactivate', $account->id) }}">Deactivate</a>
@else
<span class="badge badge-danger">Unactivated</span> <a href="{{ route('admin.account.activate', $account->id) }}">Activate</a>
@endif
<a class="btn btn-danger" href="{{ route('admin.account.delete', $account->id) }}">Delete the account</a>
<br />
@if ($account->admin)
<span class="badge badge-success">Admin</span> <a href="{{ route('admin.account.unadmin', $account->id) }}">Remove admin role</a>
@else
<span class="badge badge-danger">Not Admin</span> <a href="{{ route('admin.account.admin', $account->id) }}">Add admin role</a>
@endif
@if ($account->confirmation_key)
<h3 class="mt-3">Provisioning</h3>
@ -50,6 +52,14 @@
<br />
<p>The following link can only be visited once</p>
<input class="form-control" type="text" readonly value="{{ route('provisioning.show', $account->confirmation_key) }}">
<p class="mt-3">
<a class="btn btn-danger mr-2" href="{{ route('admin.account.provision', $account->id) }}">Renew the provision link</a>
The current one will be unavailable
</p>
@else
<p class="mt-3">
<a class="btn btn-danger" href="{{ route('admin.account.provision', $account->id) }}">Generate a provision link</a>
</p>
@endif
@endsection

View file

@ -165,7 +165,7 @@ JSON parameters:
* `username` unique username, minimum 6 characters
* `password` required minimum 6 characters
* `algorithm` required, values can be `SHA-256` or `MD5`
* `domain` optional, the value is set to the default registration domain if not set
* `domain` **not configurable** the value is enforced to the default registration domain set in the global configuration
* `activated` optional, a boolean, set to `false` by default
* `admin` optional, a boolean, set to `false` by default, create an admin account
* `phone` optional, a phone number, set a phone number to the account

View file

@ -64,17 +64,27 @@ Route::group(['middleware' => 'auth'], function () {
});
Route::group(['middleware' => 'auth.admin'], function () {
Route::get('admin/accounts/{search?}', 'Admin\AccountController@index')->name('admin.account.index');
Route::post('admin/search', 'Admin\AccountController@search')->name('admin.account.search');
Route::post('admin/api_key', 'Admin\AccountController@generateApiKey')->name('admin.api_key.generate');
Route::get('admin/accounts/show/{id}', 'Admin\AccountController@show')->name('admin.account.show');
Route::get('admin/accounts/{id}/activate', 'Admin\AccountController@activate')->name('admin.account.activate');
Route::get('admin/accounts/{id}/deactivate', 'Admin\AccountController@deactivate')->name('admin.account.deactivate');
Route::get('admin/accounts/{id}/admin', 'Admin\AccountController@admin')->name('admin.account.admin');
Route::get('admin/accounts/{account}/show', 'Admin\AccountController@show')->name('admin.account.show');
Route::get('admin/accounts/{account}/activate', 'Admin\AccountController@activate')->name('admin.account.activate');
Route::get('admin/accounts/{account}/deactivate', 'Admin\AccountController@deactivate')->name('admin.account.deactivate');
Route::get('admin/accounts/{account}/admin', 'Admin\AccountController@admin')->name('admin.account.admin');
Route::get('admin/accounts/{id}/unadmin', 'Admin\AccountController@unadmin')->name('admin.account.unadmin');
Route::get('admin/accounts/{id}/delete', 'Admin\AccountController@delete')->name('admin.account.delete');
Route::get('admin/accounts/{account}/provision', 'Admin\AccountController@provision')->name('admin.account.provision');
Route::get('admin/accounts/create', 'Admin\AccountController@create')->name('admin.account.create');
Route::post('admin/accounts', 'Admin\AccountController@store')->name('admin.account.store');
Route::get('admin/accounts/{account}/edit', 'Admin\AccountController@edit')->name('admin.account.edit');
Route::put('admin/accounts/{id}', 'Admin\AccountController@update')->name('admin.account.update');
Route::get('admin/accounts/{account}/delete', 'Admin\AccountController@delete')->name('admin.account.delete');
Route::delete('admin/accounts', 'Admin\AccountController@destroy')->name('admin.account.destroy');
Route::get('admin/accounts/{search?}', 'Admin\AccountController@index')->name('admin.account.index');
Route::post('admin/accounts/search', 'Admin\AccountController@search')->name('admin.account.search');
});

View file

@ -85,6 +85,9 @@ class AccountApiTest extends TestCase
public function testDomain()
{
$configDomain = 'sip.domain.com';
config()->set('app.sip_domain', $configDomain);
$admin = Admin::factory()->create();
$password = $admin->account->passwords()->first();
$username = 'foobar';
@ -104,7 +107,7 @@ class AccountApiTest extends TestCase
->assertJson([
'id' => 2,
'username' => $username,
'domain' => $domain,
'domain' => $configDomain,
'activated' => false
]);

View file

@ -8,7 +8,7 @@
#%define _datadir %{_datarootdir}
#%define _docdir %{_datadir}/doc
%define build_number 88
%define build_number 89
%define var_dir /var/opt/belledonne-communications
%define opt_dir /opt/belledonne-communications/share/flexisip-account-manager