1: <?php declare(strict_types=1);
2:
3: use Opcenter\Admin\Bootstrapper\Config;
4: use Opcenter\Admin\Settings\Setting;
5:
6: /**
7: * Copyright (C) Apis Networks, Inc - All Rights Reserved.
8: *
9: * Unauthorized copying of this file, via any medium, is
10: * strictly prohibited without consent. Any dissemination of
11: * material herein is prohibited.
12: *
13: * For licensing inquiries email <licensing@apisnetworks.com>
14: *
15: * Written by Matt Saladna <matt@apisnetworks.com>, August 2019
16: */
17: class Scope_Module extends Module_Skeleton
18: {
19: const SCOPE_CACHE_KEY = 'admin.syscfg';
20: protected $exportedFunctions = [
21: '*' => PRIVILEGE_ADMIN
22: ];
23:
24: public function __construct()
25: {
26: parent::__construct();
27: if (!AUTH_ADMIN_API) {
28: $this->exportedFunctions = ['*' => PRIVILEGE_NONE];
29: }
30: }
31:
32: /**
33: * Set server var
34: *
35: * @param string $name
36: * @param mixed $val
37: * @return bool
38: */
39: public function set(string $name, ...$val): bool
40: {
41: if (DEMO_ADMIN_LOCK && posix_getuid()) {
42: return error("Scopes may not be set in demo mode");
43: }
44:
45: if (!IS_CLI && posix_getuid()) {
46: if ($name === 'cp.restart') {
47: /**
48: * Special case. Restart triggers HTTP restart, which drops
49: * connection. Send asynchronously and hope for the best.
50: */
51: DataStream::get($this->getAuthContext())->
52: setOption(\apnscpObject::NO_WAIT)->query('scope_set', $name, ...$val);
53: return success('Panel restarting now. Service will be briefly interrupted.');
54: }
55: return $this->query('scope_set', $name, ...$val);
56: }
57: $c = Setting::className($name);
58: if (!$c) {
59: return $this->suggest($name);
60: }
61: if (!\array_key_exists(0, $val)) {
62: return error("`scope_set %s' requires one or more values. See `scope_info %s' for more information",
63: $name,
64: $name
65: );
66: }
67:
68: return (new $c)->set(...$val);
69: }
70:
71: /**
72: * Alias to list()
73: *
74: * @see list()
75: *
76: * @param string $filter
77: * @return array
78: */
79: public function l(string $filter = ''): array
80: {
81: return $this->list($filter);
82: }
83:
84: /**
85: * List available configuration settings
86: *
87: * @param string $filter filter using glob-style rules (e.g. apache.*
88: * @return array
89: */
90: public function list(string $filter = ''): array
91: {
92: $cache = Cache_Global::spawn();
93: $filter = str_replace(':', '.', $filter);
94:
95: if (is_debug() || false === ($c = $cache->get(self::SCOPE_CACHE_KEY))) {
96: $c = Opcenter\Admin\Settings\Setting::list();
97: asort($c);
98: $c = array_values($c);
99: $cache->set(self::SCOPE_CACHE_KEY, $c);
100: }
101:
102: return !$filter ? $c : array_values(array_filter($c, static function ($scope) use ($filter) {
103: return fnmatch($filter, $scope);
104: }));
105: }
106:
107: /**
108: * Alias for info()
109: *
110: * @see info()
111: * @param string $name
112: * @return array|null
113: */
114: public function i(string $name): ?array
115: {
116: return $this->info($name);
117: }
118:
119: /**
120: * Get Scope information
121: *
122: * @param string $name
123: * @return array|null
124: */
125: public function info(string $name): ?array
126: {
127: if (!IS_CLI && posix_getuid()) {
128: return $this->query('scope_info', $name);
129: }
130: $c = Setting::className($name);
131: if (!$c) {
132: return null;
133: }
134: $class = new $c;
135:
136: return [
137: 'info' => $class->getHelp(),
138: 'value' => $class->get(),
139: 'settings' => $class->getValues(),
140: 'default' => $class->getDefault()
141: ];
142: }
143:
144: /**
145: * Get server var
146: *
147: * @param string $name
148: * @param array $val optional subselection
149: * @return mixed
150: */
151: public function get(string $name, ...$val)
152: {
153: if (!IS_CLI && posix_getuid()) {
154: return $this->query('scope_get', $name, ...$val);
155: }
156: $c = Setting::className($name);
157: if (!$c) {
158: return $this->suggest($name);
159: }
160:
161: return (new $c)->get(...$val);
162: }
163:
164: /**
165: * Report changed defaults
166: *
167: * @param string|null $role role to inspect
168: * @param string $pattern glob-style pattern
169: * @return array
170: */
171: public function diff(?string $role = null, string $pattern = "*"): array
172: {
173: $cfg = new Config();
174:
175: $userConfiguration = $cfg->toArray();
176:
177: if ($role && !$cfg->exists($role)) {
178: error("Role %s does not exist", $role);
179: return [];
180: }
181:
182: $defaults = $role ? $cfg->loadRole($role) : $userConfiguration;
183:
184: $filtered = !$pattern ? $defaults : array_filter($defaults, static function ($var) use ($pattern) {
185: return fnmatch($pattern, $var);
186: }, ARRAY_FILTER_USE_KEY);
187:
188: return array_intersect_key($userConfiguration, $filtered);
189: }
190:
191: private function suggest(string $name): bool
192: {
193: $max = [99999, $name];
194: foreach ($this->list() as $scope) {
195: $score = levenshtein($name, $scope, 1, 2, 1);
196: if ($score < $max[0]) {
197: $max = [$score, $scope];
198: }
199: }
200:
201: return error("Scope `%(target)s' does not exist. Did you mean `%(suggest)s'?", [
202: 'target' => $name,
203: 'suggest' => $max[1]
204: ]);
205: }
206:
207: public function _housekeeping()
208: {
209: Cache_Global::spawn()->delete(self::SCOPE_CACHE_KEY);
210: }
211: }
212: