2023-03-08 18:30:01 +00:00
< ? php
use Appwrite\Event\Certificate ;
use Appwrite\Event\Delete ;
use Appwrite\Event\Event ;
use Appwrite\Extend\Exception ;
use Appwrite\Network\Validator\CNAME ;
2025-01-17 04:31:39 +00:00
use Appwrite\SDK\AuthType ;
use Appwrite\SDK\ContentType ;
use Appwrite\SDK\Method ;
use Appwrite\SDK\Response as SDKResponse ;
2023-03-08 18:30:01 +00:00
use Appwrite\Utopia\Database\Validator\Queries\Rules ;
use Appwrite\Utopia\Response ;
2024-10-08 07:54:40 +00:00
use Utopia\App ;
2023-03-08 18:30:01 +00:00
use Utopia\Database\Database ;
use Utopia\Database\Document ;
2024-02-12 16:02:04 +00:00
use Utopia\Database\Exception\Query as QueryException ;
2024-11-22 17:16:51 +00:00
use Utopia\Database\Helpers\ID ;
2023-03-08 18:30:01 +00:00
use Utopia\Database\Query ;
2024-10-17 05:41:24 +00:00
use Utopia\Database\Validator\Query\Cursor ;
2023-03-08 18:30:01 +00:00
use Utopia\Database\Validator\UID ;
use Utopia\Domains\Domain ;
2024-02-12 01:18:19 +00:00
use Utopia\Logger\Log ;
2024-04-01 11:08:46 +00:00
use Utopia\System\System ;
2024-10-08 07:54:40 +00:00
use Utopia\Validator\Domain as ValidatorDomain ;
use Utopia\Validator\Text ;
use Utopia\Validator\WhiteList ;
2023-03-08 18:30:01 +00:00
2024-10-08 07:54:40 +00:00
App :: post ( '/v1/proxy/rules' )
2023-03-08 18:30:01 +00:00
-> groups ([ 'api' , 'proxy' ])
2024-09-03 16:22:30 +00:00
-> desc ( 'Create rule' )
2023-03-08 18:30:01 +00:00
-> label ( 'scope' , 'rules.write' )
-> label ( 'event' , 'rules.[ruleId].create' )
-> label ( 'audits.event' , 'rule.create' )
-> label ( 'audits.resource' , 'rule/{response.$id}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'proxy' ,
2025-04-12 06:41:57 +00:00
group : null ,
2025-01-17 04:31:39 +00:00
name : 'createRule' ,
description : '/docs/references/proxy/create-rule.md' ,
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_CREATED ,
model : Response :: MODEL_PROXY_RULE ,
)
]
))
2023-08-06 08:51:53 +00:00
-> param ( 'domain' , null , new ValidatorDomain (), 'Domain name.' )
2023-08-19 18:26:47 +00:00
-> param ( 'resourceType' , null , new WhiteList ([ 'api' , 'function' ]), 'Action definition for the rule. Possible values are "api", "function"' )
-> param ( 'resourceId' , '' , new UID (), 'ID of resource for the action type. If resourceType is "api", leave empty. If resourceType is "function", provide ID of the function.' , true )
2023-03-08 18:30:01 +00:00
-> inject ( 'response' )
-> inject ( 'project' )
2023-10-19 15:28:01 +00:00
-> inject ( 'queueForCertificates' )
-> inject ( 'queueForEvents' )
2024-12-12 10:30:26 +00:00
-> inject ( 'dbForPlatform' )
2023-03-08 18:30:01 +00:00
-> inject ( 'dbForProject' )
2024-12-12 10:30:26 +00:00
-> action ( function ( string $domain , string $resourceType , string $resourceId , Response $response , Document $project , Certificate $queueForCertificates , Event $queueForEvents , Database $dbForPlatform , Database $dbForProject ) {
2024-04-01 11:02:47 +00:00
$mainDomain = System :: getEnv ( '_APP_DOMAIN' , '' );
2023-08-22 18:14:20 +00:00
if ( $domain === $mainDomain ) {
throw new Exception ( Exception :: GENERAL_ARGUMENT_INVALID , 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.' );
}
2024-04-08 03:57:08 +00:00
2024-05-05 05:22:59 +00:00
$functionsDomain = System :: getEnv ( '_APP_DOMAIN_FUNCTIONS' , '' );
2024-08-03 06:56:21 +00:00
if ( $functionsDomain != '' && str_ends_with ( $domain , $functionsDomain )) {
2024-04-08 03:46:15 +00:00
throw new Exception ( Exception :: GENERAL_ARGUMENT_INVALID , 'You cannot assign your functions domain or it\'s subdomain to specific resource. Please use different domain.' );
}
2024-04-08 03:57:08 +00:00
2023-08-22 18:14:20 +00:00
if ( $domain === 'localhost' || $domain === APP_HOSTNAME_INTERNAL ) {
2023-08-22 08:16:17 +00:00
throw new Exception ( Exception :: GENERAL_ARGUMENT_INVALID , 'This domain name is not allowed. Please pick another one.' );
2023-08-21 17:43:03 +00:00
}
2023-08-22 13:18:07 +00:00
2024-11-22 17:16:51 +00:00
// TODO: @christyjacob remove once we migrate the rules in 1.7.x
2024-11-28 10:33:00 +00:00
if ( System :: getEnv ( '_APP_RULES_FORMAT' ) === 'md5' ) {
2024-12-12 10:30:26 +00:00
$document = $dbForPlatform -> getDocument ( 'rules' , md5 ( $domain ));
2024-11-28 10:18:37 +00:00
} else {
2024-12-12 10:30:26 +00:00
$document = $dbForPlatform -> findOne ( 'rules' , [
2024-11-22 17:16:51 +00:00
Query :: equal ( 'domain' , [ $domain ]),
]);
}
2024-11-22 17:18:23 +00:00
2023-03-08 18:30:01 +00:00
2024-10-31 08:13:23 +00:00
if ( ! $document -> isEmpty ()) {
2023-03-14 19:31:23 +00:00
if ( $document -> getAttribute ( 'projectId' ) === $project -> getId ()) {
2023-03-10 13:36:31 +00:00
$resourceType = $document -> getAttribute ( 'resourceType' );
$resourceId = $document -> getAttribute ( 'resourceId' );
$message = " Domain already assigned to ' { $resourceType } ' service " ;
2023-03-14 19:31:23 +00:00
if ( ! empty ( $resourceId )) {
2023-03-10 13:36:31 +00:00
$message .= " with ID ' { $resourceId } ' " ;
}
$message .= '.' ;
} else {
2023-08-18 06:55:44 +00:00
$message = 'Domain already assigned to different project.' ;
2023-03-10 13:36:31 +00:00
}
2023-03-14 19:31:23 +00:00
2023-03-10 13:36:31 +00:00
throw new Exception ( Exception :: RULE_ALREADY_EXISTS , $message );
2023-03-08 18:30:01 +00:00
}
$resourceInternalId = '' ;
2023-03-14 19:31:23 +00:00
if ( $resourceType == 'function' ) {
if ( empty ( $resourceId )) {
2023-07-28 08:27:16 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2023-03-08 18:30:01 +00:00
}
$function = $dbForProject -> getDocument ( 'functions' , $resourceId );
if ( $function -> isEmpty ()) {
2023-07-28 07:56:07 +00:00
throw new Exception ( Exception :: RULE_RESOURCE_NOT_FOUND );
2023-03-08 18:30:01 +00:00
}
$resourceInternalId = $function -> getInternalId ();
}
2023-11-06 14:44:00 +00:00
try {
$domain = new Domain ( $domain );
2024-02-08 01:17:54 +00:00
} catch ( \Throwable ) {
2023-11-06 14:49:33 +00:00
throw new Exception ( Exception :: GENERAL_ARGUMENT_INVALID , 'Domain may not start with http:// or https://.' );
2023-11-06 14:44:00 +00:00
}
2023-11-06 14:48:54 +00:00
2024-11-22 17:16:51 +00:00
// TODO: @christyjacob remove once we migrate the rules in 1.7.x
2024-11-28 10:33:00 +00:00
$ruleId = System :: getEnv ( '_APP_RULES_FORMAT' ) === 'md5' ? md5 ( $domain -> get ()) : ID :: unique ();
2024-11-22 17:18:23 +00:00
2023-07-12 10:55:33 +00:00
$rule = new Document ([
2023-03-08 18:30:01 +00:00
'$id' => $ruleId ,
'projectId' => $project -> getId (),
'projectInternalId' => $project -> getInternalId (),
'domain' => $domain -> get (),
'resourceType' => $resourceType ,
'resourceId' => $resourceId ,
'resourceInternalId' => $resourceInternalId ,
'certificateId' => '' ,
2025-03-14 08:37:44 +00:00
'owner' => '' ,
'region' => $project -> getAttribute ( 'region' )
2023-07-12 10:55:33 +00:00
]);
$status = 'created' ;
2024-04-01 11:02:47 +00:00
$functionsDomain = System :: getEnv ( '_APP_DOMAIN_FUNCTIONS' );
2023-07-24 13:12:36 +00:00
if ( ! empty ( $functionsDomain ) && \str_ends_with ( $domain -> get (), $functionsDomain )) {
2023-07-12 10:55:33 +00:00
$status = 'verified' ;
}
if ( $status === 'created' ) {
2024-04-01 11:02:47 +00:00
$target = new Domain ( System :: getEnv ( '_APP_DOMAIN_TARGET' , '' ));
2023-07-12 10:55:33 +00:00
$validator = new CNAME ( $target -> get ()); // Verify Domain with DNS records
if ( $validator -> isValid ( $domain -> get ())) {
$status = 'verifying' ;
2023-10-19 15:28:01 +00:00
$queueForCertificates
2023-07-12 10:55:33 +00:00
-> setDomain ( new Document ([
'domain' => $rule -> getAttribute ( 'domain' )
]))
-> trigger ();
}
}
$rule -> setAttribute ( 'status' , $status );
2024-12-12 10:30:26 +00:00
$rule = $dbForPlatform -> createDocument ( 'rules' , $rule );
2023-03-08 18:30:01 +00:00
2023-10-19 15:28:01 +00:00
$queueForEvents -> setParam ( 'ruleId' , $rule -> getId ());
2023-03-08 18:30:01 +00:00
2023-03-13 13:35:34 +00:00
$rule -> setAttribute ( 'logs' , '' );
2023-03-08 18:30:01 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
2023-03-10 07:42:52 +00:00
-> dynamic ( $rule , Response :: MODEL_PROXY_RULE );
2023-03-08 18:30:01 +00:00
});
2024-10-08 07:54:40 +00:00
App :: get ( '/v1/proxy/rules' )
2023-03-08 18:30:01 +00:00
-> groups ([ 'api' , 'proxy' ])
2024-09-03 16:22:30 +00:00
-> desc ( 'List rules' )
2023-03-08 18:30:01 +00:00
-> label ( 'scope' , 'rules.read' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'proxy' ,
2025-04-12 06:41:57 +00:00
group : null ,
2025-01-17 04:31:39 +00:00
name : 'listRules' ,
description : '/docs/references/proxy/list-rules.md' ,
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_PROXY_RULE_LIST ,
)
]
))
2023-03-08 18:30:01 +00:00
-> param ( 'queries' , [], new Rules (), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode ( ', ' , Rules :: ALLOWED_ATTRIBUTES ), true )
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
-> inject ( 'response' )
-> inject ( 'project' )
2024-12-12 10:30:26 +00:00
-> inject ( 'dbForPlatform' )
-> action ( function ( array $queries , string $search , Response $response , Document $project , Database $dbForPlatform ) {
2024-02-12 16:02:04 +00:00
try {
$queries = Query :: parseQueries ( $queries );
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2023-03-08 18:30:01 +00:00
if ( ! empty ( $search )) {
$queries [] = Query :: search ( 'search' , $search );
}
$queries [] = Query :: equal ( 'projectInternalId' , [ $project -> getInternalId ()]);
2024-02-12 09:55:45 +00:00
/**
2024-02-12 10:03:31 +00:00
* Get cursor document if there was a cursor query , we use array_filter and reset for reference $cursor to $queries
2024-02-12 09:55:45 +00:00
*/
$cursor = \array_filter ( $queries , function ( $query ) {
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSOR_AFTER , Query :: TYPE_CURSOR_BEFORE ]);
});
2023-03-08 18:30:01 +00:00
$cursor = reset ( $cursor );
if ( $cursor ) {
/** @var Query $cursor */
2024-10-17 05:41:24 +00:00
$validator = new Cursor ();
if ( ! $validator -> isValid ( $cursor )) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $validator -> getDescription ());
}
2023-03-08 18:30:01 +00:00
$ruleId = $cursor -> getValue ();
2024-12-12 10:30:26 +00:00
$cursorDocument = $dbForPlatform -> getDocument ( 'rules' , $ruleId );
2023-03-08 18:30:01 +00:00
if ( $cursorDocument -> isEmpty ()) {
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Rule ' { $ruleId } ' for the 'cursor' value not found. " );
}
$cursor -> setValue ( $cursorDocument );
}
$filterQueries = Query :: groupByType ( $queries )[ 'filters' ];
2024-12-12 10:30:26 +00:00
$rules = $dbForPlatform -> find ( 'rules' , $queries );
2023-03-13 13:35:34 +00:00
foreach ( $rules as $rule ) {
2024-12-12 10:30:26 +00:00
$certificate = $dbForPlatform -> getDocument ( 'certificates' , $rule -> getAttribute ( 'certificateId' , '' ));
2023-03-13 13:35:34 +00:00
$rule -> setAttribute ( 'logs' , $certificate -> getAttribute ( 'logs' , '' ));
2023-06-08 15:24:27 +00:00
$rule -> setAttribute ( 'renewAt' , $certificate -> getAttribute ( 'renewDate' , '' ));
2023-03-13 13:35:34 +00:00
}
2023-03-08 18:30:01 +00:00
$response -> dynamic ( new Document ([
2023-03-13 13:35:34 +00:00
'rules' => $rules ,
2024-12-12 10:30:26 +00:00
'total' => $dbForPlatform -> count ( 'rules' , $filterQueries , APP_LIMIT_COUNT ),
2023-03-08 18:30:01 +00:00
]), Response :: MODEL_PROXY_RULE_LIST );
});
2024-10-08 07:54:40 +00:00
App :: get ( '/v1/proxy/rules/:ruleId' )
2023-03-08 18:30:01 +00:00
-> groups ([ 'api' , 'proxy' ])
2024-09-03 16:22:30 +00:00
-> desc ( 'Get rule' )
2023-03-08 18:30:01 +00:00
-> label ( 'scope' , 'rules.read' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'proxy' ,
2025-04-12 06:41:57 +00:00
group : null ,
2025-01-17 04:31:39 +00:00
name : 'getRule' ,
description : '/docs/references/proxy/get-rule.md' ,
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_PROXY_RULE ,
)
]
))
2023-03-08 18:30:01 +00:00
-> param ( 'ruleId' , '' , new UID (), 'Rule ID.' )
-> inject ( 'response' )
-> inject ( 'project' )
2024-12-12 10:30:26 +00:00
-> inject ( 'dbForPlatform' )
-> action ( function ( string $ruleId , Response $response , Document $project , Database $dbForPlatform ) {
$rule = $dbForPlatform -> getDocument ( 'rules' , $ruleId );
2023-03-08 18:30:01 +00:00
if ( $rule -> isEmpty () || $rule -> getAttribute ( 'projectInternalId' ) !== $project -> getInternalId ()) {
throw new Exception ( Exception :: RULE_NOT_FOUND );
}
2024-12-12 10:30:26 +00:00
$certificate = $dbForPlatform -> getDocument ( 'certificates' , $rule -> getAttribute ( 'certificateId' , '' ));
2023-03-13 13:35:34 +00:00
$rule -> setAttribute ( 'logs' , $certificate -> getAttribute ( 'logs' , '' ));
2023-06-08 15:24:27 +00:00
$rule -> setAttribute ( 'renewAt' , $certificate -> getAttribute ( 'renewDate' , '' ));
2023-03-13 13:35:34 +00:00
2023-03-08 18:30:01 +00:00
$response -> dynamic ( $rule , Response :: MODEL_PROXY_RULE );
});
2024-10-08 07:54:40 +00:00
App :: delete ( '/v1/proxy/rules/:ruleId' )
2023-03-08 18:30:01 +00:00
-> groups ([ 'api' , 'proxy' ])
2024-09-03 16:22:30 +00:00
-> desc ( 'Delete rule' )
2023-03-08 18:30:01 +00:00
-> label ( 'scope' , 'rules.write' )
-> label ( 'event' , 'rules.[ruleId].delete' )
2023-07-13 11:42:04 +00:00
-> label ( 'audits.event' , 'rules.delete' )
2023-03-08 18:30:01 +00:00
-> label ( 'audits.resource' , 'rule/{request.ruleId}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'proxy' ,
2025-04-12 06:41:57 +00:00
group : null ,
2025-01-17 04:31:39 +00:00
name : 'deleteRule' ,
description : '/docs/references/proxy/delete-rule.md' ,
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_NOCONTENT ,
model : Response :: MODEL_NONE ,
)
],
contentType : ContentType :: NONE
))
2023-03-08 18:30:01 +00:00
-> param ( 'ruleId' , '' , new UID (), 'Rule ID.' )
-> inject ( 'response' )
-> inject ( 'project' )
2024-12-12 10:30:26 +00:00
-> inject ( 'dbForPlatform' )
2023-10-19 15:28:01 +00:00
-> inject ( 'queueForDeletes' )
-> inject ( 'queueForEvents' )
2024-12-12 10:30:26 +00:00
-> action ( function ( string $ruleId , Response $response , Document $project , Database $dbForPlatform , Delete $queueForDeletes , Event $queueForEvents ) {
$rule = $dbForPlatform -> getDocument ( 'rules' , $ruleId );
2023-03-08 18:30:01 +00:00
if ( $rule -> isEmpty () || $rule -> getAttribute ( 'projectInternalId' ) !== $project -> getInternalId ()) {
throw new Exception ( Exception :: RULE_NOT_FOUND );
}
2024-12-12 10:30:26 +00:00
$dbForPlatform -> deleteDocument ( 'rules' , $rule -> getId ());
2023-03-08 18:30:01 +00:00
2023-10-19 15:28:01 +00:00
$queueForDeletes
2023-03-08 18:30:01 +00:00
-> setType ( DELETE_TYPE_DOCUMENT )
-> setDocument ( $rule );
2023-10-19 15:28:01 +00:00
$queueForEvents -> setParam ( 'ruleId' , $rule -> getId ());
2023-03-08 18:30:01 +00:00
$response -> noContent ();
});
2024-10-08 07:54:40 +00:00
App :: patch ( '/v1/proxy/rules/:ruleId/verification' )
-> desc ( 'Update rule verification status' )
2023-03-08 18:30:01 +00:00
-> groups ([ 'api' , 'proxy' ])
-> label ( 'scope' , 'rules.write' )
2023-03-10 12:20:24 +00:00
-> label ( 'event' , 'rules.[ruleId].update' )
-> label ( 'audits.event' , 'rule.update' )
-> label ( 'audits.resource' , 'rule/{response.$id}' )
2025-01-17 04:31:39 +00:00
-> label ( 'sdk' , new Method (
namespace : 'proxy' ,
2025-04-12 06:41:57 +00:00
group : null ,
2025-01-17 04:31:39 +00:00
name : 'updateRuleVerification' ,
2025-01-17 07:44:25 +00:00
description : '/docs/references/proxy/update-rule-verification.md' ,
2025-01-17 04:31:39 +00:00
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_PROXY_RULE ,
)
]
))
2023-03-08 18:30:01 +00:00
-> param ( 'ruleId' , '' , new UID (), 'Rule ID.' )
-> inject ( 'response' )
2023-10-19 15:28:01 +00:00
-> inject ( 'queueForCertificates' )
-> inject ( 'queueForEvents' )
2023-03-08 18:30:01 +00:00
-> inject ( 'project' )
2024-12-12 10:30:26 +00:00
-> inject ( 'dbForPlatform' )
2024-02-12 01:18:19 +00:00
-> inject ( 'log' )
2024-12-12 10:30:26 +00:00
-> action ( function ( string $ruleId , Response $response , Certificate $queueForCertificates , Event $queueForEvents , Document $project , Database $dbForPlatform , Log $log ) {
$rule = $dbForPlatform -> getDocument ( 'rules' , $ruleId );
2023-03-08 18:30:01 +00:00
if ( $rule -> isEmpty () || $rule -> getAttribute ( 'projectInternalId' ) !== $project -> getInternalId ()) {
throw new Exception ( Exception :: RULE_NOT_FOUND );
}
2024-04-01 11:02:47 +00:00
$target = new Domain ( System :: getEnv ( '_APP_DOMAIN_TARGET' , '' ));
2023-03-08 18:30:01 +00:00
if ( ! $target -> isKnown () || $target -> isTest ()) {
2023-07-28 08:27:16 +00:00
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Domain target must be configured as environment variable.' );
2023-03-08 18:30:01 +00:00
}
if ( $rule -> getAttribute ( 'verification' ) === true ) {
return $response -> dynamic ( $rule , Response :: MODEL_PROXY_RULE );
}
$validator = new CNAME ( $target -> get ()); // Verify Domain with DNS records
2023-03-10 07:42:52 +00:00
$domain = new Domain ( $rule -> getAttribute ( 'domain' , '' ));
2023-03-08 18:30:01 +00:00
2024-02-12 01:18:19 +00:00
$validationStart = \microtime ( true );
2023-03-10 07:42:52 +00:00
if ( ! $validator -> isValid ( $domain -> get ())) {
2024-02-12 01:18:19 +00:00
$log -> addExtra ( 'dnsTiming' , \strval ( \microtime ( true ) - $validationStart ));
$log -> addTag ( 'dnsDomain' , $domain -> get ());
$error = $validator -> getLogs ();
$log -> addExtra ( 'dnsResponse' , \is_array ( $error ) ? \json_encode ( $error ) : \strval ( $error ));
2023-03-08 18:30:01 +00:00
throw new Exception ( Exception :: RULE_VERIFICATION_FAILED );
}
2024-12-12 10:30:26 +00:00
$dbForPlatform -> updateDocument ( 'rules' , $rule -> getId (), $rule -> setAttribute ( 'status' , 'verifying' ));
2023-03-08 18:30:01 +00:00
// Issue a TLS certificate when domain is verified
2023-10-19 15:28:01 +00:00
$queueForCertificates
2023-03-08 18:30:01 +00:00
-> setDomain ( new Document ([
'domain' => $rule -> getAttribute ( 'domain' )
]))
-> trigger ();
2023-10-19 15:28:01 +00:00
$queueForEvents -> setParam ( 'ruleId' , $rule -> getId ());
2023-03-10 12:20:24 +00:00
2024-12-12 10:30:26 +00:00
$certificate = $dbForPlatform -> getDocument ( 'certificates' , $rule -> getAttribute ( 'certificateId' , '' ));
2023-03-13 13:35:34 +00:00
$rule -> setAttribute ( 'logs' , $certificate -> getAttribute ( 'logs' , '' ));
2023-03-08 18:30:01 +00:00
$response -> dynamic ( $rule , Response :: MODEL_PROXY_RULE );
2023-03-14 19:31:23 +00:00
});