mirror of https://github.com/MISP/MISP
Merge branch 'develop' of https://github.com/MISP/MISP into develop
commit
eefb1fc32a
|
@ -213,12 +213,13 @@ $config = array(
|
|||
// Warning: The following is a 3rd party contribution and still untested (including security) by the MISP-project team.
|
||||
// Feel free to enable it and report back to us if you run into any issues.
|
||||
//
|
||||
// Uncomment the following to enable Kerberos authentication
|
||||
// Uncomment the following to enable Kerberos/LDAP authentication
|
||||
// needs PHP LDAP support enabled (e.g. compile flag --with-ldap or Debian package php5-ldap)
|
||||
/*
|
||||
'ApacheSecureAuth' => array( // Configuration for kerberos authentication
|
||||
'ApacheSecureAuth' => array( // Configuration for kerberos/LDAP authentication
|
||||
'apacheEnv' => 'REMOTE_USER', // If proxy variable = HTTP_REMOTE_USER, If BasicAuth ldap = PHP_AUTH_USER
|
||||
'ldapServer' => 'ldap://example.com', // FQDN or IP
|
||||
'ldapServer' => 'ldap://example.com', // FQDN or IP, ldap:// for LDAP or LDAP+STARTTLS, ldaps:// for LDAPS
|
||||
'starttls' => true, // true for STARTTLS, ignored for LDAPS
|
||||
'ldapProtocol' => 3,
|
||||
'ldapNetworkTimeout' => -1, // use -1 for unlimited network timeout
|
||||
'ldapReaderUser' => 'cn=userWithReadAccess,ou=users,dc=example,dc=com', // DN ou RDN LDAP with reader user right
|
||||
|
|
|
@ -29,6 +29,8 @@ class Ls22Shell extends AppShell
|
|||
|
||||
public function getOptionParser()
|
||||
{
|
||||
$this->stdout->styles('green', array('text' => 'green'));
|
||||
|
||||
$parser = parent::getOptionParser();
|
||||
$parser->addSubcommand('enableTaxonomy', [
|
||||
'help' => __('Enable a taxonomy with all its tags.'),
|
||||
|
@ -120,7 +122,7 @@ class Ls22Shell extends AppShell
|
|||
'short' => 's',
|
||||
'required' => true
|
||||
],
|
||||
'json' => [
|
||||
'value' => [
|
||||
'help' => 'The value to set for the given setting',
|
||||
'short' => 'v',
|
||||
'required' => true
|
||||
|
@ -448,6 +450,8 @@ class Ls22Shell extends AppShell
|
|||
}
|
||||
$time_range[] = $this->param['to'];
|
||||
}
|
||||
$event_extended_uuids = [];
|
||||
$event_uuid_per_org = [];
|
||||
foreach ($org_mapping as $org_name => $org_id) {
|
||||
$time_range = [];
|
||||
$params = [
|
||||
|
@ -462,6 +466,7 @@ class Ls22Shell extends AppShell
|
|||
$results[$org_name] = [
|
||||
'attribute_count' => 0,
|
||||
'object_count' => 0,
|
||||
'event_count' => count($events['response']),
|
||||
'connected_elements' => 0,
|
||||
'event_tags' => 0,
|
||||
'attribute_tags' => 0,
|
||||
|
@ -470,9 +475,16 @@ class Ls22Shell extends AppShell
|
|||
'attribute_attack' => 0,
|
||||
'attribute_other' => 0,
|
||||
'score' => 0,
|
||||
'warnings' => 0
|
||||
'warnings' => 0,
|
||||
'events_extended' => 0,
|
||||
'extending_events' => 0,
|
||||
];
|
||||
foreach ($events['response'] as $event) {
|
||||
$event_uuid_per_org[$event['Event']['uuid']] = $org_name;
|
||||
if (!empty($event['Event']['extends_uuid'])) {
|
||||
$event_extended_uuids[$org_name] = $event['Event']['extends_uuid'];
|
||||
}
|
||||
|
||||
if (!empty($event['Event']['Tag'])) {
|
||||
foreach ($event['Event']['Tag'] as $tag) {
|
||||
if (substr($tag['name'], 0, 32) === 'misp-galaxy:mitre-attack-pattern') {
|
||||
|
@ -505,7 +517,7 @@ class Ls22Shell extends AppShell
|
|||
}
|
||||
}
|
||||
if (!empty($attribute['warnings'])) {
|
||||
$result[$org_name]['warnings'] += 1;
|
||||
$results[$org_name]['warnings'] += 1;
|
||||
}
|
||||
}
|
||||
$results[$org_name]['attribute_count'] += count($event['Event']['Attribute']);
|
||||
|
@ -532,6 +544,18 @@ class Ls22Shell extends AppShell
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($event_extended_uuids as $orgc => $uuid) {
|
||||
$org_name = $event_uuid_per_org[$uuid];
|
||||
if ($orgc != $org_name) {
|
||||
// Add point for org extending another event
|
||||
$results[$orgc]['extending_events'] += 1;
|
||||
// Add point for org getting their event extended
|
||||
$results[$org_name]['events_extended'] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$scores = [];
|
||||
foreach ($results as $k => $result) {
|
||||
$totalCount = $result['attribute_count'] + $result['object_count'];
|
||||
|
@ -546,8 +570,9 @@ class Ls22Shell extends AppShell
|
|||
$results[$k]['metrics']['connectedness'] = 100 * ($result['connected_elements'] / ($result['attribute_count'] + $result['object_count']));
|
||||
$results[$k]['metrics']['attack_weight'] = 100 * (2*($result['attack']) + $result['attribute_attack']) / ($result['attribute_count'] + $result['object_count']);
|
||||
$results[$k]['metrics']['other_weight'] = 100 * (2*($result['other']) + $result['attribute_other']) / ($result['attribute_count'] + $result['object_count']);
|
||||
$results[$k]['metrics']['collaboration'] = 100 * ((2*$result['events_extended'] + $result['extending_events']) / $result['event_count']);
|
||||
}
|
||||
foreach (['connectedness', 'attack_weight', 'other_weight', 'warnings'] as $metric) {
|
||||
foreach (['connectedness', 'attack_weight', 'other_weight', 'warnings', 'collaboration'] as $metric) {
|
||||
if (empty($results[$k]['metrics'][$metric])) {
|
||||
$results[$k]['metrics'][$metric] = 0;
|
||||
}
|
||||
|
@ -559,13 +584,15 @@ class Ls22Shell extends AppShell
|
|||
20 * $results[$k]['metrics']['warnings'] +
|
||||
20 * $results[$k]['metrics']['connectedness'] +
|
||||
40 * $results[$k]['metrics']['attack_weight'] +
|
||||
20 * $results[$k]['metrics']['other_weight']
|
||||
10 * $results[$k]['metrics']['other_weight'] +
|
||||
10 * $results[$k]['metrics']['collaboration']
|
||||
) / 100;
|
||||
$scores[$k]['total'] = $results[$k]['score'];
|
||||
$scores[$k]['warnings'] = round(20 * $results[$k]['metrics']['warnings']);
|
||||
$scores[$k]['connectedness'] = round(20 * $results[$k]['metrics']['connectedness']);
|
||||
$scores[$k]['attack_weight'] = round(40 * $results[$k]['metrics']['attack_weight']);
|
||||
$scores[$k]['other_weight'] = round(20 * $results[$k]['metrics']['other_weight']);
|
||||
$scores[$k]['other_weight'] = round(10 * $results[$k]['metrics']['other_weight']);
|
||||
$scores[$k]['collaboration'] = round(10 * $results[$k]['metrics']['collaboration']);
|
||||
}
|
||||
arsort($scores, SORT_DESC);
|
||||
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
|
||||
|
@ -581,15 +608,17 @@ class Ls22Shell extends AppShell
|
|||
$score_string[1] = str_repeat('█', round($score['connectedness']/100));
|
||||
$score_string[2] = str_repeat('█', round($score['attack_weight']/100));
|
||||
$score_string[3] = str_repeat('█', round($score['other_weight']/100));
|
||||
$score_string[4] = str_repeat('█', round($score['collaboration']/100));
|
||||
$this->out(sprintf(
|
||||
'| %s | %s | %s |',
|
||||
str_pad($org, 10, ' ', STR_PAD_RIGHT),
|
||||
sprintf(
|
||||
'<error>%s</error><warning>%s</warning><question>%s</question><info>%s</info>%s',
|
||||
'<error>%s</error><warning>%s</warning><question>%s</question><info>%s</info><green>%s</green>%s',
|
||||
$score_string[0],
|
||||
$score_string[1],
|
||||
$score_string[2],
|
||||
$score_string[3],
|
||||
$score_string[4],
|
||||
str_repeat(' ', 100 - mb_strlen(implode('', $score_string)))
|
||||
),
|
||||
str_pad($score['total'] . '%', 8, ' ', STR_PAD_RIGHT)
|
||||
|
@ -602,6 +631,7 @@ class Ls22Shell extends AppShell
|
|||
'<warning>█: Connectedness</warning>',
|
||||
'<question>█: ATT&CK context</question>',
|
||||
'<info>█: Other Context</info>',
|
||||
'<green>█: Collaboration</green>',
|
||||
str_repeat(' ', 52)
|
||||
), 1, Shell::NORMAL);
|
||||
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
|
||||
|
|
|
@ -310,7 +310,7 @@ class AppController extends Controller
|
|||
$this->__accessMonitor($user);
|
||||
|
||||
} else {
|
||||
$preAuthActions = array('login', 'register', 'getGpgPublicKey');
|
||||
$preAuthActions = array('login', 'register', 'getGpgPublicKey', 'logout401');
|
||||
if (!empty(Configure::read('Security.email_otp_enabled'))) {
|
||||
$preAuthActions[] = 'email_otp';
|
||||
}
|
||||
|
|
|
@ -750,6 +750,7 @@ class ACLComponent extends Component
|
|||
'initiatePasswordReset' => ['AND' => ['perm_admin', 'password_change_enabled']],
|
||||
'login' => array('*'),
|
||||
'logout' => array('*'),
|
||||
'logout401' => array('*'),
|
||||
'notificationSettings' => ['*'],
|
||||
'register' => array('*'),
|
||||
'registrations' => array(),
|
||||
|
|
|
@ -38,10 +38,10 @@ class ApacheAuthenticate extends BaseAuthenticate
|
|||
}
|
||||
return $returnCode;
|
||||
}
|
||||
|
||||
|
||||
private function getEmailAddress($ldapEmailField, $ldapUserData)
|
||||
{
|
||||
// return the email address of an LDAP user if one of the fields in $ldapEmaiLField exists
|
||||
// return the email address of an LDAP user if one of the fields in $ldapEmaiLField exists
|
||||
foreach($ldapEmailField as $field) {
|
||||
if (isset($ldapUserData[0][$field][0])) {
|
||||
return $ldapUserData[0][$field][0];
|
||||
|
@ -73,6 +73,14 @@ class ApacheAuthenticate extends BaseAuthenticate
|
|||
ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, Configure::read('ApacheSecureAuth.ldapProtocol'));
|
||||
ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, Configure::read('ApacheSecureAuth.ldapAllowReferrals', true));
|
||||
|
||||
if (Configure::read('ApacheSecureAuth.starttls', false) == true) {
|
||||
# Default is false, sine STARTTLS support is a new feature
|
||||
# Ignored on ldaps://, but can trigger problems for orgs
|
||||
# using unencrypted LDAP. Loose comparison allows users to
|
||||
# use # true / 1 / etc.
|
||||
ldap_start_tls($ldapconn);
|
||||
}
|
||||
|
||||
if ($ldapconn) {
|
||||
// LDAP bind
|
||||
$ldapbind = ldap_bind($ldapconn, $ldaprdn, $ldappass);
|
||||
|
@ -105,7 +113,6 @@ class ApacheAuthenticate extends BaseAuthenticate
|
|||
} else {
|
||||
die("User not found in LDAP");
|
||||
}
|
||||
|
||||
// close LDAP connection
|
||||
ldap_close($ldapconn);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class UsersController extends AppController
|
|||
parent::beforeFilter();
|
||||
|
||||
// what pages are allowed for non-logged-in users
|
||||
$allowedActions = array('login', 'logout', 'getGpgPublicKey');
|
||||
$allowedActions = array('login', 'logout', 'getGpgPublicKey', 'logout401');
|
||||
if(!empty(Configure::read('Security.email_otp_enabled'))) {
|
||||
$allowedActions[] = 'email_otp';
|
||||
}
|
||||
|
@ -2896,4 +2896,11 @@ class UsersController extends AppController
|
|||
$this->render('/genericTemplates/confirm');
|
||||
}
|
||||
}
|
||||
public function logout401() {
|
||||
# You should read the documentation in docs/CONFIG.ApacheSecureAuth.md
|
||||
# before using this endpoint. It is not useful without webserver config
|
||||
# changes.
|
||||
# To use this, set Plugin.CustomAuth_custom_logout to /users/logout401
|
||||
$this->response->statusCode(401);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,14 @@ class Module_splunk_hec_export extends Module_webhook
|
|||
'type' => 'input',
|
||||
'placeholder' => '00000000-0000-0000-000000000000'
|
||||
],
|
||||
[
|
||||
'id' => 'source_type',
|
||||
'label' => __('Source Type'),
|
||||
'type' => 'select',
|
||||
'type' => 'input',
|
||||
'default' => '',
|
||||
'placeholder' => 'misp:event'
|
||||
],
|
||||
[
|
||||
'id' => 'event_per_attribute',
|
||||
'label' => __('Create one Splunk Event per Attribute'),
|
||||
|
@ -110,10 +118,10 @@ class Module_splunk_hec_export extends Module_webhook
|
|||
$splunk_events = $extracted_events;
|
||||
}
|
||||
|
||||
return $this->sendToSplunk($splunk_events, $params['hec_token']['value'], $params['url']['value']);
|
||||
return $this->sendToSplunk($splunk_events, $params['hec_token']['value'], $params['url']['value'], $params['source_type']['value']);
|
||||
}
|
||||
|
||||
protected function sendToSplunk(array $splunk_events, $token, $url): bool
|
||||
protected function sendToSplunk(array $splunk_events, $token, $url, $source_type): bool
|
||||
{
|
||||
foreach ($splunk_events as $splunk_event) {
|
||||
try {
|
||||
|
@ -123,12 +131,20 @@ class Module_splunk_hec_export extends Module_webhook
|
|||
$serverConfig = [
|
||||
'Server' => ['self_signed' => empty($params['verify_tls']['value'])]
|
||||
];
|
||||
|
||||
$hec_event = [
|
||||
'event' => $splunk_event
|
||||
];
|
||||
if (!empty($source_type)) {
|
||||
$hec_event['sourcetype'] = $source_type;
|
||||
}
|
||||
|
||||
$response = $this->doRequest(
|
||||
$url,
|
||||
'json',
|
||||
$splunk_event,
|
||||
$hec_event,
|
||||
$headers,
|
||||
$serverConfig,
|
||||
$serverConfig
|
||||
);
|
||||
if (!$response->isOk()) {
|
||||
if ($response->code === 403 || $response->code === 401) {
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<div style="width:100%;">
|
||||
<script>
|
||||
// Chrome/Edge will log you out once it sees the HTTP401.
|
||||
// We need to be extra hacky to properly log out on i.e. Firefox.
|
||||
<?php
|
||||
$split_baseurl = array();
|
||||
# We split the baseurl, since we need to add data between the
|
||||
# schema and hostname. We *could* use parse_url here, but then
|
||||
# we would need a lot of code to rebuild it
|
||||
if (preg_match("/(https?:\/\/)(.*)/", $baseurl, $split_baseurl)):
|
||||
?>
|
||||
// The following call has to be done in the users browser to properly make
|
||||
// Firefox forget HTTP Basic auth credentials. The login with user set to
|
||||
// "logout" will be captured by webserver configuration, and not be sendt
|
||||
// to LDAP, but will invalidate the old, cached login in the browser.
|
||||
// If this is not working, make sure you have configured the webserver
|
||||
// as described in docs/CONFIG.ApacheSecureAuth.md Logout => LDAP => Option 2.
|
||||
let logoutxhr401 = new XMLHttpRequest()
|
||||
logoutxhr401.open("GET", "<?php echo $split_baseurl[1]; ?>logout:@<?php echo $split_baseurl[2]; ?>/users/login")
|
||||
logoutxhr401.send()
|
||||
<?php
|
||||
else:
|
||||
echo "// We failed to parse baseurl";
|
||||
endif;
|
||||
?>
|
||||
</script>
|
||||
<table style="margin-left:auto;margin-right:auto;">
|
||||
<tr>
|
||||
<td style="width:460px">
|
||||
<br /><br />
|
||||
<div>
|
||||
<?php if (Configure::read('MISP.main_logo') && file_exists(APP . '/webroot/img/custom/' . Configure::read('MISP.main_logo'))): ?>
|
||||
<img src="<?php echo $baseurl?>/img/custom/<?php echo h(Configure::read('MISP.main_logo'));?>" style=" display:block; margin-left: auto; margin-right: auto;" />
|
||||
<?php else: ?>
|
||||
<img src="<?php echo $baseurl?>/img/misp-logo-s-u.png" style="display:block; margin-left: auto; margin-right: auto;"/>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
<br>
|
||||
<?php
|
||||
echo sprintf('<h5>%s</h5>',
|
||||
__('You have been successfully logged out.'),
|
||||
);
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
|
@ -1 +1 @@
|
|||
Subproject commit 57f3e462733bd7dbfea3c123c7e2ba507e721c0b
|
||||
Subproject commit 963a389216ae0c1a829a2e3fdb80e4cb3fe0650c
|
|
@ -715,7 +715,7 @@ class EventGraph {
|
|||
if ( node.node_type == 'object' ) {
|
||||
var group = 'object';
|
||||
var label = dataHandler.generate_label(node);
|
||||
var labelHtml = label + '</br><i>' + escapeHtml(node.comment) + '</i>'
|
||||
var labelHtml = escapeHtml(label) + '</br><i>' + escapeHtml(node.comment) + '</i>'
|
||||
label += ' ' + escapeHtml(node.comment)
|
||||
var striped_value = that.strip_text_value(label);
|
||||
node_conf = {
|
||||
|
@ -742,7 +742,7 @@ class EventGraph {
|
|||
id: node.id,
|
||||
uuid: node.uuid,
|
||||
label: label,
|
||||
title: label,
|
||||
title: escapeHtml(label),
|
||||
group: group,
|
||||
mass: 20,
|
||||
color: {
|
||||
|
@ -766,15 +766,15 @@ class EventGraph {
|
|||
node_conf = {
|
||||
id: node.id,
|
||||
label: striped_value,
|
||||
title: label,
|
||||
title: escapeHtml(label),
|
||||
group: group
|
||||
};
|
||||
dataHandler.mapping_value_to_nodeID.set(label, node.id);
|
||||
} else {
|
||||
group = 'attribute';
|
||||
label = node.type + ': ' + node.label;
|
||||
label = escapeHtml(node.type) + ': ' + node.label;
|
||||
label += ' ' + escapeHtml(node.comment)
|
||||
var labelHtml = label + '</br><i>' + escapeHtml(node.comment) + '</i>'
|
||||
var labelHtml = escapeHtml(label) + '</br><i>' + escapeHtml(node.comment) + '</i>'
|
||||
var striped_value = that.strip_text_value(label);
|
||||
node_conf = {
|
||||
id: node.id,
|
||||
|
@ -822,7 +822,7 @@ class EventGraph {
|
|||
from: rel.from,
|
||||
to: rel.to,
|
||||
label: rel.type,
|
||||
title: rel.comment,
|
||||
title: escapeHtml(rel.comment),
|
||||
color: {
|
||||
opacity: 1.0,
|
||||
}
|
||||
|
@ -1053,7 +1053,7 @@ class EventGraph {
|
|||
x: parent_pos.x,
|
||||
y: parent_pos.y,
|
||||
label: attr.object_relation + ': ' + striped_value,
|
||||
title: attr.object_relation + ': ' + attr.value,
|
||||
title: escapeHtml(attr.object_relation) + ': ' + escapeHtml(attr.value),
|
||||
group: 'obj_relation',
|
||||
color: {
|
||||
background: parent_color
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
# ApacheSecureAuth
|
||||
<!---
|
||||
Ugly diff hack to render text as red using Github's markdown parser
|
||||
-->
|
||||
```diff
|
||||
- BE AWARE: The ApacheSecureAuth / LDAP login code is a
|
||||
- 3rd party contribution and untested (including security)
|
||||
- by the MISP-project team.
|
||||
```
|
||||
|
||||
However, you are free to enable it and report back to the developers if you run into any issues.
|
||||
|
||||
## Configuration
|
||||
### MISP configuration
|
||||
See the commented sections of [config.default.php](../app/Config/config.default.php) for an example of the MISP configuration variables that the ApacheSecureAuth module requires.
|
||||
|
||||
### Webserver configuration
|
||||
`TODO`
|
||||
|
||||
## Logout
|
||||
### Kerberos
|
||||
If you have configured you webserver to authenticate users using Kerberos/SPNEGO/Negotiate,
|
||||
there is no "log out", other than invalidating the user's Kerberos tickets.
|
||||
You can hide the GUI "Log out" link by setting `Plugin.CustomAuth_disable_logout` to `true`.
|
||||
|
||||
If you just want to log in as another user, you should be able to do this in an ingonito window.
|
||||
Most browser will not allow Kerberos/SPNEGO/Negotiate authentification when in ingognito mode,
|
||||
and i.e. Apache will fall back to having the user input his credentials in a HTTP Basic Auth
|
||||
popup, for then to authenticate the user with AD using these credentials.
|
||||
|
||||
### LDAP
|
||||
If you are capturing the user's credentials using HTTP Basic Auth, it can be difficult to make
|
||||
the browser forget these.
|
||||
There is no common or properly defined way of "logging out" after logging in with HTTP Basic Auth.
|
||||
|
||||
If the user presses the GUI "Log out" link, this can result in a logout-login loop, where the user
|
||||
is logged out, but then immediately loggged back in by means of the browsers cached HTTP Basic Auth
|
||||
credentials. This can be observed when a user presses "Log out", for then to be returned to the
|
||||
events view with two flash messages - one about a successful logout, and one "Welcome back" login-message.
|
||||
|
||||
There are two options to improve the user experience:
|
||||
|
||||
#### Option 1 (simple): Hide GUI "Log Out"
|
||||
As with Kerberos, the admin can hide the GUI "Log out" link by setting `Plugin.CustomAuth_disable_logout` to `true`.
|
||||
This is sufficient for many organizations.
|
||||
|
||||
#### Options 2 (complicated): Trick the browser into forgetting cached HTTP Basic Auth credentials
|
||||
The internal path `/users/logout401` in combination with webserver configuration
|
||||
can trick most browsers into forgetting cached HTTP Basic Auth credentials.
|
||||
|
||||
1. Set `Plugin.CustomAuth_custom_logout` to the internal path `/users/logout401`
|
||||
2. Modify your webserver configuration. Below is an example for Apache2
|
||||
|
||||
````
|
||||
# Only requiring LDAP auth for the /users/login path will improve the user experience.
|
||||
#<Location "/">
|
||||
<Location "/users/login">
|
||||
# This block will catch the Ajax logout from /users/logout401 that is required for
|
||||
# some browsers, i.e. Firefox. 'Basic bG9nb3V0Og==' equals 'Basic logout:' as
|
||||
# used buy the `/users/logout401` endpoint. This will prevent extraneous failed
|
||||
# logins a "logout" user on the LDAP server.
|
||||
<If "-n %{HTTP:Authorization} && %{HTTP:Authorization} == 'Basic bG9nb3V0Og==' ">
|
||||
AuthType Basic
|
||||
AuthName "MISP" # Must be same as in LDAP block
|
||||
AuthUserFile /dev/null
|
||||
Require valid-user
|
||||
</If>
|
||||
AuthType Basic
|
||||
AuthName "MISP"
|
||||
AuthBasicProvider ldap
|
||||
...
|
||||
</Else>
|
||||
</Location>
|
||||
````
|
Loading…
Reference in New Issue