more unit tests

This commit is contained in:
anhefti 2020-01-14 14:24:19 +01:00
parent 45fb04955b
commit b5c9057589
15 changed files with 1362 additions and 83 deletions

View file

@ -1,40 +0,0 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class DeactivateExamConfig extends RestCall<EntityProcessingReport> {
public DeactivateExamConfig() {
super(new TypeKey<>(
CallType.ACTIVATION_DEACTIVATE,
EntityType.CONFIGURATION_NODE,
new TypeReference<EntityProcessingReport>() {
}),
HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED,
API.CONFIGURATION_NODE_ENDPOINT + API.PATH_VAR_INACTIVE);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -17,24 +17,24 @@ import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class ActivateExamConfig extends RestCall<EntityProcessingReport> {
public class GetConfigurationTableValues extends RestCall<ConfigurationTableValues> {
public ActivateExamConfig() {
public GetConfigurationTableValues() {
super(new TypeKey<>(
CallType.ACTIVATION_ACTIVATE,
EntityType.CONFIGURATION_NODE,
new TypeReference<EntityProcessingReport>() {
CallType.GET_SINGLE,
EntityType.CONFIGURATION_VALUE,
new TypeReference<ConfigurationTableValues>() {
}),
HttpMethod.POST,
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.CONFIGURATION_NODE_ENDPOINT + API.PATH_VAR_ACTIVE);
API.CONFIGURATION_VALUE_ENDPOINT + API.CONFIGURATION_TABLE_VALUE_PATH_SEGMENT);
}
}

View file

@ -34,7 +34,9 @@ public class GetFollowupConfiguration extends RestCall<Configuration> {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.MODEL_ID_VAR_PATH_SEGMENT + API.CONFIGURATION_FOLLOWUP_PATH_SEGMENT);
API.CONFIGURATION_NODE_ENDPOINT +
API.MODEL_ID_VAR_PATH_SEGMENT +
API.CONFIGURATION_FOLLOWUP_PATH_SEGMENT);
}
}

View file

@ -72,7 +72,7 @@ public interface AttributeValueConverter {
*
* @param attribute
* @return the SEB configuration attribute name */
default String extractName(final ConfigurationAttribute attribute) {
static String extractName(final ConfigurationAttribute attribute) {
final int lastIndexOf = attribute.name.lastIndexOf('.');
if (lastIndexOf > 0) {
return attribute.name.substring(lastIndexOf + 1, attribute.name.length());

View file

@ -91,7 +91,7 @@ public class ArrayOfStringConverter implements AttributeValueConverter {
final StringBuilder sb = new StringBuilder();
sb.append(String.format(
(xml) ? XML_TEMPLATE : JSON_TEMPLATE,
extractName(attribute)));
AttributeValueConverter.extractName(attribute)));
for (final String v : values) {
sb.append(String.format(
@ -112,7 +112,7 @@ public class ArrayOfStringConverter implements AttributeValueConverter {
} else {
out.write(Utils.toByteArray(String.format(
(xml) ? XML_TEMPLATE_EMPTY : JSON_TEMPLATE_EMPTY,
extractName(attribute))));
AttributeValueConverter.extractName(attribute))));
}
}

View file

@ -77,7 +77,7 @@ public class BooleanConverter implements AttributeValueConverter {
out.write(Utils.toByteArray(
String.format(
template,
extractName(attribute),
AttributeValueConverter.extractName(attribute),
(value != null && value.value != null) ? value.value : defaultValue)));
}

View file

@ -89,7 +89,7 @@ public class InlineTableConverter implements AttributeValueConverter {
out.write(Utils.toByteArray(String.format(
(xml) ? XML_KEY_TEMPLATE : JSON_KEY_TEMPLATE,
extractName(attribute))));
AttributeValueConverter.extractName(attribute))));
if (StringUtils.isBlank(value.value)) {
out.write((xml) ? XML_EMPTY_ARRAY : JSON_EMPTY_ARRAY);

View file

@ -93,7 +93,7 @@ public class IntegerConverter implements AttributeValueConverter {
out.write(Utils.toByteArray(String.format(
template,
extractName(attribute),
AttributeValueConverter.extractName(attribute),
intVal)));
}

View file

@ -88,12 +88,12 @@ public class StringConverter implements AttributeValueConverter {
if (StringUtils.isNotBlank(val)) {
out.write(Utils.toByteArray(String.format(
template,
extractName(attribute),
AttributeValueConverter.extractName(attribute),
val)));
} else {
out.write(Utils.toByteArray(String.format(
emptyTemplate,
extractName(attribute))));
AttributeValueConverter.extractName(attribute))));
}
}

View file

@ -121,7 +121,7 @@ public class TableConverter implements AttributeValueConverter {
out.write(Utils.toByteArray(String.format(
(xml) ? XML_KEY_TEMPLATE : JSON_KEY_TEMPLATE,
extractName(attribute))));
AttributeValueConverter.extractName(attribute))));
if (noValues) {
out.write((xml) ? XML_EMPTY_ARRAY : JSON_EMPTY_ARRAY);
@ -136,7 +136,7 @@ public class TableConverter implements AttributeValueConverter {
} else {
out.write(Utils.toByteArray(String.format(
(xml) ? XML_KEY_TEMPLATE : JSON_KEY_TEMPLATE,
extractName(attribute))));
AttributeValueConverter.extractName(attribute))));
}
}

View file

@ -96,7 +96,7 @@ public class ConfigurationValueController extends EntityController<Configuration
method = RequestMethod.GET,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ConfigurationTableValues getTableValueBy(
public ConfigurationTableValues getTableValue(
@RequestParam(
name = Domain.CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID,
required = true) final Long attributeId,

View file

@ -18,16 +18,22 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.codec.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.tomcat.util.buf.StringUtils;
import org.joda.time.DateTimeZone;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.util.StreamUtils;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
@ -48,7 +54,10 @@ import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues.TableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View;
@ -90,10 +99,10 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.GetClientConfigPage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.NewClientConfig;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.clientconfig.SaveClientConfig;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ActivateExamConfig;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.DeactivateExamConfig;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ExportPlainXML;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigAttributes;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationPage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationTableValues;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationValuePage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationValues;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurations;
@ -103,6 +112,8 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.Ge
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetOrientationPage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetOrientations;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetViewList;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ImportExamConfigOnExistingConfig;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.ImportNewExamConfig;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.NewExamConfig;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigHistory;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigTableValues;
@ -973,6 +984,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
@Order(10)
// *************************************
// Use Case 10: Login as examAdmin2 and create a new SEB Exam Configuration
// - test creation
// - save configuration in history
// - change some attribute
// - process an undo
@ -986,15 +998,13 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
new GetConfigurationPage(),
new GetConfigurations(),
new SaveExamConfigHistory(),
new ExportExamConfig(),
new GetFollowupConfiguration(),
new GetConfigurationTableValues(),
new SebExamConfigUndo(),
new SaveExamConfigValue(),
new SaveExamConfigTableValues(),
new GetConfigurationValuePage(),
new GetConfigurationValues(),
new ActivateExamConfig(),
new DeactivateExamConfig(),
new GetConfigAttributes(),
new GetUserAccountNames());
// get user id
@ -1063,6 +1073,13 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
List<ConfigurationValue> values = valuesResponse.get();
assertFalse(values.isEmpty());
UsecaseTestUtils.testProhibitedProcessesInit(
followup.getModelId(),
restService);
UsecaseTestUtils.testPermittedProcessesInit(
followup.getModelId(),
restService);
// update a value -- grab first
final ConfigurationValue value = values.get(0);
ConfigurationValue newValue = new ConfigurationValue(
@ -1170,29 +1187,22 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
@Test
@Order(11)
// *************************************
// Use Case 11: Login as examAdmin2 and create a new SEB Exam Configuration
// - table value add, delete, modify
// - export
// Use Case 11: Login as examAdmin2 and get newly created exam configuration
// - get permitted processes table values
// - modify permitted processes table values
// - save permitted processes table values
// - check save OK
public void testUsecase11() throws IOException {
final RestServiceImpl restService = createRestServiceForUser(
"examAdmin2",
"examAdmin2",
new NewExamConfig(),
new GetExamConfigNode(),
new GetConfigAttributes(),
new GetExamConfigNodePage(),
new GetConfigurationPage(),
new GetConfigurations(),
new GetConfigurationPage(),
new SaveExamConfigHistory(),
new ExportExamConfig(),
new GetFollowupConfiguration(),
new SebExamConfigUndo(),
new SaveExamConfigValue(),
new SaveExamConfigTableValues(),
new GetConfigurationValuePage(),
new GetConfigurationValues(),
new ActivateExamConfig(),
new DeactivateExamConfig(),
new GetUserAccountNames());
new GetConfigurationTableValues(),
new SaveExamConfigTableValues());
// get configuration page
final Result<Page<ConfigurationNode>> pageResponse = restService
@ -1206,6 +1216,208 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
final ConfigurationNode configurationNode = page.content.get(0);
assertEquals("New Exam Config", configurationNode.name);
// get follow-up configuration
final Result<List<Configuration>> configHistoryResponse = restService
.getBuilder(GetConfigurations.class)
.withQueryParam(Configuration.FILTER_ATTR_CONFIGURATION_NODE_ID, configurationNode.getModelId())
.call();
final List<Configuration> configHistory = configHistoryResponse.get();
final Configuration followup = configHistory
.stream()
.filter(config -> BooleanUtils.isTrue(config.followup))
.findFirst()
.orElseThrow(() -> new RuntimeException("Followup Node not found"));
final ConfigurationTableValues permittedProcessValues = UsecaseTestUtils.getTableValues(
"73",
followup.getModelId(),
restService);
assertNotNull(permittedProcessValues);
assertFalse(permittedProcessValues.values.isEmpty());
// get all configuration attributes
final Map<Long, ConfigurationAttribute> attributes = restService
.getBuilder(GetConfigAttributes.class)
.call()
.getOrThrow()
.stream()
.collect(Collectors.toMap(attr -> attr.id, Function.identity()));
// create new row by copy the values
final List<TableValue> newTableValues = permittedProcessValues.values
.stream()
.map(attr -> new TableValue(attr.attributeId, 1, attributes.get(attr.attributeId).defaultValue))
.collect(Collectors.toList());
newTableValues.addAll(permittedProcessValues.values);
// test institutional integrity violation
try {
final ConfigurationTableValues newTableValue = new ConfigurationTableValues(
1000L,
followup.id,
73L,
newTableValues);
restService.getBuilder(SaveExamConfigTableValues.class)
.withBody(newTableValue)
.call()
.getOrThrow();
fail("Exception expected here");
} catch (final Exception e) {
assertEquals("Unexpected error while rest call", e.getMessage());
}
// test follow-up integrity violation
try {
final ConfigurationTableValues newTableValue = new ConfigurationTableValues(
configHistory.get(0).id,
followup.id,
73L,
newTableValues);
restService.getBuilder(SaveExamConfigTableValues.class)
.withBody(newTableValue)
.call()
.getOrThrow();
fail("Exception expected here");
} catch (final Exception e) {
assertEquals("Unexpected error while rest call", e.getMessage());
}
final ConfigurationTableValues newTableValue = new ConfigurationTableValues(
followup.institutionId,
followup.id,
73L,
newTableValues);
final ConfigurationTableValues savedValues = restService.getBuilder(SaveExamConfigTableValues.class)
.withBody(newTableValue)
.call()
.getOrThrow();
assertNotNull(savedValues);
assertFalse(savedValues.values.isEmpty());
assertTrue(savedValues.values.size() == newTableValues.size());
}
@Test
@Order(12)
// *************************************
// Use Case 12: Login as examAdmin2 and use newly created configuration
// - get follow-up configuration by API
// - import
// - export
public void testUsecase12() throws IOException {
final RestServiceImpl restService = createRestServiceForUser(
"examAdmin2",
"examAdmin2",
new GetConfigAttributes(),
new GetExamConfigNodePage(),
new SaveExamConfigHistory(),
new ExportExamConfig(),
new ImportNewExamConfig(),
new ImportExamConfigOnExistingConfig(),
new ExportPlainXML(),
new GetFollowupConfiguration());
// get all configuration attributes
final Collection<ConfigurationAttribute> attributes = restService
.getBuilder(GetConfigAttributes.class)
.call()
.getOrThrow()
.stream()
.collect(Collectors.toList());
// get configuration page
final Result<Page<ConfigurationNode>> pageResponse = restService
.getBuilder(GetExamConfigNodePage.class)
.call();
assertNotNull(pageResponse);
assertFalse(pageResponse.hasError());
final Page<ConfigurationNode> page = pageResponse.get();
assertFalse(page.content.isEmpty());
final ConfigurationNode configurationNode = page.content.get(0);
assertEquals("New Exam Config", configurationNode.name);
final Configuration followup = restService
.getBuilder(GetFollowupConfiguration.class)
.withURIVariable(API.PARAM_MODEL_ID, configurationNode.getModelId())
.call()
.getOrThrow();
assertNotNull(followup);
assertTrue(followup.followup);
// export1
final InputStream input = restService
.getBuilder(ExportPlainXML.class)
.withURIVariable(API.PARAM_MODEL_ID, configurationNode.getModelId())
.call()
.getOrThrow();
final String xmlString = StreamUtils.copyToString(input, Charsets.UTF_8);
assertNotNull(xmlString);
for (final ConfigurationAttribute attribute : attributes) {
if (attribute.name.contains(".") || attribute.name.equals("kioskMode")) {
continue;
}
if (!xmlString.contains(attribute.name)) {
fail("missing attribute: " + attribute.name);
}
}
// import plain config
InputStream inputStream = new ClassPathResource("importTest.seb").getInputStream();
Configuration importedConfig = restService
.getBuilder(ImportNewExamConfig.class)
.withBody(inputStream)
.withHeader(Domain.CONFIGURATION_NODE.ATTR_NAME, "Imported Test Configuration")
.call()
.getOrThrow();
assertNotNull(importedConfig);
// import with the same name should cause an exception
try {
restService
.getBuilder(ImportNewExamConfig.class)
.withBody(inputStream)
.withHeader(Domain.CONFIGURATION_NODE.ATTR_NAME, "Imported Test Configuration")
.withHeader(API.IMPORT_PASSWORD_ATTR_NAME, "123")
.call()
.getOrThrow();
fail("Expecting an exception here");
} catch (final Exception e) {
}
// import encrypted config with password encryption
inputStream = new ClassPathResource("importTest_123.seb").getInputStream();
importedConfig = restService
.getBuilder(ImportNewExamConfig.class)
.withBody(inputStream)
.withHeader(Domain.CONFIGURATION_NODE.ATTR_NAME, "Imported Encrypted Test Configuration")
.withHeader(API.IMPORT_PASSWORD_ATTR_NAME, "123")
.call()
.getOrThrow();
assertNotNull(importedConfig);
// import config within existing configuration
inputStream = new ClassPathResource("importTest.seb").getInputStream();
importedConfig = restService
.getBuilder(ImportExamConfigOnExistingConfig.class)
.withBody(inputStream)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(importedConfig.getConfigurationNodeId()))
.call()
.getOrThrow();
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui.integration;
import static org.junit.Assert.*;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.tomcat.util.buf.StringUtils;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestServiceImpl;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigAttributes;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationTableValues;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.init.XMLAttributeLoader;
public abstract class UsecaseTestUtils {
static ConfigurationTableValues testProhibitedProcessesInit(
final String configId,
final RestServiceImpl restService) {
final ConfigurationTableValues tableValues = getTableValues("93", configId, restService);
assertNotNull(tableValues);
assertFalse(tableValues.values.isEmpty());
final String names = StringUtils.join(
tableValues.values
.stream()
.filter(attr -> attr.attributeId == 98)
.map(attr -> attr.value)
.sorted()
.collect(Collectors.toList()),
Constants.LIST_SEPARATOR_CHAR);
// get all configuration attributes
final Map<String, ConfigurationAttribute> attributes = restService
.getBuilder(GetConfigAttributes.class)
.call()
.getOrThrow()
.stream()
.collect(Collectors.toMap(attr -> attr.name, Function.identity()));
final XMLAttributeLoader xmlAttributeLoader = new XMLAttributeLoader();
final String configuraedNames = StringUtils.join(xmlAttributeLoader.loadFromXML(
1L,
Long.parseLong(configId),
attrName -> attributes.get(attrName),
"config/initialProhibitedProcesses.xml")
.stream()
.filter(attr -> attr.attributeId == 98)
.map(attr -> attr.value)
.sorted()
.collect(Collectors.toList()),
Constants.LIST_SEPARATOR_CHAR);
assertEquals(configuraedNames, names);
return tableValues;
}
static ConfigurationTableValues getTableValues(
final String attributeId,
final String configId,
final RestServiceImpl restService) {
final ConfigurationTableValues tableValues = restService.getBuilder(GetConfigurationTableValues.class)
.withQueryParam(
Domain.CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID,
attributeId)
.withQueryParam(
Domain.CONFIGURATION_VALUE.ATTR_CONFIGURATION_ID,
configId)
.call()
.getOrThrow();
return tableValues;
}
static ConfigurationTableValues testPermittedProcessesInit(
final String configId,
final RestServiceImpl restService) {
final ConfigurationTableValues tableValues = getTableValues("73", configId, restService);
assertNotNull(tableValues);
assertFalse(tableValues.values.isEmpty());
final String names = StringUtils.join(
tableValues.values
.stream()
.filter(attr -> attr.attributeId == 76)
.map(attr -> attr.value)
.sorted()
.collect(Collectors.toList()),
Constants.LIST_SEPARATOR_CHAR);
// get all configuration attributes
final Map<String, ConfigurationAttribute> attributes = restService
.getBuilder(GetConfigAttributes.class)
.call()
.getOrThrow()
.stream()
.collect(Collectors.toMap(attr -> attr.name, Function.identity()));
final XMLAttributeLoader xmlAttributeLoader = new XMLAttributeLoader();
final String configuraedNames = StringUtils.join(xmlAttributeLoader.loadFromXML(
1L,
Long.parseLong(configId),
attrName -> attributes.get(attrName),
"config/initialPermittedProcesses.xml")
.stream()
.filter(attr -> attr.attributeId == 76)
.map(attr -> attr.value)
.sorted()
.collect(Collectors.toList()),
Constants.LIST_SEPARATOR_CHAR);
assertEquals(configuraedNames, names);
return tableValues;
}
}

View file

@ -0,0 +1,973 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>URLFilterEnable</key>
<false />
<key>URLFilterEnableContentFilter</key>
<false />
<key>URLFilterIgnoreList</key>
<array></array>
<key>URLFilterMessage</key>
<integer>0</integer>
<key>URLFilterRules</key>
<array>
<dict>
<key>active</key>
<true />
<key>regex</key>
<true />
<key>expression</key>
<string>jrtjrtzj</string>
<key>action</key>
<integer>0</integer>
</dict>
<dict>
<key>active</key>
<true />
<key>regex</key>
<false />
<key>expression</key>
<string>rtjrtj</string>
<key>action</key>
<integer>1</integer>
</dict>
</array>
<key>allowBrowsingBackForward</key>
<true />
<key>allowDictation</key>
<false />
<key>allowDictionaryLookup</key>
<false />
<key>allowDisplayMirroring</key>
<true />
<key>allowDownUploads</key>
<false />
<key>allowFlashFullscreen</key>
<false />
<key>allowPDFPlugIn</key>
<false />
<key>allowPreferencesWindow</key>
<true />
<key>allowQuit</key>
<true />
<key>allowScreenSharing</key>
<false />
<key>allowSiri</key>
<false />
<key>allowSpellCheck</key>
<false />
<key>allowSwitchToApplications</key>
<false />
<key>allowUserSwitching</key>
<true />
<key>allowVideoCapture</key>
<false />
<key>allowVirtualMachine</key>
<false />
<key>allowWLAN</key>
<false />
<key>allowedDisplayBuiltin</key>
<false />
<key>allowedDisplaysMaxNumber</key>
<integer>3</integer>
<key>allowiOSBetaVersionNumber</key>
<integer>12</integer>
<key>allowiOSVersionNumberMajor</key>
<integer>9</integer>
<key>allowiOSVersionNumberMinor</key>
<integer>0</integer>
<key>allowiOSVersionNumberPatch</key>
<integer>0</integer>
<key>blacklistURLFilter</key>
<string>jrtjrtzj</string>
<key>blockPopUpWindows</key>
<false />
<key>browserExamKey</key>
<string />
<key>browserMessagingPingTime</key>
<integer>120000</integer>
<key>browserMessagingSocket</key>
<string>ws:\localhost:8706</string>
<key>browserScreenKeyboard</key>
<false />
<key>browserURLSalt</key>
<true />
<key>browserUserAgent</key>
<string />
<key>browserUserAgentMac</key>
<integer>0</integer>
<key>browserUserAgentMacCustom</key>
<string />
<key>browserUserAgentWinDesktopMode</key>
<integer>0</integer>
<key>browserUserAgentWinDesktopModeCustom</key>
<string />
<key>browserUserAgentWinTouchMode</key>
<integer>0</integer>
<key>browserUserAgentWinTouchModeCustom</key>
<string />
<key>browserUserAgentWinTouchModeIPad</key>
<string>Mozilla/5.0 (iPad; CPU OS 11_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Mobile/15E148 Safari/604.1</string>
<key>browserUserAgentiOS</key>
<integer>0</integer>
<key>browserUserAgentiOSCustom</key>
<string />
<key>browserViewMode</key>
<integer>0</integer>
<key>browserWindowAllowReload</key>
<true />
<key>browserWindowShowURL</key>
<integer>1</integer>
<key>chooseFileToUploadPolicy</key>
<integer>0</integer>
<key>createNewDesktop</key>
<true />
<key>downloadAndOpenSebConfig</key>
<true />
<key>downloadDirectoryOSX</key>
<string>/var/mobile/Containers/Data/Application/DE2E302B-36C4-48B7-A29E-804F8B27C2F6/Downloads</string>
<key>downloadDirectoryWin</key>
<string>Desktop</string>
<key>downloadPDFFiles</key>
<false />
<key>embeddedCertificates</key>
<array></array>
<key>enableAltEsc</key>
<false />
<key>enableAltF4</key>
<false />
<key>enableAltMouseWheel</key>
<true />
<key>enableAltTab</key>
<true />
<key>enableAppSwitcherCheck</key>
<true />
<key>enableBrowserWindowToolbar</key>
<true />
<key>enableCtrlEsc</key>
<false />
<key>enableDrawingEditor</key>
<false />
<key>enableEsc</key>
<false />
<key>enableF1</key>
<false />
<key>enableF10</key>
<false />
<key>enableF11</key>
<false />
<key>enableF12</key>
<false />
<key>enableF2</key>
<false />
<key>enableF3</key>
<false />
<key>enableF4</key>
<false />
<key>enableF5</key>
<true />
<key>enableF6</key>
<false />
<key>enableF7</key>
<false />
<key>enableF8</key>
<false />
<key>enableF9</key>
<false />
<key>enableJava</key>
<false />
<key>enableJavaScript</key>
<true />
<key>enableLogging</key>
<true />
<key>enablePlugIns</key>
<true />
<key>enablePrintScreen</key>
<false />
<key>enablePrivateClipboard</key>
<true />
<key>enableRightMouse</key>
<false />
<key>enableSebBrowser</key>
<true />
<key>enableStartMenu</key>
<false />
<key>enableTouchExit</key>
<false />
<key>enableURLContentFilter</key>
<false />
<key>enableURLFilter</key>
<false />
<key>enableZoomPage</key>
<true />
<key>enableZoomText</key>
<true />
<key>examConfigKeyContainedKeys</key>
<array></array>
<key>examKeySalt</key>
<data>sq3GTYYzHLg6MT9b+SAi9vEA3TuMIHDxus8hzwe2KNA=</data>
<key>exitKey1</key>
<integer>2</integer>
<key>exitKey2</key>
<integer>10</integer>
<key>exitKey3</key>
<integer>5</integer>
<key>forceAppFolderInstall</key>
<true />
<key>hashedAdminPassword</key>
<string />
<key>hashedQuitPassword</key>
<string />
<key>hideBrowserWindowToolbar</key>
<false />
<key>hookKeys</key>
<true />
<key>ignoreExitKeys</key>
<true />
<key>ignoreQuitPassword</key>
<false />
<key>insideSebEnableChangeAPassword</key>
<false />
<key>insideSebEnableEaseOfAccess</key>
<false />
<key>insideSebEnableLockThisComputer</key>
<false />
<key>insideSebEnableLogOff</key>
<false />
<key>insideSebEnableShutDown</key>
<false />
<key>insideSebEnableStartTaskManager</key>
<false />
<key>insideSebEnableSwitchUser</key>
<false />
<key>insideSebEnableVmWareClientShade</key>
<false />
<key>killExplorerShell</key>
<false />
<key>logDirectoryOSX</key>
<string />
<key>logDirectoryWin</key>
<string />
<key>logLevel</key>
<integer>3</integer>
<key>mainBrowserWindowHeight</key>
<string>100%</string>
<key>mainBrowserWindowPositioning</key>
<integer>1</integer>
<key>mainBrowserWindowWidth</key>
<string>100%</string>
<key>minMacOSVersion</key>
<integer>0</integer>
<key>mobileAllowGuidedAccess</key>
<false />
<key>mobileAllowQRCodeConfig</key>
<true />
<key>mobileAllowSingleAppMode</key>
<false />
<key>mobileEnableASAM</key>
<true />
<key>mobileEnableGuidedAccessLinkTransform</key>
<false />
<key>mobilePreventAutoLock</key>
<true />
<key>mobileShowSettings</key>
<false />
<key>mobileStatusBarAppearance</key>
<integer>1</integer>
<key>mobileStatusBarAppearanceExtended</key>
<integer>1</integer>
<key>mobileSupportedFormFactorsCompact</key>
<true />
<key>mobileSupportedFormFactorsNonTelephonyCompact</key>
<true />
<key>mobileSupportedFormFactorsRegular</key>
<true />
<key>mobileSupportedScreenOrientationsCompactLandscapeLeft</key>
<true />
<key>mobileSupportedScreenOrientationsCompactLandscapeRight</key>
<true />
<key>mobileSupportedScreenOrientationsCompactPortrait</key>
<true />
<key>mobileSupportedScreenOrientationsCompactPortraitUpsideDown</key>
<false />
<key>mobileSupportedScreenOrientationsRegularLandscapeLeft</key>
<true />
<key>mobileSupportedScreenOrientationsRegularLandscapeRight</key>
<true />
<key>mobileSupportedScreenOrientationsRegularPortrait</key>
<true />
<key>mobileSupportedScreenOrientationsRegularPortraitUpsideDown</key>
<true />
<key>monitorProcesses</key>
<true />
<key>newBrowserWindowAllowReload</key>
<true />
<key>newBrowserWindowByLinkBlockForeign</key>
<false />
<key>newBrowserWindowByLinkHeight</key>
<string>100%</string>
<key>newBrowserWindowByLinkPolicy</key>
<integer>2</integer>
<key>newBrowserWindowByLinkPositioning</key>
<integer>2</integer>
<key>newBrowserWindowByLinkWidth</key>
<string>1000</string>
<key>newBrowserWindowByScriptBlockForeign</key>
<false />
<key>newBrowserWindowByScriptPolicy</key>
<integer>2</integer>
<key>newBrowserWindowNavigation</key>
<true />
<key>newBrowserWindowShowReloadWarning</key>
<false />
<key>newBrowserWindowShowURL</key>
<integer>2</integer>
<key>openDownloads</key>
<false />
<key>originatorVersion</key>
<string>SEB_iOS_2.1.12_11838</string>
<key>oskBehavior</key>
<integer>2</integer>
<key>outsideSebEnableChangeAPassword</key>
<true />
<key>outsideSebEnableEaseOfAccess</key>
<true />
<key>outsideSebEnableLockThisComputer</key>
<true />
<key>outsideSebEnableLogOff</key>
<true />
<key>outsideSebEnableShutDown</key>
<true />
<key>outsideSebEnableStartTaskManager</key>
<true />
<key>outsideSebEnableSwitchUser</key>
<true />
<key>outsideSebEnableVmWareClientShade</key>
<true />
<key>permittedProcesses</key>
<array>
<dict>
<key>active</key>
<true />
<key>allowUserToChooseApp</key>
<false />
<key>allowedExecutables</key>
<string />
<key>arguments</key>
<array></array>
<key>autostart</key>
<true />
<key>description</key>
<string />
<key>executable</key>
<string>firefox.exe</string>
<key>iconInTaskbar</key>
<true />
<key>identifier</key>
<string>Firefox</string>
<key>os</key>
<integer>1</integer>
<key>path</key>
<string>../xulrunner/</string>
<key>runInBackground</key>
<false />
<key>strongKill</key>
<true />
<key>title</key>
<string>SEB</string>
<key>windowHandlingProcess</key>
<string />
<key>originalName</key>
<string>firefox.exe</string>
</dict>
<dict>
<key>active</key>
<true />
<key>autostart</key>
<true />
<key>iconInTaskbar</key>
<true />
<key>runInBackground</key>
<true />
<key>allowUserToChooseApp</key>
<true />
<key>strongKill</key>
<true />
<key>os</key>
<integer>0</integer>
<key>title</key>
<string>eheth</string>
<key>description</key>
<string>etheth</string>
<key>executable</key>
<string>tzjtzjtzj</string>
<key>originalName</key>
<string>eh</string>
<key>windowHandlingProcess</key>
<string>rh</string>
<key>path</key>
<string>rh</string>
<key>identifier</key>
<string>rehreh</string>
<key>arguments</key>
<array>
<dict>
<key>active</key>
<true />
<key>argument</key>
<string>erherh</string>
</dict>
<dict>
<key>active</key>
<true />
<key>argument</key>
<string>eherh</string>
</dict>
<dict>
<key>active</key>
<true />
<key>argument</key>
<string>rtherhe</string>
</dict>
</array>
</dict>
</array>
<key>pinEmbeddedCertificates</key>
<false />
<key>prohibitedProcesses</key>
<array>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>join.me</string>
<key>originalName</key>
<string>join.me</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>RPCSuite</string>
<key>originalName</key>
<string>RPCSuite</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>RPCService</string>
<key>originalName</key>
<string>RPCService</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>RemotePCDesktop</string>
<key>originalName</key>
<string>RemotePCDesktop</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>beamyourscreen-host</string>
<key>originalName</key>
<string>beamyourscreen-host</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>AeroAdmin</string>
<key>originalName</key>
<string>AeroAdmin</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>Mikogo-host</string>
<key>originalName</key>
<string>Mikogo-host</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>chromoting</string>
<key>originalName</key>
<string>chromoting</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>vncserverui</string>
<key>originalName</key>
<string>vncserverui</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>vncviewer</string>
<key>originalName</key>
<string>vncviewer</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>vncserver</string>
<key>originalName</key>
<string>vncserver</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>TeamViewer</string>
<key>originalName</key>
<string>TeamViewer</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>GotoMeetingWinStore</string>
<key>originalName</key>
<string>GotoMeetingWinStore</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>g2mcomm.exe</string>
<key>originalName</key>
<string>g2mcomm.exe</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>SkypeHost</string>
<key>originalName</key>
<string>SkypeHost</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
<dict>
<key>active</key>
<true />
<key>currentUser</key>
<true />
<key>strongKill</key>
<false />
<key>os</key>
<integer>1</integer>
<key>executable</key>
<string>Skype</string>
<key>originalName</key>
<string>Skype</string>
<key>description</key>
<string />
<key>identifier</key>
<string />
<key>windowHandlingProcess</key>
<string />
<key>user</key>
<string />
</dict>
</array>
<key>proxies</key>
<dict>
<key>AutoConfigurationEnabled</key>
<false />
<key>AutoConfigurationJavaScript</key>
<string />
<key>AutoConfigurationURL</key>
<string />
<key>AutoDiscoveryEnabled</key>
<false />
<key>ExceptionsList</key>
<array></array>
<key>ExcludeSimpleHostnames</key>
<false />
<key>FTPEnable</key>
<false />
<key>FTPPassive</key>
<true />
<key>FTPPassword</key>
<string />
<key>FTPPort</key>
<integer>21</integer>
<key>FTPProxy</key>
<string />
<key>FTPRequiresPassword</key>
<false />
<key>FTPUsername</key>
<string />
<key>HTTPEnable</key>
<false />
<key>HTTPPassword</key>
<string />
<key>HTTPPort</key>
<integer>80</integer>
<key>HTTPProxy</key>
<string />
<key>HTTPRequiresPassword</key>
<false />
<key>HTTPSEnable</key>
<true />
<key>HTTPSPassword</key>
<string>zul,.lli</string>
<key>HTTPSPort</key>
<integer>443</integer>
<key>HTTPSProxy</key>
<string>zilzklz</string>
<key>HTTPSRequiresPassword</key>
<true />
<key>HTTPSUsername</key>
<string>luzulz</string>
<key>HTTPUsername</key>
<string />
<key>RTSPEnable</key>
<false />
<key>RTSPPassword</key>
<string />
<key>RTSPPort</key>
<integer>554</integer>
<key>RTSPProxy</key>
<string />
<key>RTSPRequiresPassword</key>
<false />
<key>RTSPUsername</key>
<string />
<key>SOCKSEnable</key>
<false />
<key>SOCKSPassword</key>
<string />
<key>SOCKSPort</key>
<integer>1080</integer>
<key>SOCKSProxy</key>
<string />
<key>SOCKSRequiresPassword</key>
<false />
<key>SOCKSUsername</key>
<string />
</dict>
<key>proxySettingsPolicy</key>
<integer>0</integer>
<key>quitURL</key>
<string />
<key>quitURLConfirm</key>
<true />
<key>removeBrowserProfile</key>
<true />
<key>removeLocalStorage</key>
<false />
<key>restartExamPasswordProtected</key>
<true />
<key>restartExamText</key>
<string />
<key>restartExamURL</key>
<string />
<key>restartExamUseStartURL</key>
<true />
<key>sebConfigPurpose</key>
<integer>0</integer>
<key>sebMode</key>
<integer>0</integer>
<key>sebServerFallback</key>
<false />
<key>sebServerURL</key>
<string />
<key>sebServicePolicy</key>
<integer>2</integer>
<key>sendBrowserExamKey</key>
<false />
<key>showBackToStartButton</key>
<false />
<key>showInputLanguage</key>
<false />
<key>showMenuBar</key>
<true />
<key>showNavigationButtons</key>
<false />
<key>showReloadButton</key>
<false />
<key>showReloadWarning</key>
<true />
<key>showScanQRCodeButton</key>
<true />
<key>showSettingsInApp</key>
<true />
<key>showTaskBar</key>
<true />
<key>showTime</key>
<true />
<key>startURL</key>
<string>https://demo.safeexambrowser.org/exams</string>
<key>startURLAllowDeepLink</key>
<false />
<key>startURLAppendQueryParameter</key>
<false />
<key>taskBarHeight</key>
<integer>40</integer>
<key>touchOptimized</key>
<false />
<key>urlFilterRegex</key>
<true />
<key>urlFilterTrustedContent</key>
<true />
<key>whitelistURLFilter</key>
<string>^.*?:\/\/((((rtjrtj)|(.*?\.rtjrtj)))|(((rtjrtj)|(.*?\.rtjrtj))\/.*?))(()|(\?.*?))$</string>
<key>zoomMode</key>
<integer>0</integer>
<key>startResource</key>
<string />
<key>useAsymmetricOnlyEncryption</key>
<false />
<key>allowSpellCheckDictionary</key>
<array></array>
<key>additionalDictionaries</key>
<array></array>
<key>audioControlEnabled</key>
<true />
<key>audioMute</key>
<false />
<key>audioVolumeLevel</key>
<integer>100</integer>
<key>audioSetVolumeLevel</key>
<false />
<key>allowAudioCapture</key>
<false />
<key>browserWindowTitleSuffix</key>
<string />
<key>backgroundOpenSEBConfig</key>
<false />
<key>additionalResources</key>
<array></array>
<key>allowWlan</key>
<false />
<key>lockOnMessageSocketClose</key>
<true />
<key>allowUserAppFolderInstall</key>
<false />
<key>detectStoppedProcess</key>
<true />
<key>insideSebEnableEnableNetworkConnectionSelector</key>
<false />
</dict>
</plist>

Binary file not shown.