[admin] agent allow&deny

This commit is contained in:
xiaomlove
2022-02-25 18:09:31 +08:00
parent e2f30ecf0c
commit 9edbaf49ca
25 changed files with 1420 additions and 28 deletions
+5 -2
View File
@@ -11,6 +11,7 @@ use App\Models\Medal;
use App\Models\SearchBox;
use App\Models\Snatch;
use App\Models\User;
use App\Repositories\AgentAllowRepository;
use App\Repositories\ExamRepository;
use App\Repositories\HitAndRunRepository;
use App\Repositories\SearchBoxRepository;
@@ -58,8 +59,10 @@ class Test extends Command
*/
public function handle()
{
$rep = new HitAndRunRepository();
$r = $rep->cronjobUpdateStatus();
$peerId = '-TR2920-9bqp8iu7v9se';
$agent = 'Transmission/2.92';
$rep = new AgentAllowRepository();
$r = $rep->checkClient($peerId, $agent, true);
dd($r);
}
+56 -8
View File
@@ -4,18 +4,47 @@ namespace App\Http\Controllers;
use App\Http\Resources\AgentAllowResource;
use App\Models\AgentAllow;
use App\Repositories\AgentAllowRepository;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
class AgentAllowController extends Controller
{
private $repository;
public function __construct(AgentAllowRepository $repository)
{
$this->repository = $repository;
}
private function getRules(): array
{
return [
'family' => 'required|string',
'start_name' => 'required|string',
'peer_id_pattern' => 'required|string',
'peer_id_match_num' => 'required|numeric',
'peer_id_matchtype' => ['required', Rule::in(array_keys(AgentAllow::$matchTypes))],
'peer_id_start' => 'required|string',
'agent_pattern' => 'required|string',
'agent_match_num' => 'required|numeric',
'agent_matchtype' => ['required', Rule::in(array_keys(AgentAllow::$matchTypes))],
'agent_start' => 'required|string',
'exception' => ['required', Rule::in(['yes', 'no'])],
'allowhttps' => ['required', Rule::in(['yes', 'no'])],
];
}
/**
* Display a listing of the resource.
*
* @return array
*/
public function index()
public function index(Request $request)
{
$result = AgentAllow::query()->orderBy('id', 'desc')->paginate();
$result = $this->repository->getList($request->all());
$resource = AgentAllowResource::collection($result);
return $this->success($resource);
}
@@ -28,7 +57,10 @@ class AgentAllowController extends Controller
*/
public function store(Request $request)
{
//
$request->validate($this->getRules());
$result = $this->repository->store($request->all());
$resource = new AgentAllowResource($result);
return $this->success($resource);
}
/**
@@ -53,8 +85,8 @@ class AgentAllowController extends Controller
*/
public function update(Request $request, $id)
{
$result = AgentAllow::query()->findOrFail($id);
$result->update($request->all());
$request->validate($this->getRules());
$result = $this->repository->update($request->all(), $id);
$resource = new AgentAllowResource($result);
return $this->success($resource);
}
@@ -67,8 +99,24 @@ class AgentAllowController extends Controller
*/
public function destroy($id)
{
$result = AgentAllow::query()->findOrFail($id);
$deleted = $result->delete();
return $this->success([$deleted]);
$result = $this->repository->delete($id);
return $this->success($result);
}
public function all()
{
$result = AgentAllow::query()->orderBy('id', 'desc')->get();
$resource = AgentAllowResource::collection($result);
return $this->success($resource);
}
public function check(Request $request)
{
$request->validate([
'peer_id' => 'required|string',
'agent' => 'required|string',
]);
$result = $this->repository->checkClient($request->peer_id, $request->agent, true);
return $this->success($result->toArray(), sprintf("Congratulations! the client is allowed by ID: %s", $result->id));
}
}
@@ -0,0 +1,96 @@
<?php
namespace App\Http\Controllers;
use App\Http\Resources\AgentDenyResource;
use App\Models\AgentDeny;
use App\Repositories\AgentDenyRepository;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
class AgentDenyController extends Controller
{
private $repository;
public function __construct(AgentDenyRepository $repository)
{
$this->repository = $repository;
}
private function getRules(): array
{
return [
'family_id' => 'required|numeric',
'name' => 'required|string',
'peer_id' => 'required|string',
'agent' => 'required|string',
'comment' => 'required|string',
];
}
/**
* Display a listing of the resource.
*
* @return array
*/
public function index(Request $request)
{
$result = $this->repository->getList($request->all());
$resource = AgentDenyResource::collection($result);
return $this->success($resource);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$request->validate($this->getRules());
$result = $this->repository->store($request->all());
$resource = new AgentDenyResource($result);
return $this->success($resource);
}
/**
* Display the specified resource.
*
* @param int $id
* @return array
*/
public function show($id)
{
$result = AgentDeny::query()->findOrFail($id);
$resource = new AgentDenyResource($result);
return $this->success($resource);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return array
*/
public function update(Request $request, $id)
{
$request->validate($this->getRules());
$result = $this->repository->update($request->all(), $id);
$resource = new AgentDenyResource($result);
return $this->success($resource);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return array
*/
public function destroy($id)
{
$result = $this->repository->delete($id);
return $this->success($result);
}
}
+1 -1
View File
@@ -20,7 +20,7 @@ class Kernel extends HttpKernel
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
// \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
/**
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class AgentDenyResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'family_id' => $this->family_id,
'agent' => $this->agent,
'peer_id' => $this->peer_id,
'comment' => $this->comment,
'name' => $this->name,
'family' => new AgentAllowResource($this->whenLoaded('family'))
];
}
}
+15 -2
View File
@@ -6,11 +6,24 @@ class AgentAllow extends NexusModel
{
protected $table = 'agent_allowed_family';
public $timestamps = true;
protected $fillable = [
'family', 'start_name', 'exception', 'allowhttps', 'comment',
'peer_id_pattern', 'peer_id_match_num', 'peer_id_matchtype', 'peer_id_start',
'agent_pattern', 'agent_match_num', 'agent_matchtype', 'agent_start',
];
const MATCH_TYPE_DEC = 'dec';
const MATCH_TYPE_HEX = 'hex';
public static $matchTypes = [
self::MATCH_TYPE_DEC => 'dec',
self::MATCH_TYPE_HEX => 'hex',
];
public function denies(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(AgentDeny::class, 'family_id');
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace App\Models;
class AgentDeny extends NexusModel
{
protected $table = 'agent_allowed_exception';
protected $fillable = [
'family_id', 'name', 'peer_id', 'agent', 'comment'
];
public function family(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(AgentAllow::class, 'family_id');
}
}
+243
View File
@@ -0,0 +1,243 @@
<?php
namespace App\Repositories;
use App\Exceptions\NexusException;
use App\Models\AgentAllow;
use App\Models\AgentDeny;
class AgentAllowRepository extends BaseRepository
{
public function getList(array $params)
{
$query = AgentAllow::query();
if (!empty($params['family'])) {
$query->where('family', 'like', "%{$params['family']}%");
}
list($sortField, $sortType) = $this->getSortFieldAndType($params);
$query->orderBy($sortField, $sortType);
return $query->paginate();
}
public function store(array $params)
{
$this->getPatternMatches($params['peer_id_pattern'], $params['peer_id_start'], $params['peer_id_match_num']);
$this->getPatternMatches($params['agent_pattern'], $params['agent_start'], $params['agent_match_num']);
$model = AgentAllow::query()->create($params);
return $model;
}
public function update(array $params, $id)
{
$this->getPatternMatches($params['peer_id_pattern'], $params['peer_id_start'], $params['peer_id_match_num']);
$this->getPatternMatches($params['agent_pattern'], $params['agent_start'], $params['agent_match_num']);
$model = AgentAllow::query()->findOrFail($id);
$model->update($params);
return $model;
}
public function getDetail($id)
{
$model = AgentAllow::query()->findOrFail($id);
return $model;
}
public function delete($id)
{
$model = AgentAllow::query()->findOrFail($id);
$model->denies()->delete();
$result = $model->delete();
return $result;
}
public function getPatternMatches($pattern, $start, $matchNum)
{
if (!preg_match($pattern, $start, $matches)) {
throw new NexusException(sprintf('pattern: %s can not match start: %s', $pattern, $start));
}
$matchCount = count($matches) - 1;
if ($matchNum > $matchCount) {
throw new NexusException("pattern: $pattern match start: $start got matches count: $matchCount, but require $matchNum.");
}
return array_slice($matches, 1, $matchNum);
}
public function checkClient($peerId, $agent, $debug = false)
{
//check from high version to low version, if high version allow, stop!
$allows = AgentAllow::query()
->orderBy('peer_id_start', 'desc')
->orderBy('agent_start', 'desc')
->get();
$agentAllowPassed = null;
$versionTooLowStr = '';
foreach ($allows as $agentAllow) {
$agentAllowId = $agentAllow->id;
$isPeerIdAllowed = $isAgentAllowed = $isPeerIdTooLow = $isAgentTooLow = false;
//check peer_id
if ($agentAllow->peer_id_pattern == '') {
$isPeerIdAllowed = true;
} else {
$pattern = $agentAllow->peer_id_pattern;
$start = $agentAllow->peer_id_start;
$matchType = $agentAllow->peer_id_matchtype;
$matchNum = $agentAllow->peer_id_match_num;
try {
$peerIdResult = $this->isAllowed($pattern, $start, $matchNum, $matchType, $peerId, $debug);
if ($debug) {
do_log(
"agentAllowId: $agentAllowId, peerIdResult: $peerIdResult, with parameters: "
. nexus_json_encode(compact('pattern', 'start', 'matchNum', 'matchType', 'peerId'))
);
}
} catch (\Exception $exception) {
do_log("agent allow: {$agentAllow->id} check peer_id error: " . $exception->getMessage(), 'error');
throw new NexusException("regular expression err for peer_id: " . $start . ", please ask sysop to fix this");
}
if ($peerIdResult == 1) {
$isPeerIdAllowed = true;
}
if ($peerIdResult == 2) {
$isPeerIdTooLow = true;
}
}
//check agent
if ($agentAllow->agent_pattern == '') {
$isAgentAllowed = true;
} else {
$pattern = $agentAllow->agent_pattern;
$start = $agentAllow->agent_start;
$matchType = $agentAllow->agent_matchtype;
$matchNum = $agentAllow->agent_match_num;
try {
$agentResult = $this->isAllowed($pattern, $start, $matchNum, $matchType, $agent, $debug);
if ($debug) {
do_log(
"agentAllowId: $agentAllowId, agentResult: $agentResult, with parameters: "
. nexus_json_encode(compact('pattern', 'start', 'matchNum', 'matchType', 'agent'))
);
}
} catch (\Exception $exception) {
do_log("agent allow: {$agentAllow->id} check agent error: " . $exception->getMessage(), 'error');
throw new NexusException("regular expression err for agent: " . $start . ", please ask sysop to fix this");
}
if ($agentResult == 1) {
$isAgentAllowed = true;
}
if ($agentResult == 2) {
$isAgentTooLow = true;
}
}
//both OK, passed, client is allowed
if ($isPeerIdAllowed && $isAgentAllowed) {
$agentAllowPassed = $agentAllow;
break;
}
if ($isPeerIdTooLow && $isAgentTooLow) {
$versionTooLowStr = "Your " . $agentAllow->family . " 's version is too low, please update it after " . $agentAllow->start_name;
}
}
if ($versionTooLowStr) {
throw new NexusException($versionTooLowStr);
}
if (!$agentAllowPassed) {
throw new NexusException("Banned Client, Please goto " . getSchemeAndHttpHost() . "/faq.php#id29 for a list of acceptable clients");
}
if ($debug) {
do_log("agentAllowPassed: " . $agentAllowPassed->toJson());
}
// check if exclude
if ($agentAllowPassed->exception == 'yes') {
$agentDeny = $this->checkIsDenied($peerId, $agent, $agentAllowPassed->id);
if ($agentDeny) {
if ($debug) {
do_log("agentDeny: " . $agentDeny->toJson());
}
throw new NexusException(sprintf(
"[%s-%s]Client: %s is banned due to: %s",
$agentAllowPassed->id, $agentDeny->id, $agentDeny->name, $agentDeny->comment
));
}
}
if (isHttps() && $agentAllowPassed->allowhttps != 'yes') {
throw new NexusException(sprintf(
"[%s]This client does not support https well, Please goto %s/faq.php#id29 for a list of proper clients",
$agentAllowPassed->id, getSchemeAndHttpHost()
));
}
return $agentAllowPassed;
}
private function checkIsDenied($peerId, $agent, $familyId)
{
$agentDenies = AgentDeny::query()->where('family_id', $familyId)->get();
foreach ($agentDenies as $agentDeny) {
if ($agentDeny->agent == $agent && preg_match("/^" . $agentDeny->peer_id . "/", $peerId)) {
return $agentDeny;
}
}
}
/**
* check peer_id or agent is allowed
*
* 0: not allowed
* 1: allowed
* 2: version too low
*
* @param $pattern
* @param $start
* @param $matchNum
* @param $matchType
* @param $value
* @param bool $debug
* @return int
* @throws NexusException
*/
private function isAllowed($pattern, $start, $matchNum, $matchType, $value, $debug = false): int
{
$matchBench = $this->getPatternMatches($pattern, $start, $matchNum);
if ($debug) {
do_log("matchBench: " . nexus_json_encode($matchBench));
}
if (!preg_match($pattern, $value, $matchTarget)) {
return 0;
}
if ($matchNum <= 0) {
return 1;
}
$matchTarget = array_slice($matchTarget, 1);
if ($debug) {
do_log("matchTarget: " . nexus_json_encode($matchTarget));
}
for ($i = 0; $i < $matchNum; $i++) {
if (!isset($matchBench[$i]) || !isset($matchTarget[$i])) {
break;
}
if ($matchType == 'dec') {
$matchBench[$i] = intval($matchBench[$i]);
$matchTarget[$i] = intval($matchTarget[$i]);
} elseif ($matchType == 'hex') {
$matchBench[$i] = hexdec($matchBench[$i]);
$matchTarget[$i] = hexdec($matchTarget[$i]);
} else {
throw new NexusException(sprintf("Invalid match type: %s", $matchType));
}
if ($matchTarget[$i] > $matchBench[$i]) {
return 1;
} elseif ($matchTarget[$i] < $matchBench[$i]) {
return 2;
}
}
return 0;
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
namespace App\Repositories;
use App\Models\AgentDeny;
class AgentDenyRepository extends BaseRepository
{
public function getList(array $params)
{
$query = AgentDeny::query()->with(['family']);
if (!empty($params['family_id'])) {
$query->where('family_id', $params['family_id']);
}
$query->orderBy('family_id', 'desc');
return $query->paginate();
}
public function store(array $params)
{
$model = AgentDeny::query()->create($params);
return $model;
}
public function update(array $params, $id)
{
$model = AgentDeny::query()->findOrFail($id);
$model->update($params);
return $model;
}
public function getDetail($id)
{
$model = AgentDeny::query()->findOrFail($id);
return $model;
}
public function delete($id)
{
$model = AgentDeny::query()->findOrFail($id);
$result = $model->delete();
return $result;
}
}
+9
View File
@@ -16,6 +16,15 @@ class UserRepository extends BaseRepository
public function getList(array $params)
{
$query = User::query();
if (!empty($params['id'])) {
$query->where('id', $params['id']);
}
if (!empty($params['username'])) {
$query->where('username', 'like',"%{$params['username']}%");
}
if (!empty($params['email'])) {
$query->where('email', 'like',"%{$params['email']}%");
}
list($sortField, $sortType) = $this->getSortFieldAndType($params);
$query->orderBy($sortField, $sortType);
return $query->paginate();