Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d1aee95d35 | |||
| 72a709c11d | |||
| 4f7f6db804 | |||
| 892299a791 | |||
| 48512fa9d1 | |||
| 204d57a4a5 | |||
| f807d75162 | |||
| e809c63e37 | |||
| e1f1973520 | |||
| 71fa53bfb2 | |||
| 834d2f8fae | |||
| 2ab765f2d7 | |||
| 04be92a1df | |||
| 4e7c7c117b | |||
| f975c55b8a | |||
| 9e0d832aa1 | |||
| 94a31282eb | |||
| 529bba5c72 | |||
| 8424a9a9a2 | |||
| 202bbb163b | |||
| 0e15d2a880 | |||
| 89c14084f5 |
@@ -60,6 +60,10 @@
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
<Compile Include="Extensions\EnrolExtensions.cs" />
|
||||
<Compile Include="Extensions\WhoAmIExtensions.cs" />
|
||||
<Compile Include="Interop\Certificates.cs" />
|
||||
<Compile Include="Interop\EndpointDiscovery.cs" />
|
||||
<Compile Include="Interop\Hardware.cs" />
|
||||
<Compile Include="Interop\LocalAuthentication.cs" />
|
||||
<Compile Include="Interop\Native\NetworkConnectionStatuses.cs" />
|
||||
|
||||
@@ -10,19 +10,18 @@ namespace Disco.Client
|
||||
{
|
||||
public static class ErrorReporting
|
||||
{
|
||||
private const string ServicePathTemplate = "http://DISCO:9292/Services/Client/ClientError";
|
||||
public static string DeviceIdentifier { get; set; }
|
||||
public static string EnrolmentSessionId { get; set; }
|
||||
|
||||
public static void ReportError(Exception Ex, bool ReportToServer)
|
||||
public static void ReportError(Exception exception, bool reportToServer)
|
||||
{
|
||||
bool isClientServiceException = Ex is ClientServiceException;
|
||||
bool isClientServiceException = exception is ClientServiceException;
|
||||
|
||||
ErrorReport report = new ErrorReport()
|
||||
{
|
||||
DeviceIdentifier = DeviceIdentifier,
|
||||
SessionId = EnrolmentSessionId,
|
||||
JsonException = Ex.IntenseExceptionSerialization()
|
||||
JsonException = exception.IntenseExceptionSerialization()
|
||||
};
|
||||
|
||||
try
|
||||
@@ -38,7 +37,7 @@ namespace Disco.Client
|
||||
catch (Exception) { }
|
||||
|
||||
// Don't log server errors back to the server
|
||||
if (!isClientServiceException && ReportToServer)
|
||||
if (!isClientServiceException && reportToServer)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -49,7 +48,7 @@ namespace Disco.Client
|
||||
|
||||
try
|
||||
{
|
||||
Presentation.WriteFatalError(Ex);
|
||||
Presentation.WriteFatalError(exception);
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
@@ -85,7 +84,9 @@ namespace Disco.Client
|
||||
string reportJson = JsonConvert.SerializeObject(report);
|
||||
string reportResponse;
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(ServicePathTemplate);
|
||||
var serverUri = new Uri(Program.ServerUrl ?? new Uri("http://disco:9292"), "/Services/Client/ClientError");
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serverUri);
|
||||
request.UserAgent = $"Disco-Client/{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}";
|
||||
request.ContentType = "application/json";
|
||||
request.Method = WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -1,30 +1,23 @@
|
||||
using Disco.Models.ClientServices;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Disco.Client.Extensions
|
||||
{
|
||||
public static class ClientServicesExtensions
|
||||
internal static class ClientServicesExtensions
|
||||
{
|
||||
//#if DEBUG
|
||||
// public const string ServicePathAuthenticatedTemplate = "http://WS-GSHARP:57252/Services/Client/Authenticated/{0}";
|
||||
// public const string ServicePathUnauthenticatedTemplate = "http://WS-GSHARP:57252/Services/Client/Unauthenticated/{0}";
|
||||
//#else
|
||||
public const string ServicePathAuthenticatedTemplate = "http://DISCO:9292/Services/Client/Authenticated/{0}";
|
||||
public const string ServicePathUnauthenticatedTemplate = "http://DISCO:9292/Services/Client/Unauthenticated/{0}";
|
||||
//#endif
|
||||
|
||||
public static ResponseType Post<ResponseType>(this ServiceBase<ResponseType> Service, bool Authenticated)
|
||||
public static ResponseType Post<ResponseType>(this ServiceBase<ResponseType> service, bool authenticated)
|
||||
{
|
||||
ResponseType serviceResponse;
|
||||
string serviceUrl;
|
||||
Uri serviceUrl;
|
||||
|
||||
if (Authenticated)
|
||||
serviceUrl = string.Format(ServicePathAuthenticatedTemplate, Service.Feature);
|
||||
if (authenticated)
|
||||
serviceUrl = new Uri(Program.ServerUrl, $"/Services/Client/Authenticated/{service.Feature}");
|
||||
else
|
||||
serviceUrl = string.Format(ServicePathUnauthenticatedTemplate, Service.Feature);
|
||||
serviceUrl = new Uri(Program.ServerUrl, $"/Services/Client/Unauthenticated/{service.Feature}");
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serviceUrl);
|
||||
request.UserAgent = $"Disco-Client/{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}";
|
||||
@@ -39,7 +32,7 @@ namespace Disco.Client.Extensions
|
||||
{
|
||||
using (var jsonWriter = new JsonTextWriter(requestWriter))
|
||||
{
|
||||
jsonSerializer.Serialize(jsonWriter, Service);
|
||||
jsonSerializer.Serialize(jsonWriter, service);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
using Disco.Models.ClientServices;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Disco.Client.Extensions
|
||||
{
|
||||
public static class EnrolExtensions
|
||||
internal static class EnrolExtensions
|
||||
{
|
||||
|
||||
public static void Build(this Enrol enrol)
|
||||
{
|
||||
enrol.ComputerName = Environment.MachineName;
|
||||
@@ -62,6 +60,28 @@ namespace Disco.Client.Extensions
|
||||
Program.AllowUninstall = enrolResponse.AllowBootstrapperUninstall;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum NETSETUP_PROVISION_FLAGS : int
|
||||
{
|
||||
NETSETUP_PROVISION_DOWNLEVEL_PRIV_SUPPORT = 0x00000001,
|
||||
NETSETUP_PROVISION_REUSE_ACCOUNT = 0x00000002,
|
||||
NETSETUP_PROVISION_USE_DEFAULT_PASSWORD = 0x00000004,
|
||||
NETSETUP_PROVISION_SKIP_ACCOUNT_SEARCH = 0x00000008,
|
||||
NETSETUP_PROVISION_ROOT_CA_CERTS = 0x00000010,
|
||||
NETSETUP_PROVISION_PERSISTENTSITE = 0x00000020,
|
||||
NETSETUP_PROVISION_ONLINE_CALLER = 0x40000000,
|
||||
NETSETUP_PROVISION_CHECK_PWD_ONLY = unchecked((int)0x80000000),
|
||||
}
|
||||
|
||||
[DllImport("Netapi32.dll", CallingConvention = CallingConvention.Winapi)]
|
||||
[return: MarshalAs(UnmanagedType.I4)]
|
||||
private static extern int NetRequestOfflineDomainJoin(
|
||||
[In] IntPtr pProvisionBinData,
|
||||
[In, MarshalAs(UnmanagedType.I4)] int cbProvisionBinDataSize,
|
||||
[In, MarshalAs(UnmanagedType.I4)] NETSETUP_PROVISION_FLAGS dwOptions,
|
||||
[In, MarshalAs(UnmanagedType.LPWStr)] string lpWindowsPath
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Processes a Client Service Enrol Response for Offline Domain Join Actions
|
||||
/// </summary>
|
||||
@@ -73,33 +93,34 @@ namespace Disco.Client.Extensions
|
||||
{
|
||||
Presentation.UpdateStatus("Enrolling Device", $"Performing Offline Domain Join:\r\nRenaming Computer: {Environment.MachineName} -> {enrolResponse.ComputerName}", true, -1, 1500);
|
||||
|
||||
string odjFile = Path.GetTempFileName();
|
||||
File.WriteAllBytes(odjFile, Convert.FromBase64String(enrolResponse.OfflineDomainJoinManifest));
|
||||
var provisionData = Convert.FromBase64String(enrolResponse.OfflineDomainJoinManifest);
|
||||
string systemRoot = Environment.GetEnvironmentVariable("SystemRoot");
|
||||
|
||||
string odjWindowsPath = Environment.GetEnvironmentVariable("SystemRoot");
|
||||
string odjProcessArguments = $"/REQUESTODJ /LOADFILE \"{odjFile}\" /WINDOWSPATH \"{odjWindowsPath}\" /LOCALOS";
|
||||
|
||||
ProcessStartInfo odjProcessStartInfo = new ProcessStartInfo("DJOIN.EXE", odjProcessArguments)
|
||||
var provisionDataPointer = Marshal.AllocCoTaskMem(provisionData.Length);
|
||||
Marshal.Copy(provisionData, 0, provisionDataPointer, provisionData.Length);
|
||||
var joinResult = default(int);
|
||||
try
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
ErrorDialog = false,
|
||||
LoadUserProfile = true,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false
|
||||
};
|
||||
string odjResult;
|
||||
using (Process odjProcess = System.Diagnostics.Process.Start(odjProcessStartInfo))
|
||||
{
|
||||
odjResult = odjProcess.StandardOutput.ReadToEnd();
|
||||
odjProcess.WaitForExit(20000); // 20 Seconds
|
||||
joinResult = NetRequestOfflineDomainJoin(provisionDataPointer, provisionData.Length, NETSETUP_PROVISION_FLAGS.NETSETUP_PROVISION_ONLINE_CALLER, systemRoot);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeCoTaskMem(provisionDataPointer);
|
||||
}
|
||||
Presentation.UpdateStatus("Enrolling Device", $"Offline Domain Join Result:\r\n{odjResult}", true, -1, 3000);
|
||||
|
||||
if (File.Exists(odjFile))
|
||||
File.Delete(odjFile);
|
||||
if (joinResult != 0)
|
||||
{
|
||||
var win32Exception = new System.ComponentModel.Win32Exception(joinResult);
|
||||
Presentation.UpdateStatus("Enrolling Device", $"Offline Domain Join Failed:\r\n{win32Exception.Message} [{joinResult}]", true, -1, 3000);
|
||||
throw new InvalidOperationException($"Offline Domain Join Failed:\r\n{win32Exception.Message} [{joinResult}]");
|
||||
}
|
||||
else
|
||||
{
|
||||
Presentation.UpdateStatus("Enrolling Device", $"Offline Domain Join Succeeded", true, -1, 2000);
|
||||
}
|
||||
|
||||
// Flush Logged-On History
|
||||
if (!string.IsNullOrEmpty(enrolResponse.DomainName))
|
||||
if (enrolResponse.SetAssignedUserForLogon && !string.IsNullOrEmpty(enrolResponse.DomainName))
|
||||
{
|
||||
using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
||||
{
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Disco.Client.Interop
|
||||
{
|
||||
internal class EndpointDiscovery
|
||||
{
|
||||
[DllImport("dnsapi", EntryPoint = "DnsQuery_W", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
|
||||
private static extern int DnsQuery([MarshalAs(UnmanagedType.VBByRefStr)] ref string pszName, NativeDnsQueryTypes wType, NativeDnsQueryOptions options, int aipServers, ref IntPtr ppQueryResults, int pReserved);
|
||||
|
||||
[DllImport("dnsapi", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern void DnsRecordListFree(IntPtr pRecordList, int FreeType);
|
||||
private const int DNS_ERROR_RCODE_NAME_ERROR = 0x232B;
|
||||
private const int DNS_ERROR_BAD_PACKET = 0x251E;
|
||||
public static Tuple<Uri, string> DiscoverServer(Uri forcedServerUri)
|
||||
{
|
||||
// 1. Check first command line argument for server name
|
||||
if (forcedServerUri != null)
|
||||
return Tuple.Create(forcedServerUri, "Manual");
|
||||
|
||||
// 2. Check for a DNS SRV record for _discoict._tcp.domain
|
||||
var domainSuffixes = new List<string>();
|
||||
var primaryDomain = IPGlobalProperties.GetIPGlobalProperties().DomainName;
|
||||
if (!string.IsNullOrEmpty(primaryDomain))
|
||||
domainSuffixes.Add(primaryDomain);
|
||||
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces()
|
||||
.Where(ni => ni.OperationalStatus == OperationalStatus.Up);
|
||||
foreach (var ni in networkInterfaces)
|
||||
{
|
||||
var domainSuffix = ni.GetIPProperties().DnsSuffix;
|
||||
if (!string.IsNullOrWhiteSpace(domainSuffix))
|
||||
{
|
||||
if (domainSuffix.Equals("mshome.net", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
if (!domainSuffixes.Contains(domainSuffix, StringComparer.OrdinalIgnoreCase))
|
||||
domainSuffixes.Add(domainSuffix);
|
||||
}
|
||||
}
|
||||
foreach (var domain in domainSuffixes)
|
||||
{
|
||||
var dnsRecords = GetSRVRecords("_discoict._tcp." + domain);
|
||||
if (dnsRecords.Count > 0)
|
||||
{
|
||||
var firstRecord = dnsRecords.OrderBy(r => r.Priority).ThenByDescending(r => r.Weight).First();
|
||||
if (firstRecord.Port == 443)
|
||||
return Tuple.Create(new Uri($"https://{firstRecord.Target}"), "SRV");
|
||||
else
|
||||
return Tuple.Create(new Uri($"https://{firstRecord.Target}:{firstRecord.Port}"), "SRV");
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Detect VicSmart network and try resolving with Disco ICT Online Services
|
||||
if (TryResolveVicSmartServer(domainSuffixes, out var vicSmartServerUrl))
|
||||
return Tuple.Create(vicSmartServerUrl, "VicSmart");
|
||||
|
||||
// 4. Legacy: Ping 'disco' and assume port 9292
|
||||
using (Ping p = new Ping())
|
||||
{
|
||||
try
|
||||
{
|
||||
PingReply pr = p.Send("disco", 2000);
|
||||
if (pr.Status == IPStatus.Success)
|
||||
return Tuple.Create(new Uri("http://disco:9292"), "Legacy");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
throw new Exception("Could not locate Disco ICT server on the network.");
|
||||
}
|
||||
|
||||
private static bool TryResolveVicSmartServer(List<string> domainSuffixes, out Uri serverUrl)
|
||||
{
|
||||
if (IsVicSmartNetwork(domainSuffixes))
|
||||
{
|
||||
var potentialVicSmartAddresses = NetworkInterface.GetAllNetworkInterfaces()
|
||||
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
|
||||
.SelectMany(ni => ni.GetIPProperties().UnicastAddresses)
|
||||
.Where(ua => ua.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
||||
.Select(ua => ua.Address.GetAddressBytes())
|
||||
.Where(a => a[0] == 10)
|
||||
.Select(a => (ushort)((a[1] >> 4) & 0x000F) | ((a[1] << 4) & 0x00F0) | ((a[2] << 12) & 0xF000) | ((a[2] << 4) & 0x0F00))
|
||||
.Distinct()
|
||||
.Select(a => $"{a:x4}.vicsmart.discoict.com")
|
||||
.ToList();
|
||||
|
||||
foreach (var potentialAddress in potentialVicSmartAddresses)
|
||||
{
|
||||
var records = GetTxtRecords(potentialAddress);
|
||||
|
||||
foreach (var record in records)
|
||||
{
|
||||
if (!record.Content.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
if (Uri.TryCreate(record.Content, UriKind.Absolute, out var discoveredUri))
|
||||
{
|
||||
serverUrl = discoveredUri;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serverUrl = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsVicSmartNetwork(List<string> domainSuffixes)
|
||||
{
|
||||
if (domainSuffixes.Any(s => string.Equals("services.education.vic.gov.au", s, StringComparison.OrdinalIgnoreCase)) ||
|
||||
domainSuffixes.Any(s => string.Equals("education.vic.gov.au", s, StringComparison.OrdinalIgnoreCase))
|
||||
)
|
||||
return true;
|
||||
|
||||
IPHostEntry doeWanDnsEntry;
|
||||
try
|
||||
{
|
||||
doeWanDnsEntry = Dns.GetHostEntry("broadband.doe.wan");
|
||||
if (doeWanDnsEntry.AddressList.Length > 0)
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{ }
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<DnsTxtRecord> GetTxtRecords(string name)
|
||||
{
|
||||
IntPtr resourceRecordsPointer = IntPtr.Zero;
|
||||
var records = new List<DnsTxtRecord>();
|
||||
var retry = 5;
|
||||
retry:
|
||||
try
|
||||
{
|
||||
int queryResult = DnsQuery(ref name, NativeDnsQueryTypes.DNS_TYPE_TEXT, NativeDnsQueryOptions.DNS_QUERY_STANDARD, 0, ref resourceRecordsPointer, 0);
|
||||
if (queryResult != 0)
|
||||
{
|
||||
if (queryResult == DNS_ERROR_RCODE_NAME_ERROR)
|
||||
return records;
|
||||
else if (queryResult == DNS_ERROR_BAD_PACKET && retry > 0)
|
||||
{
|
||||
// Sometimes a BAD_PACKET error is returned, retry a few times
|
||||
Thread.Sleep(200);
|
||||
retry--;
|
||||
goto retry;
|
||||
}
|
||||
else
|
||||
throw new Win32Exception(queryResult);
|
||||
}
|
||||
NativeDnsTxtRecord record;
|
||||
for (var resourceRecordPointer = resourceRecordsPointer; !resourceRecordPointer.Equals(IntPtr.Zero); resourceRecordPointer = record.pNext)
|
||||
{
|
||||
record = Marshal.PtrToStructure<NativeDnsTxtRecord>(resourceRecordPointer);
|
||||
if (record.wType == (ushort)NativeDnsQueryTypes.DNS_TYPE_TEXT)
|
||||
records.Add(DnsTxtRecord.FromNativeRecord(record));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (resourceRecordsPointer != IntPtr.Zero)
|
||||
DnsRecordListFree(resourceRecordsPointer, 0);
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
private static List<DnsSrvRecord> GetSRVRecords(string name)
|
||||
{
|
||||
IntPtr resourceRecordsPointer = IntPtr.Zero;
|
||||
var records = new List<DnsSrvRecord>();
|
||||
var retry = 5;
|
||||
retry:
|
||||
try
|
||||
{
|
||||
int queryResult = DnsQuery(ref name, NativeDnsQueryTypes.DNS_TYPE_SRV, NativeDnsQueryOptions.DNS_QUERY_STANDARD, 0, ref resourceRecordsPointer, 0);
|
||||
if (queryResult != 0)
|
||||
{
|
||||
if (queryResult == DNS_ERROR_RCODE_NAME_ERROR)
|
||||
return records;
|
||||
else if (queryResult == DNS_ERROR_BAD_PACKET && retry > 0)
|
||||
{
|
||||
// Sometimes a BAD_PACKET error is returned, retry a few times
|
||||
Thread.Sleep(200);
|
||||
retry--;
|
||||
goto retry;
|
||||
}
|
||||
else
|
||||
throw new Win32Exception(queryResult);
|
||||
}
|
||||
NativeDnsSrvRecord record;
|
||||
for (var resourceRecordPointer = resourceRecordsPointer; !resourceRecordPointer.Equals(IntPtr.Zero); resourceRecordPointer = record.pNext)
|
||||
{
|
||||
record = Marshal.PtrToStructure<NativeDnsSrvRecord>(resourceRecordPointer);
|
||||
if (record.wType == (ushort)NativeDnsQueryTypes.DNS_TYPE_SRV)
|
||||
records.Add(DnsSrvRecord.FromNativeRecord(record));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (resourceRecordsPointer != IntPtr.Zero)
|
||||
DnsRecordListFree(resourceRecordsPointer, 0);
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
private enum NativeDnsQueryOptions
|
||||
{
|
||||
DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE = 1,
|
||||
DNS_QUERY_BYPASS_CACHE = 8,
|
||||
DNS_QUERY_DONT_RESET_TTL_VALUES = 0x100000,
|
||||
DNS_QUERY_NO_HOSTS_FILE = 0x40,
|
||||
DNS_QUERY_NO_LOCAL_NAME = 0x20,
|
||||
DNS_QUERY_NO_NETBT = 0x80,
|
||||
DNS_QUERY_NO_RECURSION = 4,
|
||||
DNS_QUERY_NO_WIRE_QUERY = 0x10,
|
||||
DNS_QUERY_RESERVED = -16777216,
|
||||
DNS_QUERY_RETURN_MESSAGE = 0x200,
|
||||
DNS_QUERY_STANDARD = 0,
|
||||
DNS_QUERY_TREAT_AS_FQDN = 0x1000,
|
||||
DNS_QUERY_USE_TCP_ONLY = 2,
|
||||
DNS_QUERY_WIRE_ONLY = 0x100
|
||||
}
|
||||
|
||||
private enum NativeDnsQueryTypes
|
||||
{
|
||||
DNS_TYPE_TEXT = 0x0010,
|
||||
DNS_TYPE_SRV = 0x0021
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct NativeDnsSrvRecord
|
||||
{
|
||||
public IntPtr pNext;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string pName;
|
||||
public ushort wType;
|
||||
public ushort wDataLength;
|
||||
public int flags;
|
||||
public int dwTtl;
|
||||
public int dwReserved;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string pNameTarget;
|
||||
public ushort wPriority;
|
||||
public ushort wWeight;
|
||||
public ushort wPort;
|
||||
public ushort Pad;
|
||||
}
|
||||
|
||||
private class DnsSrvRecord
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Type { get; set; }
|
||||
public int Ttl { get; set; }
|
||||
public string Target { get; set; }
|
||||
public int Priority { get; set; }
|
||||
public int Weight { get; set; }
|
||||
public int Port { get; set; }
|
||||
|
||||
public static DnsSrvRecord FromNativeRecord(NativeDnsSrvRecord nativeRecord)
|
||||
{
|
||||
return new DnsSrvRecord
|
||||
{
|
||||
Name = nativeRecord.pName,
|
||||
Type = nativeRecord.wType,
|
||||
Ttl = nativeRecord.dwTtl,
|
||||
Target = nativeRecord.pNameTarget,
|
||||
Priority = nativeRecord.wPriority,
|
||||
Weight = nativeRecord.wWeight,
|
||||
Port = nativeRecord.wPort
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct NativeDnsTxtRecord
|
||||
{
|
||||
public IntPtr pNext;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string pName;
|
||||
public ushort wType;
|
||||
public ushort wDataLength;
|
||||
public int flags;
|
||||
public int dwTtl;
|
||||
public int dwReserved;
|
||||
public uint dwStringLength;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string pStringArray;
|
||||
}
|
||||
|
||||
private class DnsTxtRecord
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Type { get; set; }
|
||||
public int Ttl { get; set; }
|
||||
public string Content { get; set; }
|
||||
|
||||
public static DnsTxtRecord FromNativeRecord(NativeDnsTxtRecord nativeRecord)
|
||||
{
|
||||
return new DnsTxtRecord
|
||||
{
|
||||
Name = nativeRecord.pName,
|
||||
Type = nativeRecord.wType,
|
||||
Ttl = nativeRecord.dwTtl,
|
||||
Content = nativeRecord.pStringArray,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using Disco.Client.Extensions;
|
||||
using Disco.Client.Interop;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
@@ -26,7 +27,7 @@ namespace Disco.Client
|
||||
}
|
||||
public static void UpdateStatus(string SubHeading, string Message, bool ShowProgress, int Progress)
|
||||
{
|
||||
Console.WriteLine($"#{SubHeading.EscapeMessage()},{Message.EscapeMessage()},{ShowProgress.ToString()},{Progress.ToString()}");
|
||||
Console.WriteLine($"#{SubHeading.EscapeMessage()},{Message.EscapeMessage()},{ShowProgress},{Progress}");
|
||||
}
|
||||
public static void TryDelay(int Milliseconds)
|
||||
{
|
||||
@@ -38,6 +39,11 @@ namespace Disco.Client
|
||||
{
|
||||
StringBuilder message = new StringBuilder();
|
||||
message.AppendLine($"Version: {Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}");
|
||||
message.Append($"Server: {Program.ServerUrl})");
|
||||
if (Program.ServerUrl.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
|
||||
message.AppendLine(" [Secure]");
|
||||
else
|
||||
message.AppendLine(" [Insecure]");
|
||||
message.AppendLine($"Device: {Hardware.Information.SerialNumber} ({Hardware.Information.Manufacturer} {Hardware.Information.Model})");
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
UpdateStatus("Preparation Client Started", message.ToString(), false, 0);
|
||||
@@ -48,12 +54,18 @@ namespace Disco.Client
|
||||
{
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Magenta;
|
||||
ClientServiceException clientServiceException = ex as ClientServiceException;
|
||||
if (clientServiceException != null)
|
||||
if (ex is ClientServiceException clientServiceException)
|
||||
{
|
||||
UpdateStatus($"An error occurred during {clientServiceException.ServiceFeature}",
|
||||
clientServiceException.Message, false, 0);
|
||||
}
|
||||
else if (ex is WebException exWeb &&
|
||||
exWeb.Response is HttpWebResponse webResponse &&
|
||||
webResponse.StatusCode == HttpStatusCode.InternalServerError)
|
||||
{
|
||||
UpdateStatus("Something went wrong on the server",
|
||||
"Review logs for more information (Configuration > Logging)", false, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder message = new StringBuilder();
|
||||
|
||||
+59
-6
@@ -1,6 +1,8 @@
|
||||
using Disco.Client.Extensions;
|
||||
using Disco.Client.Interop;
|
||||
using Disco.Models.ClientServices;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
@@ -11,6 +13,9 @@ namespace Disco.Client
|
||||
public static bool IsAuthenticated { get; set; }
|
||||
public static bool RebootRequired { get; set; }
|
||||
public static bool AllowUninstall { get; set; }
|
||||
public static int BootstrapperVersion { get; private set; } = 1;
|
||||
public static int BootstrapperProcessId { get; private set; } = -1;
|
||||
public static Uri ServerUrl { get; private set; }
|
||||
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
@@ -24,12 +29,15 @@ namespace Disco.Client
|
||||
{
|
||||
Console.WriteLine("Waiting for Debugger to Attach");
|
||||
System.Threading.Thread.Sleep(1000);
|
||||
} while (!System.Diagnostics.Debugger.IsAttached);
|
||||
} while (!Debugger.IsAttached);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Initialize Environment Settings
|
||||
SetupEnvironment();
|
||||
SetupEnvironment(args);
|
||||
|
||||
if (ServerUrl == null)
|
||||
keepProcessing = DiscoverDiscoIct();
|
||||
|
||||
// Report to Bootstrapper
|
||||
Presentation.WriteBanner();
|
||||
@@ -45,7 +53,7 @@ namespace Disco.Client
|
||||
Presentation.WriteFooter(RebootRequired, AllowUninstall, !keepProcessing);
|
||||
}
|
||||
|
||||
public static void SetupEnvironment()
|
||||
public static void SetupEnvironment(string[] args)
|
||||
{
|
||||
// Hookup Unhandled Error Handling
|
||||
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.CurrentDomain_UnhandledException;
|
||||
@@ -54,21 +62,66 @@ namespace Disco.Client
|
||||
WebRequest.DefaultWebProxy = new WebProxy();
|
||||
// Override Http 100 Continue Behaviour
|
||||
ServicePointManager.Expect100Continue = false;
|
||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||
|
||||
// Assume success unless otherwise notified
|
||||
AllowUninstall = true;
|
||||
|
||||
if (args != null && args.Length == 3)
|
||||
{
|
||||
// Parse Bootstrapper Version
|
||||
int parsedVersion;
|
||||
if (int.TryParse(args[0], out parsedVersion))
|
||||
BootstrapperVersion = parsedVersion;
|
||||
// Parse Bootstrapper Process ID
|
||||
int parsedProcessId;
|
||||
if (int.TryParse(args[1], out parsedProcessId))
|
||||
BootstrapperProcessId = parsedProcessId;
|
||||
// Parse Server URL
|
||||
Uri parsedUri;
|
||||
if (Uri.TryCreate(args[2], UriKind.Absolute, out parsedUri))
|
||||
ServerUrl = parsedUri;
|
||||
}
|
||||
else
|
||||
{
|
||||
BootstrapperVersion = 1;
|
||||
BootstrapperProcessId = -1;
|
||||
ServerUrl = null;
|
||||
}
|
||||
|
||||
// Detect Disco.Bootstrapper - Create Enable UI Delay if Running
|
||||
Presentation.DelayUI = false;
|
||||
try
|
||||
{
|
||||
Presentation.DelayUI = (System.Diagnostics.Process.GetProcessesByName("Disco.ClientBootstrapper").Length > 0);
|
||||
if (BootstrapperProcessId != -1)
|
||||
{
|
||||
var parentProcess = Process.GetProcessById(BootstrapperProcessId);
|
||||
Presentation.DelayUI = !parentProcess.HasExited;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Presentation.DelayUI = true; // Add Delays on Error
|
||||
}
|
||||
}
|
||||
|
||||
public static bool DiscoverDiscoIct()
|
||||
{
|
||||
try
|
||||
{
|
||||
Presentation.UpdateStatus("Detecting Disco ICT", "Locating Disco ICT Server, Please wait...", true, -1);
|
||||
Presentation.TryDelay(3000);
|
||||
ServerUrl = EndpointDiscovery.DiscoverServer(null).Item1;
|
||||
|
||||
// Complete
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorReporting.ReportError(ex, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool WhoAmI()
|
||||
{
|
||||
try
|
||||
@@ -144,7 +197,7 @@ namespace Disco.Client
|
||||
var secondsConsumed = (DateTimeOffset.Now - startTime).TotalSeconds;
|
||||
var progress = (int)((secondsConsumed / totalSeconds) * 100);
|
||||
|
||||
Presentation.UpdateStatus($"Pending Device Enrolment Approval: {response.PendingIdentifier}", $"Waiting for enrolment session '{response.PendingIdentifier}' to be approved.{Environment.NewLine}Reason: {response.PendingReason}", true, progress);
|
||||
Presentation.UpdateStatus($"Pending Device Enrolment Approval: {response.PendingIdentifier}", $"Server: {Program.ServerUrl}{Environment.NewLine}Reason: {response.PendingReason}", true, progress);
|
||||
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(10));
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
@ECHO OFF
|
||||
IF /I "%USERDOMAIN%"=="NT AUTHORITY" GOTO RunAsNetworkService
|
||||
Disco.Client.exe
|
||||
Disco.Client.exe %1 %2 %3
|
||||
EXIT /B 0
|
||||
|
||||
:RunAsNetworkService
|
||||
ECHO #Running,Launching Preparation Client, Please wait...{newline}Starting client as 'NT AUTHORITY\Network Service',true,-1
|
||||
PsExec -acceptula -i -u "NT AUTHORITY\Network Service" -w "%CD%" "%CD%\Start.bat"
|
||||
EXIT /B 0
|
||||
PsExec -acceptula -i -u "NT AUTHORITY\Network Service" -w "%CD%" "%CD%\Start.bat %1 %2 %3"
|
||||
EXIT /B 0
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using Disco.Client.Interop;
|
||||
using Disco.ClientBootstrapper.Interop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
@@ -6,46 +8,191 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.ClientBootstrapper
|
||||
{
|
||||
class BootstrapperLoop
|
||||
internal class BootstrapperLoop
|
||||
{
|
||||
|
||||
public Thread LoopThread;
|
||||
public delegate void LoopCompleteCallback();
|
||||
private LoopCompleteCallback mLoopCompleteCallback;
|
||||
private IStatus statusUI;
|
||||
private readonly Func<CancellationToken, Task> completeCallback;
|
||||
private readonly CancellationToken cancellationToken;
|
||||
private readonly IStatus statusUI;
|
||||
private readonly Uri forcedServerUrl;
|
||||
private string tempWorkingDirectory;
|
||||
private StringBuilder errorMessage;
|
||||
private Process clientProcess;
|
||||
|
||||
//#if DEBUG
|
||||
// public const string DiscoServerName = "WS-GSHARP";
|
||||
// public const int DiscoServerPort = 57252;
|
||||
//#else
|
||||
public const string DiscoServerName = "DISCO";
|
||||
public const int DiscoServerPort = 9292;
|
||||
//#endif
|
||||
|
||||
public BootstrapperLoop(IStatus StatusUI, LoopCompleteCallback Callback)
|
||||
public BootstrapperLoop(IStatus statusUI, Uri forcedServerUrl, Func<CancellationToken, Task> callback, CancellationToken cancellationToken)
|
||||
{
|
||||
statusUI = StatusUI;
|
||||
mLoopCompleteCallback = Callback;
|
||||
errorMessage = new StringBuilder();
|
||||
this.statusUI = statusUI;
|
||||
this.forcedServerUrl = forcedServerUrl;
|
||||
completeCallback = callback;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
LoopThread = new Thread(new ThreadStart(loopHost));
|
||||
LoopThread.Start();
|
||||
Task.Factory.StartNew(async () =>
|
||||
{
|
||||
await Loop(forcedServerUrl, cancellationToken);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
private void loopHost()
|
||||
private async Task Loop(Uri forcedServerUrl, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
loop();
|
||||
statusUI.UpdateStatus("System Preparation (Bootstrapper)", "Starting", "Please wait...", true, -1);
|
||||
|
||||
tempWorkingDirectory = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), @"Disco\Temp");
|
||||
if (!Directory.Exists(tempWorkingDirectory))
|
||||
Directory.CreateDirectory(tempWorkingDirectory);
|
||||
|
||||
// Check for Network Connectivity
|
||||
statusUI.UpdateStatus(null, "Detecting Network", "Checking network connectivity, Please wait...", true, -1);
|
||||
if (!NetworkInterop.HasNetworkConnectivity())
|
||||
{
|
||||
statusUI.UpdateStatus(null, "Detecting Network", "No network connectivity detected, Diagnosing...", true, -1);
|
||||
statusUI_WriteAdapterInfo();
|
||||
|
||||
if (!NetworkInterop.HasNetworkConnectivity())
|
||||
{
|
||||
// Check for Wireless
|
||||
var hasWireless = (NetworkInterop.NetworkAdapters.Count(na => na.IsWireless) > 0);
|
||||
if (hasWireless)
|
||||
{
|
||||
// True: Do wireless loop
|
||||
statusUI.UpdateStatus(null, "Configuring Wireless Network", "Wireless adapter detected, Configuring...", true, -1);
|
||||
await NetworkInterop.ConfigureWireless(cancellationToken);
|
||||
statusUI.UpdateStatus(null, "Waiting for Wireless Network", null, true, 0);
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
statusUI_WriteAdapterInfo();
|
||||
statusUI.UpdateStatus(null, null, null, true, i);
|
||||
await Program.SleepThread(2000, false, cancellationToken);
|
||||
if (NetworkInterop.HasNetworkConnectivity())
|
||||
break;
|
||||
}
|
||||
if (!NetworkInterop.HasNetworkConnectivity())
|
||||
{
|
||||
statusUI.UpdateStatus(null, "Wireless Network Failed", "Unable to connect to the wireless network, please connect the network cable...", false);
|
||||
await Program.SleepThread(3000, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
if (!NetworkInterop.HasNetworkConnectivity())
|
||||
{
|
||||
// Instruct user to connect network cable
|
||||
statusUI.UpdateStatus(null, "Please connect the network cable", null);
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
statusUI_WriteAdapterInfo();
|
||||
statusUI.UpdateStatus(null, null, null, true, i);
|
||||
await Program.SleepThread(2000, false, cancellationToken);
|
||||
if (NetworkInterop.HasNetworkConnectivity())
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!NetworkInterop.HasNetworkConnectivity())
|
||||
{
|
||||
// Client Failed
|
||||
if (completeCallback != null)
|
||||
await completeCallback(cancellationToken);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Tuple<Uri, string> serverDiscovery;
|
||||
statusUI.UpdateStatus(null, "Detecting Disco ICT", "Locating Disco ICT Server, Please wait...", true, -1);
|
||||
try
|
||||
{
|
||||
serverDiscovery = EndpointDiscovery.DiscoverServer(forcedServerUrl);
|
||||
statusUI.UpdateStatus(null, null, $"{serverDiscovery.Item1} ({serverDiscovery.Item2})", true, -1);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
statusUI.UpdateStatus(null, null, "Failed to locate Disco ICT Server, exiting...", true, -1);
|
||||
await Program.SleepThread(2000, false, cancellationToken);
|
||||
throw;
|
||||
}
|
||||
|
||||
// Download Client
|
||||
statusUI.UpdateStatus(null, "Downloading", "Retrieving Preparation Client, Please wait...", true, -1);
|
||||
string clientSourceLocation = Path.Combine(tempWorkingDirectory, "PreparationClient.zip");
|
||||
using (var webClient = new WebClient())
|
||||
{
|
||||
// Don't use a proxy when downloading the Client
|
||||
webClient.Proxy = new WebProxy();
|
||||
webClient.Headers.Add("X-DiscoICT-Discovery", serverDiscovery.Item2);
|
||||
try
|
||||
{
|
||||
webClient.DownloadFile(new Uri(serverDiscovery.Item1, "/Services/Client/PreparationClient"), clientSourceLocation);
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
if (ex.Response != null &&
|
||||
ex.Response is HttpWebResponse response)
|
||||
{
|
||||
if (response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
statusUI.UpdateStatus(null, "Download failed: Bad Request", response.StatusDescription, true, -1);
|
||||
await Program.SleepThread(5000, false, cancellationToken);
|
||||
}
|
||||
else if (response.StatusCode == HttpStatusCode.InternalServerError)
|
||||
{
|
||||
statusUI.UpdateStatus(null, "Download failed: Something went wrong on the server", "Review logs for more information (Configuration > Logging)", true, -1);
|
||||
await Program.SleepThread(5000, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Unzip Client
|
||||
statusUI.UpdateStatus(null, "Extracting", "Retrieving Preparation Client, Please wait...", true, -1);
|
||||
string clientLocation = Path.Combine(tempWorkingDirectory, "PreparationClient");
|
||||
if (Directory.Exists(clientLocation))
|
||||
Directory.Delete(clientLocation, true);
|
||||
|
||||
Directory.CreateDirectory(clientLocation);
|
||||
using (var clientSource = Ionic.Zip.ZipFile.Read(clientSourceLocation))
|
||||
{
|
||||
clientSource.ExtractAll(clientLocation, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently);
|
||||
}
|
||||
|
||||
// Launch Client
|
||||
statusUI.UpdateStatus("System Preparation (Client)", "Running", "Launching Preparation Client, Please wait...", true, -1);
|
||||
ProcessStartInfo clientProcessStart = new ProcessStartInfo(Path.Combine(clientLocation, "Start.bat"), $"2 {Process.GetCurrentProcess().Id} {serverDiscovery.Item1}")
|
||||
{
|
||||
WorkingDirectory = clientLocation,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
};
|
||||
using (clientProcess = Process.Start(clientProcessStart))
|
||||
{
|
||||
// Read StdOutput until End
|
||||
try
|
||||
{
|
||||
clientProcess.OutputDataReceived += new DataReceivedEventHandler(clientProcess_OutputDataReceived);
|
||||
clientProcess.BeginOutputReadLine();
|
||||
clientProcess.WaitForExit();
|
||||
}
|
||||
catch (Exception) { throw; }
|
||||
finally
|
||||
{
|
||||
try { clientProcess.CloseMainWindow(); }
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
clientProcess = null;
|
||||
|
||||
// Cleanup
|
||||
if (Directory.Exists(tempWorkingDirectory))
|
||||
Directory.Delete(tempWorkingDirectory, true);
|
||||
CertificateInterop.RemoveTempCerts();
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -53,167 +200,20 @@ namespace Disco.ClientBootstrapper
|
||||
return;
|
||||
if (ex.GetType() == typeof(ThreadInterruptedException))
|
||||
return;
|
||||
Program.WriteAppError(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void loop()
|
||||
{
|
||||
|
||||
#if Debug
|
||||
statusUI.UpdateStatus("Waiting for Debugger", "Please wait...", true, -1);
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
System.Threading.Thread.Sleep(10);
|
||||
} while (!System.Diagnostics.Debugger.IsAttached);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
statusUI.UpdateStatus("Error", ex.Message, true, -1);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
statusUI.UpdateStatus("System Preparation (Bootstrapper)", "Starting", "Please wait...", true, -1);
|
||||
#endif
|
||||
|
||||
tempWorkingDirectory = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco\\Temp");
|
||||
if (!Directory.Exists(tempWorkingDirectory))
|
||||
Directory.CreateDirectory(tempWorkingDirectory);
|
||||
|
||||
// Check for Network Connectivity
|
||||
statusUI.UpdateStatus(null, "Detecting Network", "Checking network connectivity, Please wait...", true, -1);
|
||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
{
|
||||
statusUI.UpdateStatus(null, "Detecting Network", "No network connectivity detected, Diagnosing...", true, -1);
|
||||
statusUI_WriteAdapterInfo();
|
||||
|
||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
{
|
||||
// Check for Wireless
|
||||
var hasWireless = (Interop.NetworkInterop.NetworkAdapters.Count(na => na.IsWireless) > 0);
|
||||
if (hasWireless)
|
||||
{
|
||||
// True: Do wireless loop
|
||||
statusUI.UpdateStatus(null, "Configuring Wireless Network", "Wireless adapter detected, Configuring...", true, -1);
|
||||
Interop.NetworkInterop.ConfigureWireless();
|
||||
statusUI.UpdateStatus(null, "Waiting for Wireless Network", null, true, 0);
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
statusUI_WriteAdapterInfo();
|
||||
statusUI.UpdateStatus(null, null, null, true, i);
|
||||
Program.SleepThread(500, false);
|
||||
if (Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
break;
|
||||
}
|
||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
{
|
||||
statusUI.UpdateStatus(null, "Wireless Network Failed", "Unable to connect to the wireless network, please connect the network cable...", false);
|
||||
Program.SleepThread(3000, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
{
|
||||
// Instruct user to connect network cable
|
||||
statusUI.UpdateStatus(null, "Please connect the network cable", null);
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
statusUI_WriteAdapterInfo();
|
||||
statusUI.UpdateStatus(null, null, null, true, i);
|
||||
Program.SleepThread(500, false);
|
||||
if (Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
{
|
||||
// Client Failed
|
||||
if (mLoopCompleteCallback != null)
|
||||
{
|
||||
mLoopCompleteCallback.BeginInvoke(null, null);
|
||||
}
|
||||
if (ex.GetType() == typeof(OperationCanceledException))
|
||||
return;
|
||||
}
|
||||
Program.WriteAppError(ex);
|
||||
}
|
||||
|
||||
// Download Client
|
||||
statusUI.UpdateStatus(null, "Downloading", "Retrieving Preparation Client, Please wait...", true, -1);
|
||||
string clientSourceLocation = Path.Combine(tempWorkingDirectory, "PreparationClient.zip");
|
||||
using (var webClient = new WebClient())
|
||||
{
|
||||
// Don't use a proxy when downloading the Client
|
||||
webClient.Proxy = new WebProxy();
|
||||
|
||||
webClient.DownloadFile($"http://{DiscoServerName}:{DiscoServerPort}/Services/Client/PreparationClient", clientSourceLocation);
|
||||
}
|
||||
|
||||
// Unzip Client
|
||||
statusUI.UpdateStatus(null, "Extracting", "Retrieving Preparation Client, Please wait...", true, -1);
|
||||
string clientLocation = Path.Combine(tempWorkingDirectory, "PreparationClient");
|
||||
if (Directory.Exists(clientLocation))
|
||||
Directory.Delete(clientLocation, true);
|
||||
|
||||
Directory.CreateDirectory(clientLocation);
|
||||
using (var clientSource = Ionic.Zip.ZipFile.Read(clientSourceLocation))
|
||||
{
|
||||
clientSource.ExtractAll(clientLocation, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently);
|
||||
}
|
||||
|
||||
// Launch Client
|
||||
statusUI.UpdateStatus("System Preparation (Client)", "Running", "Launching Preparation Client, Please wait...", true, -1);
|
||||
ProcessStartInfo clientProcessStart = new ProcessStartInfo(Path.Combine(clientLocation, "Start.bat"))
|
||||
{
|
||||
WorkingDirectory = clientLocation,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
};
|
||||
using (clientProcess = Process.Start(clientProcessStart))
|
||||
{
|
||||
// Read StdOutput until End
|
||||
try
|
||||
{
|
||||
clientProcess.OutputDataReceived += new DataReceivedEventHandler(clientProcess_OutputDataReceived);
|
||||
clientProcess.BeginOutputReadLine();
|
||||
clientProcess.WaitForExit();
|
||||
}
|
||||
catch (Exception) { throw; }
|
||||
finally
|
||||
{
|
||||
try { clientProcess.CloseMainWindow(); }
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
clientProcess = null;
|
||||
|
||||
// Cleanup
|
||||
if (Directory.Exists(tempWorkingDirectory))
|
||||
Directory.Delete(tempWorkingDirectory, true);
|
||||
Interop.CertificateInterop.RemoveTempCerts();
|
||||
|
||||
// Pause if Error
|
||||
if (errorMessage.Length > 0)
|
||||
{
|
||||
Program.SleepThread(10000, true);
|
||||
}
|
||||
|
||||
// End Of Loop
|
||||
if (mLoopCompleteCallback != null)
|
||||
{
|
||||
mLoopCompleteCallback.BeginInvoke(null, null);
|
||||
}
|
||||
if (completeCallback != null)
|
||||
await completeCallback(cancellationToken);
|
||||
}
|
||||
|
||||
void statusUI_WriteAdapterInfo()
|
||||
private void statusUI_WriteAdapterInfo()
|
||||
{
|
||||
|
||||
var info = new StringBuilder();
|
||||
foreach (var na in Interop.NetworkInterop.NetworkAdapters)
|
||||
foreach (var na in NetworkInterop.NetworkAdapters)
|
||||
{
|
||||
if (na.IsWireless)
|
||||
{
|
||||
@@ -228,11 +228,10 @@ namespace Disco.ClientBootstrapper
|
||||
|
||||
}
|
||||
|
||||
void clientProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
private void clientProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(e.Data))
|
||||
{
|
||||
Debug.WriteLine($"OUTPUT: {e.Data}");
|
||||
var data = e.Data.Substring(1).Split(new char[] { ',' });
|
||||
switch (e.Data[0])
|
||||
{
|
||||
@@ -249,15 +248,5 @@ namespace Disco.ClientBootstrapper
|
||||
}
|
||||
}
|
||||
|
||||
//void clientProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
|
||||
//{
|
||||
// if (!string.IsNullOrEmpty(e.Data))
|
||||
// {
|
||||
// System.Diagnostics.Debug.WriteLine(string.Format("ERROR: {0}", e.Data));
|
||||
// this.errorMessage.AppendLine(e.Data);
|
||||
// statusUI.UpdateStatus(null, "An Error Occurred", this.errorMessage.ToString(), false);
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,9 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Disco.Client\Interop\EndpointDiscovery.cs">
|
||||
<Link>Interop\EndpointDiscovery.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Resources\Libraries\DotNetZip\Source\BZip2\BitWriter.cs">
|
||||
<Link>DotNetZip\BZip2\BitWriter.cs</Link>
|
||||
</Compile>
|
||||
|
||||
@@ -7,22 +7,22 @@ namespace Disco.ClientBootstrapper
|
||||
{
|
||||
|
||||
private delegate void dUpdateStatus(string Heading, string SubHeading, string Message, bool? ShowProgress, int? Progress);
|
||||
private dUpdateStatus mUpdateStatus;
|
||||
private readonly dUpdateStatus mUpdateStatus;
|
||||
|
||||
public FormStatus()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
var version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
||||
this.labelVersion.Text = $"v{version.ToString(3)}";
|
||||
labelVersion.Text = $"v{version.ToString(3)}";
|
||||
|
||||
this.FormClosed += new FormClosedEventHandler(FormStatus_FormClosed);
|
||||
FormClosed += new FormClosedEventHandler(FormStatus_FormClosed);
|
||||
|
||||
mUpdateStatus = new dUpdateStatus(UpdateStatusDo);
|
||||
Cursor.Hide();
|
||||
}
|
||||
|
||||
void FormStatus_FormClosed(object sender, FormClosedEventArgs e)
|
||||
private void FormStatus_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
Cursor.Show();
|
||||
Program.ExitApplication();
|
||||
@@ -32,43 +32,43 @@ namespace Disco.ClientBootstrapper
|
||||
{
|
||||
try
|
||||
{
|
||||
this.Invoke(mUpdateStatus, Heading, SubHeading, Message, ShowProgress, Progress);
|
||||
Invoke(mUpdateStatus, Heading, SubHeading, Message, ShowProgress, Progress);
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
private void UpdateStatusDo(string Heading, string SubHeading, string Message, bool? ShowProgress, int? Progress)
|
||||
{
|
||||
if (Heading != null)
|
||||
if (this.labelHeading.Text != Heading)
|
||||
this.labelHeading.Text = Heading;
|
||||
if (labelHeading.Text != Heading)
|
||||
labelHeading.Text = Heading;
|
||||
if (SubHeading != null)
|
||||
if (this.labelSubHeading.Text != SubHeading)
|
||||
this.labelSubHeading.Text = SubHeading;
|
||||
if (labelSubHeading.Text != SubHeading)
|
||||
labelSubHeading.Text = SubHeading;
|
||||
if (Message != null)
|
||||
if (this.labelMessage.Text != Message)
|
||||
this.labelMessage.Text = Message;
|
||||
if (labelMessage.Text != Message)
|
||||
labelMessage.Text = Message;
|
||||
|
||||
if (ShowProgress.HasValue)
|
||||
{
|
||||
if (ShowProgress.Value)
|
||||
{
|
||||
this.progressBar.Visible = true;
|
||||
progressBar.Visible = true;
|
||||
if (Progress.HasValue)
|
||||
{
|
||||
if (Progress.Value >= 0)
|
||||
{
|
||||
this.progressBar.Value = Math.Min(Progress.Value, 100);
|
||||
this.progressBar.Style = ProgressBarStyle.Continuous;
|
||||
progressBar.Value = Math.Min(Progress.Value, 100);
|
||||
progressBar.Style = ProgressBarStyle.Continuous;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.progressBar.Style = ProgressBarStyle.Marquee;
|
||||
progressBar.Style = ProgressBarStyle.Marquee;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.progressBar.Visible = false;
|
||||
progressBar.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +1,49 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.ClientBootstrapper
|
||||
{
|
||||
class InstallLoop
|
||||
internal class InstallLoop
|
||||
{
|
||||
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||
private readonly string installLocation;
|
||||
private readonly string wimImageId;
|
||||
private readonly string tempPath;
|
||||
private readonly Action completeCallback;
|
||||
private readonly Uri forcedServerUrl;
|
||||
|
||||
public Thread LoopThread;
|
||||
public delegate void CompleteCallback();
|
||||
private CompleteCallback mCompleteCallback;
|
||||
private string InstallLocation;
|
||||
private string WimImageId;
|
||||
private string TempPath;
|
||||
|
||||
public InstallLoop(string InstallLocation, string WimImageId, string TempPath)
|
||||
public InstallLoop(string installLocation, string wimImageId, string tempPath, Action completeCallback, Uri forcedServerUrl)
|
||||
{
|
||||
this.InstallLocation = InstallLocation;
|
||||
this.WimImageId = WimImageId;
|
||||
this.TempPath = TempPath;
|
||||
this.installLocation = installLocation;
|
||||
this.wimImageId = wimImageId;
|
||||
this.tempPath = tempPath;
|
||||
this.completeCallback = completeCallback;
|
||||
this.forcedServerUrl = forcedServerUrl;
|
||||
}
|
||||
|
||||
public void Start(CompleteCallback Callback)
|
||||
public void Start()
|
||||
{
|
||||
mCompleteCallback = Callback;
|
||||
LoopThread = new Thread(new ThreadStart(loopHost));
|
||||
LoopThread.Start();
|
||||
}
|
||||
private void loopHost()
|
||||
{
|
||||
try
|
||||
var cancellationToken = cancellationTokenSource.Token;
|
||||
Task.Run(async () =>
|
||||
{
|
||||
|
||||
//Program.Status.UpdateStatus(null, null, "Testing UI");
|
||||
//Program.SleepThread(5000, false);
|
||||
Interop.InstallInterop.Install(InstallLocation, WimImageId, TempPath);
|
||||
if (mCompleteCallback != null)
|
||||
try
|
||||
{
|
||||
mCompleteCallback.BeginInvoke(null, null);
|
||||
await Interop.InstallInterop.Install(installLocation, wimImageId, tempPath, forcedServerUrl, cancellationToken);
|
||||
completeCallback?.BeginInvoke(null, null);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex.GetType() == typeof(ThreadAbortException))
|
||||
return;
|
||||
if (ex.GetType() == typeof(ThreadInterruptedException))
|
||||
return;
|
||||
Program.WriteAppError(ex);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex.GetType() == typeof(ThreadAbortException))
|
||||
return;
|
||||
if (ex.GetType() == typeof(ThreadInterruptedException))
|
||||
return;
|
||||
if (ex.GetType() == typeof(OperationCanceledException))
|
||||
return;
|
||||
Program.WriteAppError(ex);
|
||||
throw;
|
||||
}
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
@@ -20,12 +22,12 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
//Remove(StoreName.Root, StoreLocation.LocalMachine, _tempCerts);
|
||||
}
|
||||
}
|
||||
public static void AddTempCerts()
|
||||
public static async Task AddTempCerts(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_tempCerts == null)
|
||||
_tempCerts = new List<string>();
|
||||
|
||||
var inlineCertificateLocation = Program.InlinePath.Value;
|
||||
var inlineCertificateLocation = Path.GetDirectoryName(typeof(Program).Assembly.Location);
|
||||
|
||||
// Root Certificates
|
||||
try
|
||||
@@ -35,6 +37,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
foreach (var certFile in CertFiles)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password");
|
||||
var result = Add(StoreName.Root, StoreLocation.LocalMachine, cert);
|
||||
if (result)
|
||||
@@ -42,7 +45,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
|
||||
_tempCerts.Add(cert.SerialNumber);
|
||||
Program.Status.UpdateStatus(null, null, $"Added Root Certificate: {cert.ShortSubjectName()}");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,6 +63,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
foreach (var certFile in CertFiles)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password");
|
||||
var result = Add(StoreName.CertificateAuthority, StoreLocation.LocalMachine, cert);
|
||||
if (result)
|
||||
@@ -67,7 +71,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
|
||||
_tempCerts.Add(cert.SerialNumber);
|
||||
Program.Status.UpdateStatus(null, null, $"Added Intermediate Certificate: {cert.ShortSubjectName()}");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,6 +89,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
foreach (var certFile in CertFiles)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
|
||||
var result = Add(StoreName.My, StoreLocation.LocalMachine, cert);
|
||||
if (result)
|
||||
@@ -92,7 +97,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
|
||||
_tempCerts.Add(cert.SerialNumber);
|
||||
Program.Status.UpdateStatus(null, null, $"Added Host Certificate: {cert.ShortSubjectName()}");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,17 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
public static class InstallInterop
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
|
||||
private static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
|
||||
[Flags]
|
||||
enum MoveFileFlags
|
||||
private enum MoveFileFlags
|
||||
{
|
||||
MOVEFILE_REPLACE_EXISTING = 0x00000001,
|
||||
MOVEFILE_COPY_ALLOWED = 0x00000002,
|
||||
@@ -22,19 +24,19 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
|
||||
}
|
||||
|
||||
private static void Install(string RootFilesystemLocation, RegistryKey RootRegistryLocation, string FilesystemInstallLocation, string VirtualRootFilesystemLocation)
|
||||
private static async Task Install(string rootFilesystemLocation, RegistryKey rootRegistryLocation, string filesystemInstallLocation, string virtualRootFilesystemLocation, Uri forcedServerUrl, CancellationToken cancellationToken)
|
||||
{
|
||||
var SourceLocation = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
|
||||
var InstallLocation = Path.Combine(RootFilesystemLocation, FilesystemInstallLocation);
|
||||
var BootstrapperCmdLinePath = Path.Combine(VirtualRootFilesystemLocation, FilesystemInstallLocation, "Disco.ClientBootstrapper.exe");
|
||||
var InstallLocation = Path.Combine(rootFilesystemLocation, filesystemInstallLocation);
|
||||
var BootstrapperCmdLinePath = Path.Combine(virtualRootFilesystemLocation, filesystemInstallLocation, "Disco.ClientBootstrapper.exe");
|
||||
|
||||
var GroupPolicyScriptsIniLocation = Path.Combine(RootFilesystemLocation, "Windows\\System32\\GroupPolicy\\Machine\\Scripts\\scripts.ini");
|
||||
var GroupPolicyScriptsIniBackupLocation = Path.Combine(RootFilesystemLocation, "Windows\\System32\\GroupPolicy\\Machine\\Scripts\\disco_scripts.ini");
|
||||
var GroupPolicyScriptsIniLocation = Path.Combine(rootFilesystemLocation, @"Windows\System32\GroupPolicy\Machine\Scripts\scripts.ini");
|
||||
var GroupPolicyScriptsIniBackupLocation = Path.Combine(rootFilesystemLocation, @"Windows\System32\GroupPolicy\Machine\Scripts\disco_scripts.ini");
|
||||
|
||||
// Create file system Location
|
||||
#region "Create File System Location"
|
||||
Program.Status.UpdateStatus(null, null, "Creating Installation Location");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
if (Directory.Exists(InstallLocation))
|
||||
{
|
||||
// Try and Delete Directory
|
||||
@@ -52,19 +54,23 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
var installDir = Directory.CreateDirectory(InstallLocation);
|
||||
installDir.Attributes = installDir.Attributes | FileAttributes.Hidden;
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
#endregion
|
||||
|
||||
// Copy files to file system location
|
||||
#region "Copy to File System"
|
||||
Program.Status.UpdateStatus(null, null, "Copying Files");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
|
||||
// Copy Bootstrapper
|
||||
// ie: Executing Assembly
|
||||
File.Copy(System.Reflection.Assembly.GetExecutingAssembly().Location, Path.Combine(InstallLocation, "Disco.ClientBootstrapper.exe"));
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(SourceLocation))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var fileName = Path.GetFileName(file);
|
||||
|
||||
// Only Copy Certain Files
|
||||
@@ -86,7 +92,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
// Backup & Create Group Policy Scripts.ini
|
||||
#region "Group Policy Scripts.ini"
|
||||
Program.Status.UpdateStatus(null, null, "Creating Group Policy Script Entry");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
// Backup
|
||||
if (!File.Exists(GroupPolicyScriptsIniBackupLocation))
|
||||
{
|
||||
@@ -95,6 +101,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
File.Move(GroupPolicyScriptsIniLocation, GroupPolicyScriptsIniBackupLocation);
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Create
|
||||
if (File.Exists(GroupPolicyScriptsIniLocation))
|
||||
@@ -105,56 +112,67 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
using (var scriptsIniStreamWriter = new StreamWriter(scriptsIniStream, Encoding.Unicode))
|
||||
{
|
||||
scriptsIniStreamWriter.Write($"[Startup]{Environment.NewLine}0CmdLine={BootstrapperCmdLinePath}{Environment.NewLine}0Parameters=/AllowUninstall");
|
||||
scriptsIniStreamWriter.Flush();
|
||||
scriptsIniStreamWriter.WriteLine("[Startup]");
|
||||
scriptsIniStreamWriter.WriteLine($"0CmdLine={BootstrapperCmdLinePath}");
|
||||
if (forcedServerUrl == null)
|
||||
scriptsIniStreamWriter.WriteLine("0Parameters=/AllowUninstall");
|
||||
else
|
||||
scriptsIniStreamWriter.WriteLine($"0Parameters=/AllowUninstall {forcedServerUrl}");
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
#endregion
|
||||
|
||||
// Backup & Create Group Policy Registry
|
||||
#region "Group Policy Registry"
|
||||
Program.Status.UpdateStatus(null, null, "Creating Group Policy Registry Entries");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
// Backup Scripts
|
||||
using (var regGroupPolicy = RootRegistryLocation.OpenSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy", true))
|
||||
using (var regGroupPolicy = rootRegistryLocation.OpenSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy", true))
|
||||
{
|
||||
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Scripts") && !regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
||||
{
|
||||
RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts");
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Create Scripts
|
||||
RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Shutdown").Dispose();
|
||||
using (var regScriptsStartup = RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Startup\\0"))
|
||||
rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown").Dispose();
|
||||
using (var regScriptsStartup = rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0"))
|
||||
{
|
||||
regScriptsStartup.SetValue("GPO-ID", "LocalGPO", RegistryValueKind.String);
|
||||
regScriptsStartup.SetValue("SOM-ID", "Local", RegistryValueKind.String);
|
||||
regScriptsStartup.SetValue("FileSysPath", Path.Combine(Environment.SystemDirectory, "GroupPolicy\\Machine"), RegistryValueKind.String);
|
||||
regScriptsStartup.SetValue("FileSysPath", Path.Combine(Environment.SystemDirectory, @"GroupPolicy\Machine"), RegistryValueKind.String);
|
||||
regScriptsStartup.SetValue("DisplayName", "Local Group Policy", RegistryValueKind.String);
|
||||
regScriptsStartup.SetValue("GPOName", "Local Group Policy", RegistryValueKind.String);
|
||||
regScriptsStartup.SetValue("PSScriptOrder", 1, RegistryValueKind.DWord);
|
||||
using (var regScriptsStartup0 = regScriptsStartup.CreateSubKey("0"))
|
||||
{
|
||||
regScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String);
|
||||
regScriptsStartup0.SetValue("Parameters", "/AllowUninstall", RegistryValueKind.String);
|
||||
if (forcedServerUrl == null)
|
||||
regScriptsStartup0.SetValue("Parameters", "/AllowUninstall", RegistryValueKind.String);
|
||||
else
|
||||
regScriptsStartup0.SetValue("Parameters", $"/AllowUninstall {forcedServerUrl}", RegistryValueKind.String);
|
||||
regScriptsStartup0.SetValue("IsPowershell", 0, RegistryValueKind.DWord);
|
||||
regScriptsStartup0.SetValue("ExecTime", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, RegistryValueKind.Binary);
|
||||
}
|
||||
}
|
||||
RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Shutdown").Dispose();
|
||||
rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Shutdown").Dispose();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Backup Scripts State
|
||||
using (var regGroupPolicy = RootRegistryLocation.OpenSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine", true))
|
||||
using (var regGroupPolicy = rootRegistryLocation.OpenSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\State\Machine", true))
|
||||
{
|
||||
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Scripts") && !regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
||||
{
|
||||
RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts");
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Create Scripts State
|
||||
using (var regStateScriptsStartup = RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Startup\\0"))
|
||||
using (var regStateScriptsStartup = rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup\0"))
|
||||
{
|
||||
regStateScriptsStartup.SetValue("GPO-ID", "LocalGPO", RegistryValueKind.String);
|
||||
regStateScriptsStartup.SetValue("SOM-ID", "Local", RegistryValueKind.String);
|
||||
@@ -165,17 +183,21 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
using (var regStateScriptsStartup0 = regStateScriptsStartup.CreateSubKey("0"))
|
||||
{
|
||||
regStateScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String);
|
||||
regStateScriptsStartup0.SetValue("Parameters", "/AllowUninstall", RegistryValueKind.String);
|
||||
if (forcedServerUrl == null)
|
||||
regStateScriptsStartup0.SetValue("Parameters", "/AllowUninstall", RegistryValueKind.String);
|
||||
else
|
||||
regStateScriptsStartup0.SetValue("Parameters", $"/AllowUninstall {forcedServerUrl}", RegistryValueKind.String);
|
||||
regStateScriptsStartup0.SetValue("ExecTime", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, RegistryValueKind.Binary);
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
#endregion
|
||||
|
||||
// Set Registry Startup Environment Policies
|
||||
#region "Registry Startup Policies"
|
||||
Program.Status.UpdateStatus(null, null, "Creating Startup Policy Registry Entries");
|
||||
Program.SleepThread(500, false);
|
||||
using (var regWinlogon = RootRegistryLocation.OpenSubKey("Microsoft\\Windows NT\\CurrentVersion\\Winlogon", true))
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
using (var regWinlogon = rootRegistryLocation.OpenSubKey(@"Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
||||
{
|
||||
regWinlogon.SetValue("HideStartupScripts", 0, RegistryValueKind.DWord);
|
||||
regWinlogon.SetValue("RunStartupScriptSync", 1, RegistryValueKind.DWord);
|
||||
@@ -183,94 +205,110 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static void Install(string InstallLocation, string WimImageId, string TempPath)
|
||||
public static async Task Install(string installLocation, string wimImageId, string tempPath, Uri forcedServerUrl, CancellationToken cancellationToken)
|
||||
{
|
||||
Program.Status.UpdateStatus("Installing Bootstrapper", "Starting", "Please wait...", false);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(InstallLocation))
|
||||
InstallLocation = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco");
|
||||
if (string.IsNullOrWhiteSpace(installLocation))
|
||||
installLocation = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco");
|
||||
|
||||
if (InstallLocation.EndsWith(".wim", StringComparison.OrdinalIgnoreCase))
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (installLocation.EndsWith(".wim", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Offline File System (WIM)
|
||||
Program.Status.UpdateStatus("Installing Bootstrapper (Offline)", "Installing", $"Install Location: {InstallLocation}");
|
||||
Program.SleepThread(1000, false);
|
||||
Program.Status.UpdateStatus("Installing Bootstrapper (Offline)", "Installing", $"Install Location: {installLocation}");
|
||||
await Program.SleepThread(1000, false, cancellationToken);
|
||||
|
||||
// Mount WIM
|
||||
int wimImageIndex = 0;
|
||||
using (var wim = new WIMInterop.WindowsImageContainer(InstallLocation, WIMInterop.WindowsImageContainer.CreateFileMode.OpenExisting, WIMInterop.WindowsImageContainer.CreateFileAccess.Write))
|
||||
using (var wim = new WIMInterop.WindowsImageContainer(installLocation, WIMInterop.WindowsImageContainer.CreateFileMode.OpenExisting, WIMInterop.WindowsImageContainer.CreateFileAccess.Write))
|
||||
{
|
||||
if (WimImageId == null)
|
||||
WimImageId = "1";
|
||||
if (!int.TryParse(WimImageId, out wimImageIndex))
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (wimImageId == null)
|
||||
wimImageId = "1";
|
||||
if (!int.TryParse(wimImageId, out wimImageIndex))
|
||||
{
|
||||
Program.Status.UpdateStatus(null, "Analysing WIM", $"Looking for Image Name: {WimImageId}");
|
||||
Program.SleepThread(500, false);
|
||||
Program.Status.UpdateStatus(null, "Analysing WIM", $"Looking for Image Name: {wimImageId}");
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
for (int i = 0; i < wim.ImageCount; i++)
|
||||
{
|
||||
var wimImageInfo = new System.Xml.XmlDocument();
|
||||
using (var wimImage = wim[i])
|
||||
wimImageInfo.LoadXml(wimImage.ImageInformation);
|
||||
var wimImageInfoName = wimImageInfo.SelectSingleNode("//IMAGE/NAME");
|
||||
if (wimImageInfoName != null && wimImageInfoName.InnerText.Equals(WimImageId, StringComparison.OrdinalIgnoreCase))
|
||||
if (wimImageInfoName != null && wimImageInfoName.InnerText.Equals(wimImageId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
wimImageIndex = i + 1;
|
||||
Program.Status.UpdateStatus(null, "Analysing WIM", $"Found Image Id '{WimImageId}' at Index {wimImageIndex}");
|
||||
Program.SleepThread(500, false);
|
||||
Program.Status.UpdateStatus(null, "Analysing WIM", $"Found Image Id '{wimImageId}' at Index {wimImageIndex}");
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (wimImageIndex == 0)
|
||||
{
|
||||
Program.Status.UpdateStatus(null, "Error", $"Unable to load WIM Image Id: {WimImageId}");
|
||||
Program.SleepThread(5000, false);
|
||||
Program.Status.UpdateStatus(null, "Error", $"Unable to load WIM Image Id: {wimImageId}");
|
||||
await Program.SleepThread(5000, false, cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get Temp Path
|
||||
var wimMountPath = Path.Combine(TempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimMount");
|
||||
var wimMountPath = Path.Combine(tempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimMount");
|
||||
if (Directory.Exists(wimMountPath))
|
||||
Directory.Delete(wimMountPath, true);
|
||||
Directory.CreateDirectory(wimMountPath);
|
||||
|
||||
var wimTempMountPath = Path.Combine(TempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimTempMount");
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var wimTempMountPath = Path.Combine(tempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimTempMount");
|
||||
if (Directory.Exists(wimTempMountPath))
|
||||
Directory.Delete(wimTempMountPath, true);
|
||||
Directory.CreateDirectory(wimTempMountPath);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
bool wimCommitChanges = true;
|
||||
WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback m_MessageCallback = null;
|
||||
try
|
||||
{
|
||||
// Mount WIM
|
||||
Program.Status.UpdateStatus(null, "Mounting WIM", $"Mounting WIM Image to '{wimMountPath}'");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
m_MessageCallback = new WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback(WimImageEventMessagePump);
|
||||
WIMInterop.WindowsImageContainer.NativeMethods.RegisterCallback(m_MessageCallback);
|
||||
|
||||
WIMInterop.WindowsImageContainer.NativeMethods.MountImage(wimMountPath, InstallLocation, wimImageIndex, wimTempMountPath);
|
||||
WIMInterop.WindowsImageContainer.NativeMethods.MountImage(wimMountPath, installLocation, wimImageIndex, wimTempMountPath);
|
||||
|
||||
// Load Local Machine Registry
|
||||
var wimHivePath = Path.Combine(wimMountPath, "Windows\\System32\\config\\SOFTWARE");
|
||||
Program.Status.UpdateStatus(null, "Mounting Offline Registry Hive", $"Mounting Offline Registry Hive at '{wimHivePath}'");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
using (var wimReg = new RegistryInterop(RegistryInterop.RegistryHives.HKEY_LOCAL_MACHINE, "DiscoClientBootstrapperWimHive", wimHivePath))
|
||||
{
|
||||
using (RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("DiscoClientBootstrapperWimHive", true))
|
||||
try
|
||||
{
|
||||
string rootFileSystemLocation = wimMountPath;
|
||||
string fileSystemInstallLocation = "Disco";
|
||||
string virtualRootFileSystemLocation = "C:\\";
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
using (RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("DiscoClientBootstrapperWimHive", true))
|
||||
{
|
||||
string rootFileSystemLocation = wimMountPath;
|
||||
string fileSystemInstallLocation = "Disco";
|
||||
string virtualRootFileSystemLocation = "C:\\";
|
||||
|
||||
Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, virtualRootFileSystemLocation);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, virtualRootFileSystemLocation, forcedServerUrl, cancellationToken);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Unload Local Machine Registry
|
||||
Program.Status.UpdateStatus(null, "Unmounting Offline Registry Hive", $"Unmounting Offline Registry Hive at '{wimHivePath}'");
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
wimReg.Unload();
|
||||
}
|
||||
|
||||
// Unload Local Machine Registry
|
||||
Program.Status.UpdateStatus(null, "Unmounting Offline Registry Hive", $"Unmounting Offline Registry Hive at '{wimHivePath}'");
|
||||
Program.SleepThread(500, false);
|
||||
wimReg.Unload();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
@@ -282,8 +320,8 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
// Unmount WIM
|
||||
Program.Status.UpdateStatus(null, "Unmounting WIM", $"Unmounting WIM Image at '{wimMountPath}'");
|
||||
Program.SleepThread(500, false);
|
||||
WIMInterop.WindowsImageContainer.NativeMethods.DismountImage(wimMountPath, InstallLocation, wimImageIndex, wimCommitChanges);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
WIMInterop.WindowsImageContainer.NativeMethods.DismountImage(wimMountPath, installLocation, wimImageIndex, wimCommitChanges);
|
||||
|
||||
if (m_MessageCallback != null)
|
||||
{
|
||||
@@ -295,23 +333,25 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
Directory.Delete(wimMountPath, true);
|
||||
if (Directory.Exists(wimTempMountPath))
|
||||
Directory.Delete(wimTempMountPath, true);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Online File System
|
||||
Program.Status.UpdateStatus("Installing Bootstrapper (Online)", "Installing", $"Install Location: {InstallLocation}", true, -1);
|
||||
Program.SleepThread(1000, false);
|
||||
string rootFileSystemLocation = Path.GetPathRoot(InstallLocation);
|
||||
Program.Status.UpdateStatus("Installing Bootstrapper (Online)", "Installing", $"Install Location: {installLocation}", true, -1);
|
||||
await Program.SleepThread(1000, false, cancellationToken);
|
||||
string rootFileSystemLocation = Path.GetPathRoot(installLocation);
|
||||
RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("SOFTWARE", true);
|
||||
string fileSystemInstallLocation = InstallLocation.Substring(rootFileSystemLocation.Length);
|
||||
string fileSystemInstallLocation = installLocation.Substring(rootFileSystemLocation.Length);
|
||||
|
||||
Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, rootFileSystemLocation);
|
||||
await Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, rootFileSystemLocation, forcedServerUrl, cancellationToken);
|
||||
Program.Status.UpdateStatus(null, "Online File System Installation Complete", string.Empty, true, -1);
|
||||
Program.SleepThread(1000, false);
|
||||
await Program.SleepThread(1000, false, cancellationToken);
|
||||
}
|
||||
Program.Status.UpdateStatus(null, "Complete", "Finished Installing Bootstrapper");
|
||||
Program.SleepThread(1500, false);
|
||||
await Program.SleepThread(1500, false, cancellationToken);
|
||||
}
|
||||
|
||||
private static uint WimImageEventMessagePump(
|
||||
@@ -349,41 +389,28 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
return status;
|
||||
}
|
||||
|
||||
public static void Uninstall()
|
||||
public static async Task Uninstall(CancellationToken cancellationToken)
|
||||
{
|
||||
// Application Directory
|
||||
var appDirectory = Program.InlinePath.Value;
|
||||
if (Program.AllowUninstall && !appDirectory.StartsWith("\\\\"))
|
||||
var appDirectory = Path.GetDirectoryName(typeof(Program).Assembly.Location);
|
||||
if (Program.AllowUninstall && !appDirectory.StartsWith(@"\\"))
|
||||
{
|
||||
Program.Status.UpdateStatus("System Preparation (Bootstrapper)", "Uninstalling Bootstrapper...", string.Empty, false, 0);
|
||||
Program.SleepThread(1000, true);
|
||||
//var uninstallScriptLocation = System.IO.Path.Combine(appDirectory, "UninstallBootstrapper.vbs");
|
||||
//if (System.IO.File.Exists(uninstallScriptLocation))
|
||||
//{
|
||||
// var bootstrapperPID = System.Diagnostics.Process.GetCurrentProcess().Id;
|
||||
// var cscriptPath = System.IO.Path.Combine(Environment.SystemDirectory, "cscript.exe");
|
||||
// var cscriptArgs = string.Format("\"{0}\" /WaitForProcessID:{1}", uninstallScriptLocation, bootstrapperPID);
|
||||
|
||||
// var startProc = new ProcessStartInfo(cscriptPath, cscriptArgs);
|
||||
// startProc.WorkingDirectory = Environment.SystemDirectory;
|
||||
// startProc.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
|
||||
// Process.Start(startProc);
|
||||
//}
|
||||
await Program.SleepThread(1000, true, cancellationToken);
|
||||
|
||||
// Remove Registry Entries
|
||||
using (var regWinlogon = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", true))
|
||||
using (var regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
||||
{
|
||||
regWinlogon.DeleteValue("HideStartupScripts", false);
|
||||
regWinlogon.DeleteValue("RunStartupScriptSync", false);
|
||||
}
|
||||
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Shutdown", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Startup", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Shutdown", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Startup", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Shutdown", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup", false);
|
||||
|
||||
// Restore Registry Backups
|
||||
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy", true))
|
||||
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy", true))
|
||||
{
|
||||
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
||||
{
|
||||
@@ -391,7 +418,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
RegistryUtilities.RenameSubKey(regGroupPolicy, "Disco_Scripts", "Scripts");
|
||||
}
|
||||
}
|
||||
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine", true))
|
||||
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine", true))
|
||||
{
|
||||
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
||||
{
|
||||
@@ -401,10 +428,10 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
}
|
||||
|
||||
// Delete Group Policy Script File
|
||||
var groupPolicyScriptsPath = Path.Combine(Environment.SystemDirectory, "GroupPolicy\\Machine\\Scripts\\scripts.ini");
|
||||
var groupPolicyScriptsPath = Path.Combine(Environment.SystemDirectory, @"GroupPolicy\Machine\Scripts\scripts.ini");
|
||||
if (File.Exists(groupPolicyScriptsPath))
|
||||
File.Delete(groupPolicyScriptsPath);
|
||||
var groupPolicyScriptsBackupPath = Path.Combine(Environment.SystemDirectory, "GroupPolicy\\Machine\\Scripts\\disco_scripts.ini");
|
||||
var groupPolicyScriptsBackupPath = Path.Combine(Environment.SystemDirectory, @"GroupPolicy\Machine\Scripts\disco_scripts.ini");
|
||||
if (File.Exists(groupPolicyScriptsBackupPath))
|
||||
File.Move(groupPolicyScriptsBackupPath, groupPolicyScriptsPath);
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
||||
namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
static class NetworkInterop
|
||||
internal static class NetworkInterop
|
||||
{
|
||||
|
||||
#region PInvoke
|
||||
@@ -164,30 +167,35 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
}
|
||||
}
|
||||
|
||||
public static bool PingDiscoIct(string ServerName)
|
||||
public static bool HasNetworkConnectivity()
|
||||
{
|
||||
using (Ping p = new Ping())
|
||||
var nics = NetworkInterface.GetAllNetworkInterfaces()
|
||||
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
|
||||
.ToList();
|
||||
|
||||
foreach (var nic in nics)
|
||||
{
|
||||
try
|
||||
if (nic.Supports(NetworkInterfaceComponent.IPv4))
|
||||
{
|
||||
PingReply pr = p.Send(ServerName, 2000);
|
||||
if (pr.Status == IPStatus.Success)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
var ipProps = nic.GetIPProperties();
|
||||
var ipv4Props = ipProps.GetIPv4Properties();
|
||||
if (ipv4Props.IsAutomaticPrivateAddressingActive)
|
||||
continue;
|
||||
|
||||
return ipProps.UnicastAddresses
|
||||
.Where(ua => ua.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
||||
.Any();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void ConfigureWireless()
|
||||
public static async Task ConfigureWireless(CancellationToken cancellationToken)
|
||||
{
|
||||
// Add Certificates
|
||||
Program.Status.UpdateStatus(null, null, "Configuring Wireless Certificates");
|
||||
CertificateInterop.AddTempCerts();
|
||||
await CertificateInterop.AddTempCerts(cancellationToken);
|
||||
|
||||
// Add Wireless Profiles
|
||||
Program.Status.UpdateStatus(null, null, "Configuring Wireless Profiles");
|
||||
@@ -208,15 +216,16 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
foreach (var inlineWirelessProfile in wirelessInlineProfiles)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (inlineWirelessProfile.AddProfile(wlanHandle, na.Guid))
|
||||
{
|
||||
Program.Status.UpdateStatus(null, null, $"Added Wireless Profile: {inlineWirelessProfile.ProfileName}");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
Program.Status.UpdateStatus(null, null, $"Unable to add Wireless Profile: {inlineWirelessProfile.ProfileName}");
|
||||
Program.SleepThread(5000, false);
|
||||
await Program.SleepThread(5000, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,14 +255,15 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
|
||||
private static List<WirelessProfile> GetInlineWirelessProfiles()
|
||||
{
|
||||
var inlineProfileFiles = System.IO.Directory.EnumerateFiles(Program.InlinePath.Value, "WLAN_Profile_*.xml").ToList();
|
||||
var directoryPath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
|
||||
var inlineProfileFiles = Directory.EnumerateFiles(directoryPath, "WLAN_Profile_*.xml").ToList();
|
||||
var inlineProfiles = new List<WirelessProfile>(inlineProfileFiles.Count);
|
||||
foreach (var filename in inlineProfileFiles)
|
||||
{
|
||||
var profile = new WirelessProfile()
|
||||
{
|
||||
Filename = filename,
|
||||
ProfileXml = System.IO.File.ReadAllText(filename)
|
||||
ProfileXml = File.ReadAllText(filename)
|
||||
};
|
||||
var profileXml = new XmlDocument();
|
||||
profileXml.LoadXml(profile.ProfileXml);
|
||||
|
||||
@@ -1,36 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Disco.ClientBootstrapper
|
||||
{
|
||||
static class Program
|
||||
internal static class Program
|
||||
{
|
||||
public static IStatus Status { get; set; }
|
||||
public static BootstrapperLoop BootstrapperLoop { get; set; }
|
||||
public static InstallLoop InstallLoop { get; set; }
|
||||
private static readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||
public static IStatus Status { get; private set; }
|
||||
|
||||
public static List<string> PostBootstrapperActions { get; set; }
|
||||
public static bool AllowUninstall { get; set; }
|
||||
public static bool ApplicationExiting { get; set; }
|
||||
public static Lazy<string> InlinePath = new Lazy<string>(() =>
|
||||
{
|
||||
return System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
|
||||
});
|
||||
public static bool AllowUninstall { get; private set; }
|
||||
public static Uri ForcedServerUrl { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main(string[] args)
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||
|
||||
|
||||
if (args.Length > 0)
|
||||
{
|
||||
#if DEBUG
|
||||
if (args.Any(a => a.Equals("debug", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
do
|
||||
{
|
||||
Console.WriteLine("Waiting for Debugger to Attach");
|
||||
Thread.Sleep(1000);
|
||||
} while (!System.Diagnostics.Debugger.IsAttached);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (args.Any(a => a.StartsWith("http://", StringComparison.OrdinalIgnoreCase)))
|
||||
throw new ArgumentException("Only HTTPS URLs are supported for a forced server URL.");
|
||||
var forcedServerArg = args.FirstOrDefault(a => a.StartsWith("https://", StringComparison.OrdinalIgnoreCase));
|
||||
if (forcedServerArg != null)
|
||||
{
|
||||
if (Uri.TryCreate(forcedServerArg, UriKind.Absolute, out var forcedUri))
|
||||
ForcedServerUrl = forcedUri;
|
||||
else
|
||||
throw new ArgumentException("The provided forced server URL is not valid.");
|
||||
}
|
||||
|
||||
switch (args[0].ToLower())
|
||||
{
|
||||
case "/install":
|
||||
@@ -46,14 +68,17 @@ namespace Disco.ClientBootstrapper
|
||||
wimImage = args[2];
|
||||
if (args.Length > 3)
|
||||
tempPath = args[3];
|
||||
InstallLoop = new InstallLoop(installLocation, wimImage, tempPath);
|
||||
InstallLoop.Start(new InstallLoop.CompleteCallback(InstallComplete));
|
||||
var installLoop = new InstallLoop(installLocation, wimImage, tempPath, InstallComplete, ForcedServerUrl);
|
||||
installLoop.Start();
|
||||
Application.Run();
|
||||
return;
|
||||
case "/uninstall":
|
||||
AllowUninstall = true;
|
||||
Status = new NullStatus();
|
||||
Interop.InstallInterop.Uninstall();
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Interop.InstallInterop.Uninstall(cancellationTokenSource.Token);
|
||||
}).Wait(cancellationTokenSource.Token);
|
||||
return;
|
||||
case "/allowuninstall":
|
||||
AllowUninstall = true;
|
||||
@@ -71,13 +96,13 @@ namespace Disco.ClientBootstrapper
|
||||
statusForm.Show();
|
||||
}
|
||||
|
||||
BootstrapperLoop = new BootstrapperLoop(Status, new BootstrapperLoop.LoopCompleteCallback(LoopComplete));
|
||||
BootstrapperLoop.Start();
|
||||
var bootstrapperLoop = new BootstrapperLoop(Status, ForcedServerUrl, LoopComplete, cancellationTokenSource.Token);
|
||||
bootstrapperLoop.Start();
|
||||
|
||||
Application.Run();
|
||||
}
|
||||
|
||||
static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
|
||||
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
|
||||
{
|
||||
WriteAppError(e.Exception);
|
||||
}
|
||||
@@ -100,7 +125,7 @@ namespace Disco.ClientBootstrapper
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
public static void LoopComplete()
|
||||
public static async Task LoopComplete(CancellationToken cancellationToken)
|
||||
{
|
||||
// Run Post Actions
|
||||
if (PostBootstrapperActions != null)
|
||||
@@ -108,32 +133,32 @@ namespace Disco.ClientBootstrapper
|
||||
// Check Uninstall
|
||||
if (AllowUninstall && PostBootstrapperActions.Contains("UninstallBootstrapper"))
|
||||
{
|
||||
Interop.InstallInterop.Uninstall();
|
||||
await Interop.InstallInterop.Uninstall(cancellationToken);
|
||||
}
|
||||
|
||||
// Check ShutdownActions
|
||||
if (PostBootstrapperActions.Contains("Shutdown"))
|
||||
{
|
||||
Status.UpdateStatus("System Preparation (Bootstrapper)", "Shutting Down; Finished...", string.Empty, false, 0);
|
||||
SleepThread(4000, true);
|
||||
await SleepThread(4000, true, cancellationToken);
|
||||
Interop.ShutdownInterop.Shutdown();
|
||||
}
|
||||
else if (PostBootstrapperActions.Contains("Reboot"))
|
||||
{
|
||||
Status.UpdateStatus("System Preparation (Bootstrapper)", "Rebooting; Finished...", string.Empty, false, 0);
|
||||
SleepThread(4000, true);
|
||||
await SleepThread(4000, true, cancellationToken);
|
||||
Interop.ShutdownInterop.Reboot();
|
||||
}
|
||||
else
|
||||
{
|
||||
Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0);
|
||||
SleepThread(2000, true);
|
||||
await SleepThread(2000, true, cancellationToken);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0);
|
||||
SleepThread(2000, true);
|
||||
await SleepThread(2000, true, cancellationToken);
|
||||
}
|
||||
|
||||
ExitApplication();
|
||||
@@ -146,33 +171,12 @@ namespace Disco.ClientBootstrapper
|
||||
|
||||
public static void ExitApplication()
|
||||
{
|
||||
if (!ApplicationExiting)
|
||||
{
|
||||
ApplicationExiting = true;
|
||||
if (BootstrapperLoop != null)
|
||||
{
|
||||
if (BootstrapperLoop.LoopThread != null)
|
||||
{
|
||||
if (BootstrapperLoop.LoopThread.ThreadState == ThreadState.WaitSleepJoin)
|
||||
{
|
||||
BootstrapperLoop.LoopThread.Interrupt();
|
||||
}
|
||||
if (BootstrapperLoop.LoopThread.ThreadState == ThreadState.Running)
|
||||
{
|
||||
BootstrapperLoop.LoopThread.Abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
Application.Exit();
|
||||
}
|
||||
if (!cancellationTokenSource.IsCancellationRequested)
|
||||
cancellationTokenSource.Cancel();
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
public static void Trace(string Format, params string[] args)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(Format, args);
|
||||
}
|
||||
|
||||
public static void SleepThread(int millisecondsTimeout, bool updateUI)
|
||||
public static async Task SleepThread(int millisecondsTimeout, bool updateUI, CancellationToken cancellationToken)
|
||||
{
|
||||
if (updateUI)
|
||||
{
|
||||
@@ -180,12 +184,12 @@ namespace Disco.ClientBootstrapper
|
||||
{
|
||||
int progress = Convert.ToInt32(((Convert.ToDouble(i) / Convert.ToDouble(millisecondsTimeout)) * 100));
|
||||
Status.UpdateStatus(null, null, null, true, progress);
|
||||
Thread.Sleep(500);
|
||||
await Task.Delay(500, cancellationToken);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(millisecondsTimeout);
|
||||
await Task.Delay(millisecondsTimeout, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,11 @@ namespace Disco.Data.Configuration.Modules
|
||||
get => Get(DeviceExportOptions.DefaultOptions());
|
||||
set => Set(value);
|
||||
}
|
||||
|
||||
public bool EnrollmentLegacyDiscoveryDisabled
|
||||
{
|
||||
get => Get(false);
|
||||
set => Set(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.Entity" />
|
||||
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll</HintPath>
|
||||
<Reference Include="System.ValueTuple, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.ValueTuple.4.6.2\lib\net462\System.ValueTuple.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
@@ -62,6 +62,7 @@
|
||||
<Compile Include="Repository\FlagType.cs" />
|
||||
<Compile Include="Repository\User\UserComment.cs" />
|
||||
<Compile Include="Repository\FlagPermission.cs" />
|
||||
<Compile Include="Services\Devices\DeviceEnrolmentServerDiscoveryMethod.cs" />
|
||||
<Compile Include="Services\Devices\DeviceFlags\DeviceFlagExportOptions.cs" />
|
||||
<Compile Include="Services\Devices\DeviceFlags\DeviceFlagExportRecord.cs" />
|
||||
<Compile Include="Services\Documents\DocumentExportOptions.cs" />
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Disco.Models.Services.Devices
|
||||
{
|
||||
public enum DeviceEnrolmentServerDiscoveryMethod
|
||||
{
|
||||
Unknown = 0,
|
||||
Manual = 1,
|
||||
SRV = 2,
|
||||
VicSmart = 3,
|
||||
Legacy = 4,
|
||||
Mac = 50,
|
||||
MacSecure = 51,
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,6 @@ namespace Disco.Models.Services.Interop.ActiveDirectory
|
||||
{
|
||||
public string GroupId { get; set; }
|
||||
public DateTime? FilterBeginDate { get; set; }
|
||||
public bool UpdateDescription { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace Disco.Models.Services.Interop.DiscoServices
|
||||
|
||||
public List<StatisticIntPair> Stat_JobIdentifiers { get; set; }
|
||||
public List<StatisticJob> Stat_Jobs { get; set; }
|
||||
public List<StatisticInt> Stat_EnrollmentDiscovery { get; set; }
|
||||
|
||||
public class StatisticIntPair
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Disco.Models.Services.Jobs.Noticeboards
|
||||
{
|
||||
@@ -9,6 +10,7 @@ namespace Disco.Models.Services.Jobs.Noticeboards
|
||||
string DeviceSerialNumber { get; }
|
||||
string DeviceComputerNameFriendly { get; }
|
||||
string DeviceComputerName { get; }
|
||||
string DeviceName { get; }
|
||||
|
||||
string DeviceLocation { get; }
|
||||
string DeviceDescription { get; }
|
||||
@@ -16,6 +18,7 @@ namespace Disco.Models.Services.Jobs.Noticeboards
|
||||
int DeviceProfileId { get; }
|
||||
int? DeviceAddressId { get; }
|
||||
string DeviceAddressShortName { get; }
|
||||
IEnumerable<int> JobQueueIds { get; }
|
||||
|
||||
string UserId { get; }
|
||||
string UserIdFriendly { get; }
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
namespace Disco.Models.UI.Config.Enrolment
|
||||
using System;
|
||||
|
||||
namespace Disco.Models.UI.Config.Enrolment
|
||||
{
|
||||
public interface ConfigEnrolmentIndexModel : BaseUIModel
|
||||
{
|
||||
string MacSshUsername { get; set; }
|
||||
int PendingTimeoutMinutes { get; set; }
|
||||
Uri MacEnrolUrl { get; set; }
|
||||
bool HostingPluginInstalled { get; set; }
|
||||
bool IsVicSmartDeployment { get; set; }
|
||||
bool IsServicesEducationVicGovAuDomain { get; set; }
|
||||
string DnsSrvRecordName { get; set; }
|
||||
string DnsSrvRecordValue { get; set; }
|
||||
bool LegacyDiscoveryEnabled { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="13.0.2" targetFramework="net45" />
|
||||
<package id="System.ValueTuple" version="4.5.0" targetFramework="net462" />
|
||||
<package id="System.ValueTuple" version="4.6.2" targetFramework="net462" />
|
||||
</packages>
|
||||
@@ -41,6 +41,10 @@
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
|
||||
@@ -56,6 +56,10 @@
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.ClientServices;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Devices;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Users;
|
||||
@@ -18,6 +19,18 @@ namespace Disco.Services.Devices.Enrolment
|
||||
private static readonly string pendingIdentifierAlphabet = "23456789ABCDEFGHJKMNPQRSTWXYZ";
|
||||
private static readonly Random pendingIdentifierRng = new Random();
|
||||
private static readonly ConcurrentDictionary<string, EnrolResponse> pendingEnrolments = new ConcurrentDictionary<string, EnrolResponse>();
|
||||
private static readonly Dictionary<DeviceEnrolmentServerDiscoveryMethod, int> discoveryMethodStatistics = Enum.GetValues(typeof(DeviceEnrolmentServerDiscoveryMethod)).Cast<DeviceEnrolmentServerDiscoveryMethod>().ToDictionary(k => k, k => 0);
|
||||
|
||||
public static string GetDnsServiceLocationRecordName()
|
||||
=> $"_discoict._tcp.{ActiveDirectory.Context.PrimaryDomain.Name}";
|
||||
|
||||
public static void IncrementDiscoveryMethod(DeviceEnrolmentServerDiscoveryMethod method)
|
||||
{
|
||||
discoveryMethodStatistics[method]++;
|
||||
}
|
||||
|
||||
public static IEnumerable<KeyValuePair<DeviceEnrolmentServerDiscoveryMethod, int>> GetDiscoveryMethodStatistics()
|
||||
=> discoveryMethodStatistics.AsEnumerable();
|
||||
|
||||
private static void CleanupPendingEnrolments()
|
||||
{
|
||||
@@ -175,9 +188,11 @@ namespace Disco.Services.Devices.Enrolment
|
||||
{
|
||||
if (!authenticatedToken.Has(Claims.ComputerAccount))
|
||||
throw new EnrolmentSafeException($"Connection not correctly authenticated (SN: {Request.SerialNumber}; Auth User: {authenticatedToken.User.UserId})");
|
||||
else if (!string.Equals($"{Request.ComputerName}$", authenticatedToken.User.DomainUsername, StringComparison.OrdinalIgnoreCase))
|
||||
throw new InvalidOperationException($"Connection not correctly authenticated (SN: {Request.SerialNumber}; Computer Name: {Request.ComputerName}; Auth User: {authenticatedToken.User.UserId})");
|
||||
|
||||
if (domain == null)
|
||||
domain = ActiveDirectory.Context.GetDomainByName(Request.DNSDomainName);
|
||||
if (domain == null && !ActiveDirectory.Context.TryGetDomainByName(Request.DNSDomainName, out domain))
|
||||
throw new EnrolmentSafeException($"The specified domain name '{Request.DNSDomainName}' is not recognized or reachable.");
|
||||
|
||||
if (!authenticatedToken.User.UserId.Equals($@"{domain.NetBiosName}\{Request.ComputerName}$", StringComparison.OrdinalIgnoreCase))
|
||||
throw new EnrolmentSafeException($"Connection not correctly authenticated (SN: {Request.SerialNumber}; Auth User: {authenticatedToken.User.UserId})");
|
||||
@@ -379,9 +394,7 @@ namespace Disco.Services.Devices.Enrolment
|
||||
EnrolmentLog.LogSessionTaskProvisioningADAccount(sessionId, device.SerialNumber, device.DeviceDomainId);
|
||||
adMachineAccount = domainController.Value.RetrieveADMachineAccount(device.DeviceDomainId);
|
||||
|
||||
response.OfflineDomainJoinManifest = domainController.Value.OfflineDomainJoinProvision(device.DeviceDomainId, device.DeviceProfile.OrganisationalUnit, ref adMachineAccount, out var offlineProvisionDiagnosicInfo);
|
||||
|
||||
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, offlineProvisionDiagnosicInfo);
|
||||
response.OfflineDomainJoinManifest = domainController.Value.OfflineDomainJoinProvision(device.DeviceDomainId, device.DeviceProfile.OrganisationalUnit, ref adMachineAccount);
|
||||
|
||||
response.RequireReboot = true;
|
||||
}
|
||||
@@ -428,10 +441,7 @@ namespace Disco.Services.Devices.Enrolment
|
||||
response.ComputerName = calculatedAccountUsername;
|
||||
|
||||
// Create New Account
|
||||
|
||||
response.OfflineDomainJoinManifest = domainController.Value.OfflineDomainJoinProvision(device.DeviceDomainId, device.DeviceProfile.OrganisationalUnit, ref adMachineAccount, out var offlineProvisionDiagnosicInfo);
|
||||
|
||||
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, offlineProvisionDiagnosicInfo);
|
||||
response.OfflineDomainJoinManifest = domainController.Value.OfflineDomainJoinProvision(device.DeviceDomainId, device.DeviceProfile.OrganisationalUnit, ref adMachineAccount);
|
||||
|
||||
response.RequireReboot = true;
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
<Reference Include="LumenWorks.Framework.IO">
|
||||
<HintPath>..\packages\LumenWorks.Framework.IO.3.8.0\lib\net20\LumenWorks.Framework.IO.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNet.SignalR.Core, Version=2.1.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNet.SignalR.Core.2.1.2\lib\net45\Microsoft.AspNet.SignalR.Core.dll</HintPath>
|
||||
<Reference Include="Microsoft.AspNet.SignalR.Core, Version=2.4.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNet.SignalR.Core.2.4.3\lib\net45\Microsoft.AspNet.SignalR.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNetCore.Connections.Abstractions, Version=9.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNetCore.Connections.Abstractions.9.0.0\lib\net462\Microsoft.AspNetCore.Connections.Abstractions.dll</HintPath>
|
||||
@@ -243,8 +243,8 @@
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll</HintPath>
|
||||
<Reference Include="System.ValueTuple, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.ValueTuple.4.6.2\lib\net462\System.ValueTuple.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
@@ -464,6 +464,7 @@
|
||||
<Compile Include="Interop\ActiveDirectory\ActiveDirectoryGroupCache.cs" />
|
||||
<Compile Include="Interop\ActiveDirectory\ActiveDirectoryManagedGroups.cs" />
|
||||
<Compile Include="Interop\ActiveDirectory\ADDeviceDescriptionUpdateTask.cs" />
|
||||
<Compile Include="Interop\ActiveDirectory\ADDeviceOfflineDomainJoining.cs" />
|
||||
<Compile Include="Interop\ActiveDirectory\ADDirectoryEntry.cs" />
|
||||
<Compile Include="Interop\ActiveDirectory\ADDiscoverServers.cs" />
|
||||
<Compile Include="Interop\ActiveDirectory\ADDomain.cs" />
|
||||
@@ -484,6 +485,7 @@
|
||||
<Compile Include="Interop\ActiveDirectory\IADObject.cs" />
|
||||
<Compile Include="Interop\DiscoServices\ActivationCleanupTask.cs" />
|
||||
<Compile Include="Interop\DiscoServices\ActivationService.cs" />
|
||||
<Compile Include="Interop\DiscoServices\AuthenticationSessionScope.cs" />
|
||||
<Compile Include="Interop\DiscoServices\DiscoServiceHelpers.cs" />
|
||||
<Compile Include="Interop\DiscoServices\Jobs.cs" />
|
||||
<Compile Include="Interop\DiscoServices\LicenseValidationTask.cs" />
|
||||
@@ -496,6 +498,14 @@
|
||||
<Compile Include="Interop\DiscoServices\Upload\UploadOnlineClient.cs" />
|
||||
<Compile Include="Interop\DiscoServices\Upload\UploadOnlineService.cs" />
|
||||
<Compile Include="Interop\DiscoServices\Upload\UploadOnlineSyncTask.cs" />
|
||||
<Compile Include="Interop\DNS\ADnsRecord.cs" />
|
||||
<Compile Include="Interop\DNS\CnameDnsRecord.cs" />
|
||||
<Compile Include="Interop\DNS\DnsRecord.cs" />
|
||||
<Compile Include="Interop\DNS\DnsRecordType.cs" />
|
||||
<Compile Include="Interop\DNS\DnsService.cs" />
|
||||
<Compile Include="Interop\DNS\NativeDns.cs" />
|
||||
<Compile Include="Interop\DNS\SrvDnsRecord.cs" />
|
||||
<Compile Include="Interop\DNS\TxtDnsRecord.cs" />
|
||||
<Compile Include="Interop\IIS\PreserveIisBindingsTask.cs" />
|
||||
<Compile Include="Interop\MimeTypes.cs" />
|
||||
<Compile Include="Interop\VicEduDept\VicSmart.cs" />
|
||||
|
||||
@@ -149,27 +149,28 @@ namespace Disco.Services
|
||||
template = database.DocumentTemplates.Find(templateId);
|
||||
if (template == null)
|
||||
throw new ArgumentException("Invalid document template id", nameof(templateId));
|
||||
if (!Enum.TryParse(template.Scope, out AttachmentTypes scope))
|
||||
throw new InvalidOperationException("Unknown DocumentType Scope");
|
||||
|
||||
// validate authorization
|
||||
switch (template.Scope)
|
||||
switch (scope)
|
||||
{
|
||||
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||
case AttachmentTypes.Device:
|
||||
authorization.Require(Claims.Device.Actions.GenerateDocuments);
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||
case AttachmentTypes.Job:
|
||||
authorization.Require(Claims.Job.Actions.GenerateDocuments);
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||
case AttachmentTypes.User:
|
||||
authorization.Require(Claims.User.Actions.GenerateDocuments);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException("Unknown DocumentType Scope");
|
||||
throw new InvalidOperationException("Unsupported DocumentType Scope");
|
||||
}
|
||||
|
||||
// resolve target
|
||||
target = template.ResolveScopeTarget(database, targetId, out targetUser);
|
||||
if (target == null)
|
||||
throw new ArgumentException("Target not found", nameof(targetId));
|
||||
target = template.ResolveScopeTarget(database, targetId, out targetUser)
|
||||
?? throw new ArgumentException("Target not found", nameof(targetId));
|
||||
}
|
||||
|
||||
public static IEnumerable<OnImportUserFlagRule> GetOnImportUserFlagRuleDetails(this DocumentTemplate template, DiscoDataContext database)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Documents;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Documents;
|
||||
using Disco.Services.Expressions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -139,12 +141,46 @@ namespace Disco.Services
|
||||
}
|
||||
|
||||
public static IAttachmentTarget ResolveScopeTarget(this DocumentTemplatePackage templatePackage, DiscoDataContext database, string targetId)
|
||||
=> templatePackage.ResolveScopeTarget(database, targetId, out _);
|
||||
|
||||
public static IAttachmentTarget ResolveScopeTarget(this DocumentTemplatePackage templatePackage, DiscoDataContext database, string targetId, out User targetUser)
|
||||
{
|
||||
if (templatePackage == null)
|
||||
throw new ArgumentNullException(nameof(templatePackage));
|
||||
|
||||
return templatePackage.Scope.ResolveScopeTarget(database, targetId);
|
||||
return templatePackage.Scope.ResolveScopeTarget(database, targetId, out targetUser);
|
||||
}
|
||||
|
||||
public static void GetPackageAndTarget(DiscoDataContext database, AuthorizationToken authorization, string packageId, string targetId, out DocumentTemplatePackage package, out IAttachmentTarget target, out User targetUser)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(packageId))
|
||||
throw new ArgumentNullException(nameof(packageId));
|
||||
if (string.IsNullOrWhiteSpace(targetId))
|
||||
throw new ArgumentNullException(nameof(targetId));
|
||||
|
||||
// get template
|
||||
package = DocumentTemplatePackages.GetPackage(packageId)
|
||||
?? throw new ArgumentException("Invalid document template package id", nameof(packageId));
|
||||
|
||||
// validate authorization
|
||||
switch (package.Scope)
|
||||
{
|
||||
case AttachmentTypes.Device:
|
||||
authorization.Require(Claims.Device.Actions.GenerateDocuments);
|
||||
break;
|
||||
case AttachmentTypes.Job:
|
||||
authorization.Require(Claims.Job.Actions.GenerateDocuments);
|
||||
break;
|
||||
case AttachmentTypes.User:
|
||||
authorization.Require(Claims.User.Actions.GenerateDocuments);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException("Unsupported DocumentType Scope");
|
||||
}
|
||||
|
||||
// resolve target
|
||||
target = package.ResolveScopeTarget(database, targetId, out targetUser)
|
||||
?? throw new ArgumentException("Target not found", nameof(targetId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Disco.Services.Expressions
|
||||
@@ -200,18 +201,31 @@ namespace Disco.Services.Expressions
|
||||
var detailsService = new DetailsProviderService(Database);
|
||||
if (target != null)
|
||||
{
|
||||
var detailsTarget = default(User);
|
||||
if (target is User targetUser)
|
||||
{
|
||||
detailsVariables.Add("UserDetails", new LazyDictionary(() => detailsService.GetDetails(targetUser)));
|
||||
}
|
||||
detailsTarget = targetUser;
|
||||
else if (target is Job targetJob)
|
||||
{
|
||||
detailsVariables.Add("UserDetails", targetJob.User == null ? (IDictionary<string, string>)new Dictionary<string, string>() : new LazyDictionary(() => detailsService.GetDetails(targetJob.User)));
|
||||
}
|
||||
detailsTarget = targetJob.User;
|
||||
else if (target is Device targetDevice)
|
||||
{
|
||||
detailsVariables.Add("UserDetails", targetDevice.AssignedUser == null ? (IDictionary<string, string>)new Dictionary<string, string>() : new LazyDictionary(() => detailsService.GetDetails(targetDevice.AssignedUser)));
|
||||
}
|
||||
detailsTarget = targetDevice.AssignedUser;
|
||||
|
||||
if (detailsTarget != null)
|
||||
detailsVariables.Add("UserDetails", new LazyDictionary(() =>
|
||||
{
|
||||
var details = detailsService.GetDetails(detailsTarget);
|
||||
foreach (var key in details.Keys.ToList())
|
||||
{
|
||||
if (key.EndsWith("*") || key.EndsWith("&"))
|
||||
{
|
||||
var baseKey = key.Substring(0, key.Length - 1);
|
||||
if (!details.ContainsKey(baseKey))
|
||||
details[baseKey] = details[key];
|
||||
}
|
||||
}
|
||||
return details;
|
||||
}));
|
||||
else
|
||||
detailsVariables.Add("UserDetails", new Dictionary<string, string>());
|
||||
}
|
||||
|
||||
return new Hashtable(detailsVariables)
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Disco.Services.Interop.ActiveDirectory
|
||||
{
|
||||
public static class ADDeviceOfflineDomainJoining
|
||||
{
|
||||
[Flags]
|
||||
private enum NETSETUP_PROVISION_FLAGS : int
|
||||
{
|
||||
NETSETUP_PROVISION_DOWNLEVEL_PRIV_SUPPORT = 0x00000001,
|
||||
NETSETUP_PROVISION_REUSE_ACCOUNT = 0x00000002,
|
||||
NETSETUP_PROVISION_USE_DEFAULT_PASSWORD = 0x00000004,
|
||||
NETSETUP_PROVISION_SKIP_ACCOUNT_SEARCH = 0x00000008,
|
||||
NETSETUP_PROVISION_ROOT_CA_CERTS = 0x00000010,
|
||||
NETSETUP_PROVISION_PERSISTENTSITE = 0x00000020,
|
||||
NETSETUP_PROVISION_ONLINE_CALLER = 0x40000000,
|
||||
NETSETUP_PROVISION_CHECK_PWD_ONLY = unchecked((int)0x80000000),
|
||||
}
|
||||
|
||||
[DllImport("Netapi32.dll", CallingConvention = CallingConvention.Winapi)]
|
||||
[return: MarshalAs(UnmanagedType.I4)]
|
||||
private static extern int NetProvisionComputerAccount(
|
||||
[In, MarshalAs(UnmanagedType.LPWStr)] string lpDomain,
|
||||
[In, MarshalAs(UnmanagedType.LPWStr)] string lpMachineName,
|
||||
[In, Optional, MarshalAs(UnmanagedType.LPWStr)] string lpMachineAccountOU,
|
||||
[In, Optional, MarshalAs(UnmanagedType.LPWStr)] string lpDcName,
|
||||
[In, MarshalAs(UnmanagedType.I4)] NETSETUP_PROVISION_FLAGS dwOptions,
|
||||
out IntPtr pProvisionBinData,
|
||||
out int pdwProvisionBinDataSize,
|
||||
[Optional] IntPtr pProvisionTextData
|
||||
);
|
||||
|
||||
[DllImport("Netapi32.dll", SetLastError = true)]
|
||||
private static extern int NetApiBufferFree(IntPtr Buffer);
|
||||
|
||||
public static string OfflineDomainJoinProvision(this ADDomainController dc, string computerSamAccountName, string organisationalUnit, ref ADMachineAccount machineAccount)
|
||||
{
|
||||
if (machineAccount != null && machineAccount.IsCriticalSystemObject)
|
||||
throw new InvalidOperationException($"This account {machineAccount.DistinguishedName} is a Critical System Active Directory Object and Disco ICT refuses to modify it");
|
||||
|
||||
if (!dc.IsWritable)
|
||||
throw new InvalidOperationException($"The domain controller [{dc.Name}] is not writable. This action (Offline Domain Join Provision) requires a writable domain controller.");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(computerSamAccountName))
|
||||
computerSamAccountName = computerSamAccountName.TrimEnd('$');
|
||||
if (!string.IsNullOrWhiteSpace(computerSamAccountName) && computerSamAccountName.Contains('\\'))
|
||||
computerSamAccountName = computerSamAccountName.Substring(computerSamAccountName.IndexOf('\\') + 1);
|
||||
|
||||
// NetBIOS Limit (16 characters; "{ComputerName}$"; 15 characters allowed)
|
||||
if (string.IsNullOrWhiteSpace(computerSamAccountName) || computerSamAccountName.Length > 15)
|
||||
throw new ArgumentException("Invalid Computer Name; > 0 and <= 15", "ComputerName");
|
||||
|
||||
// Ensure Specified OU Exists
|
||||
if (!string.IsNullOrEmpty(organisationalUnit))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var deOU = dc.RetrieveDirectoryEntry(organisationalUnit, new string[] { "distinguishedName" }))
|
||||
{
|
||||
if (deOU == null)
|
||||
throw new Exception($"OU's Directory Entry couldn't be found at [{organisationalUnit}]");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ArgumentException($"An error occurred while trying to locate the specified OU: {organisationalUnit}", "OrganisationalUnit", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (machineAccount != null && !string.Equals(machineAccount.Name, computerSamAccountName, StringComparison.Ordinal))
|
||||
{
|
||||
// rename the account
|
||||
machineAccount.RenameAccount(dc, computerSamAccountName);
|
||||
}
|
||||
|
||||
var result = NetProvisionComputerAccount(dc.Domain.Name, computerSamAccountName, string.IsNullOrWhiteSpace(organisationalUnit) ? null : organisationalUnit, dc.Name, NETSETUP_PROVISION_FLAGS.NETSETUP_PROVISION_REUSE_ACCOUNT, out var provisionDataPointer, out var provisionDataLength);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
var win32Exception = new System.ComponentModel.Win32Exception(result);
|
||||
throw new InvalidOperationException($"NetProvisionComputerAccount failed with error code {result}: {win32Exception.Message}");
|
||||
}
|
||||
|
||||
if (provisionDataPointer == IntPtr.Zero || provisionDataLength == 0)
|
||||
throw new InvalidOperationException("NetProvisionComputerAccount did not return valid provisioning data.");
|
||||
|
||||
var buffer = new byte[provisionDataLength];
|
||||
Marshal.Copy(provisionDataPointer, buffer, 0, provisionDataLength);
|
||||
var freeResult = NetApiBufferFree(provisionDataPointer);
|
||||
var encodedResult = Convert.ToBase64String(buffer);
|
||||
|
||||
// Reload Machine Account
|
||||
machineAccount = dc.RetrieveADMachineAccount($@"{dc.Domain.NetBiosName}\{computerSamAccountName}", (machineAccount == null ? null : machineAccount.LoadedProperties.Keys.ToArray()));
|
||||
|
||||
return encodedResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.DirectoryServices;
|
||||
using System.DirectoryServices.ActiveDirectory;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
|
||||
namespace Disco.Services.Interop.ActiveDirectory
|
||||
{
|
||||
public class ADDomainController
|
||||
{
|
||||
private const string LdapPathTemplate = @"LDAP://{0}/{1}";
|
||||
private ActiveDirectoryContext context;
|
||||
private readonly ActiveDirectoryContext context;
|
||||
|
||||
public DomainController DomainController { get; private set; }
|
||||
public ADDomain Domain { get; private set; }
|
||||
@@ -100,25 +98,25 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
return results;
|
||||
}
|
||||
|
||||
internal IEnumerable<ADSearchResult> SearchInternal(string SearchRoot, string LdapFilter, string[] LoadProperties, int? ResultLimit)
|
||||
internal IEnumerable<ADSearchResult> SearchInternal(string searchRoot, string ldapFilter, string[] loadProperties, int? resultLimit, bool searchSubtree = true)
|
||||
{
|
||||
if (string.IsNullOrEmpty(SearchRoot))
|
||||
if (string.IsNullOrEmpty(searchRoot))
|
||||
throw new ArgumentNullException("SearchRoot");
|
||||
if (string.IsNullOrEmpty(LdapFilter))
|
||||
if (string.IsNullOrEmpty(ldapFilter))
|
||||
throw new ArgumentNullException("LdapFilter");
|
||||
if (ResultLimit.HasValue && ResultLimit.Value < 1)
|
||||
if (resultLimit.HasValue && resultLimit.Value < 1)
|
||||
throw new ArgumentOutOfRangeException("ResultLimit", "The ResultLimit must be 1 or greater");
|
||||
|
||||
using (ADDirectoryEntry rootEntry = RetrieveDirectoryEntry(SearchRoot))
|
||||
using (ADDirectoryEntry rootEntry = RetrieveDirectoryEntry(searchRoot))
|
||||
{
|
||||
using (DirectorySearcher searcher = new DirectorySearcher(rootEntry.Entry, LdapFilter, LoadProperties, System.DirectoryServices.SearchScope.Subtree))
|
||||
using (DirectorySearcher searcher = new DirectorySearcher(rootEntry.Entry, ldapFilter, loadProperties, searchSubtree ? System.DirectoryServices.SearchScope.Subtree : System.DirectoryServices.SearchScope.OneLevel))
|
||||
{
|
||||
searcher.PageSize = 500;
|
||||
|
||||
if (ResultLimit.HasValue)
|
||||
searcher.SizeLimit = ResultLimit.Value;
|
||||
if (resultLimit.HasValue)
|
||||
searcher.SizeLimit = resultLimit.Value;
|
||||
|
||||
return searcher.FindAll().Cast<SearchResult>().Select(result => new ADSearchResult(Domain, this, SearchRoot, LdapFilter, result));
|
||||
return searcher.FindAll().Cast<SearchResult>().Select(result => new ADSearchResult(Domain, this, searchRoot, ldapFilter, result));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,7 +223,7 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
if (SecurityIdentifier == null)
|
||||
throw new ArgumentNullException("SecurityIdentifier");
|
||||
if (!SecurityIdentifier.IsEqualDomainSid(Domain.SecurityIdentifier))
|
||||
throw new ArgumentException($"The specified Security Identifier [{SecurityIdentifier.ToString()}] does not belong to this domain [{Domain.Name}]", "SecurityIdentifier");
|
||||
throw new ArgumentException($"The specified Security Identifier [{SecurityIdentifier}] does not belong to this domain [{Domain.Name}]", "SecurityIdentifier");
|
||||
|
||||
var sidBinaryString = SecurityIdentifier.ToBinaryString();
|
||||
|
||||
@@ -295,6 +293,7 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
private const string OrganisationalUnitsLdapFilter = "(objectCategory=organizationalUnit)";
|
||||
private static readonly string[] OrganisationalUnitsLoadProperties = { "name", "distinguishedName" };
|
||||
|
||||
[Obsolete("Retrieve as needed using RetrieveADOrganisationUnits(parentDistinguishedName)")]
|
||||
public List<ADOrganisationalUnit> RetrieveADOrganisationalUnitStructure()
|
||||
{
|
||||
Dictionary<string, List<ADOrganisationalUnit>> resultTree = new Dictionary<string, List<ADOrganisationalUnit>>();
|
||||
@@ -319,6 +318,15 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
|
||||
return indexedChildren[Domain.DistinguishedName];
|
||||
}
|
||||
|
||||
public List<ADOrganisationalUnit> RetrieveADOrganisationUnits(string parentDistinguishedName = null)
|
||||
{
|
||||
if (parentDistinguishedName is null)
|
||||
parentDistinguishedName = Domain.DistinguishedName;
|
||||
|
||||
return SearchInternal(parentDistinguishedName, OrganisationalUnitsLdapFilter, OrganisationalUnitsLoadProperties, null, false)
|
||||
.Select(r => r.AsADOrganisationalUnit()).OrderBy(ou => ou.Name).ToList();
|
||||
}
|
||||
#endregion
|
||||
|
||||
private ADSearchResult RetrieveBySamAccountName(string Id, string LdapFilterTemplate, string[] LoadProperties)
|
||||
@@ -343,93 +351,6 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
return (pr.Status == IPStatus.Success);
|
||||
}
|
||||
}
|
||||
|
||||
public string OfflineDomainJoinProvision(string ComputerSamAccountName, string OrganisationalUnit, ref ADMachineAccount MachineAccount, out string DiagnosticInformation)
|
||||
{
|
||||
if (MachineAccount != null && MachineAccount.IsCriticalSystemObject)
|
||||
throw new InvalidOperationException($"This account {MachineAccount.DistinguishedName} is a Critical System Active Directory Object and Disco ICT refuses to modify it");
|
||||
|
||||
if (!IsWritable)
|
||||
throw new InvalidOperationException($"The domain controller [{Name}] is not writable. This action (Offline Domain Join Provision) requires a writable domain controller.");
|
||||
|
||||
StringBuilder diagnosticInfo = new StringBuilder();
|
||||
string DJoinResult = null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ComputerSamAccountName))
|
||||
ComputerSamAccountName = ComputerSamAccountName.TrimEnd('$');
|
||||
if (!string.IsNullOrWhiteSpace(ComputerSamAccountName) && ComputerSamAccountName.Contains('\\'))
|
||||
ComputerSamAccountName = ComputerSamAccountName.Substring(ComputerSamAccountName.IndexOf('\\') + 1);
|
||||
|
||||
// NetBIOS Limit (16 characters; "{ComputerName}$"; 15 characters allowed)
|
||||
if (string.IsNullOrWhiteSpace(ComputerSamAccountName) || ComputerSamAccountName.Length > 15)
|
||||
throw new ArgumentException("Invalid Computer Name; > 0 and <= 15", "ComputerName");
|
||||
|
||||
// Ensure Specified OU Exists
|
||||
if (!string.IsNullOrEmpty(OrganisationalUnit))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var deOU = RetrieveDirectoryEntry(OrganisationalUnit, new string[] { "distinguishedName" }))
|
||||
{
|
||||
if (deOU == null)
|
||||
throw new Exception($"OU's Directory Entry couldn't be found at [{OrganisationalUnit}]");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ArgumentException($"An error occurred while trying to locate the specified OU: {OrganisationalUnit}", "OrganisationalUnit", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (MachineAccount != null)
|
||||
MachineAccount.DeleteAccount(this);
|
||||
|
||||
string tempFileName = System.IO.Path.GetTempFileName();
|
||||
string argumentOU = (!string.IsNullOrWhiteSpace(OrganisationalUnit)) ? $" /MACHINEOU \"{OrganisationalUnit}\"" : string.Empty;
|
||||
string arguments = $"/PROVISION /DOMAIN \"{Domain.Name}\" /DCNAME \"{Name}\" /MACHINE \"{ComputerSamAccountName}\"{argumentOU} /REUSE /SAVEFILE \"{tempFileName}\"";
|
||||
ProcessStartInfo commandStarter = new ProcessStartInfo("DJOIN.EXE", arguments)
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
ErrorDialog = false,
|
||||
LoadUserProfile = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false
|
||||
};
|
||||
diagnosticInfo.AppendFormat($"DJOIN.EXE {arguments}");
|
||||
diagnosticInfo.AppendLine();
|
||||
|
||||
string stdOutput;
|
||||
string stdError;
|
||||
using (Process commandProc = Process.Start(commandStarter))
|
||||
{
|
||||
commandProc.WaitForExit(20000);
|
||||
stdOutput = commandProc.StandardOutput.ReadToEnd();
|
||||
stdError = commandProc.StandardError.ReadToEnd();
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(stdOutput))
|
||||
diagnosticInfo.AppendLine(stdOutput);
|
||||
if (!string.IsNullOrWhiteSpace(stdError))
|
||||
diagnosticInfo.AppendLine(stdError);
|
||||
|
||||
if (System.IO.File.Exists(tempFileName))
|
||||
{
|
||||
DJoinResult = Convert.ToBase64String(System.IO.File.ReadAllBytes(tempFileName));
|
||||
System.IO.File.Delete(tempFileName);
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(DJoinResult))
|
||||
throw new InvalidOperationException(
|
||||
$@"Domain Join Unsuccessful
|
||||
Error: {stdError}
|
||||
Output: {stdOutput}");
|
||||
|
||||
DiagnosticInformation = diagnosticInfo.ToString();
|
||||
|
||||
// Reload Machine Account
|
||||
MachineAccount = RetrieveADMachineAccount($@"{Domain.NetBiosName}\{ComputerSamAccountName}", (MachineAccount == null ? null : MachineAccount.LoadedProperties.Keys.ToArray()));
|
||||
|
||||
return DJoinResult;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override string ToString()
|
||||
|
||||
@@ -204,6 +204,29 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
|
||||
#region Actions
|
||||
|
||||
public void RenameAccount(ADDomainController writableDomainController, string newName)
|
||||
{
|
||||
if (IsCriticalSystemObject)
|
||||
throw new InvalidOperationException($"This account [{DistinguishedName}] is a Critical System Active Directory Object and Disco ICT refuses to modify it");
|
||||
|
||||
if (!writableDomainController.IsWritable)
|
||||
throw new InvalidOperationException($"The domain controller [{Name}] is not writable. This action (Delete Account) requires a writable domain controller.");
|
||||
|
||||
using (ADDirectoryEntry adEntry = writableDomainController.RetrieveDirectoryEntry(DistinguishedName))
|
||||
{
|
||||
var entry = adEntry.Entry;
|
||||
entry.Properties["dNSHostName"][0] = $"{newName}.{Domain.Name}";
|
||||
entry.Properties["sAMAccountName"][0] = $"{newName}$";
|
||||
entry.CommitChanges();
|
||||
entry.Rename($"CN={newName}");
|
||||
entry.CommitChanges();
|
||||
|
||||
// Update Distinguished Name
|
||||
Name = newName;
|
||||
DistinguishedName = entry.Properties["distinguishedName"][0].ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteAccount(ADDomainController WritableDomainController)
|
||||
{
|
||||
if (IsCriticalSystemObject)
|
||||
|
||||
@@ -80,21 +80,23 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
return JsonConvert.DeserializeObject<ADManagedGroupConfiguration>(ConfigurationJson);
|
||||
}
|
||||
public static string ValidConfigurationToJson(string GroupKey, string GroupId, DateTime? FilterBeginDate)
|
||||
=> ValidConfigurationToJson(GroupKey, GroupId, FilterBeginDate, true);
|
||||
public static string ValidConfigurationToJson(string groupKey, string groupId, DateTime? filterBeginDate, bool updateDescription)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(GroupId))
|
||||
GroupId = null;
|
||||
if (string.IsNullOrWhiteSpace(groupId))
|
||||
groupId = null;
|
||||
|
||||
if (GroupId != null)
|
||||
GroupId = ActiveDirectory.Context.ManagedGroups.ValidateGroupId(GroupId, GroupKey);
|
||||
|
||||
if (GroupId == null)
|
||||
if (groupId != null)
|
||||
groupId = ActiveDirectory.Context.ManagedGroups.ValidateGroupId(groupId, groupKey);
|
||||
if (groupId == null)
|
||||
return null;
|
||||
else
|
||||
return JsonConvert.SerializeObject(new ADManagedGroupConfiguration()
|
||||
{
|
||||
GroupId = GroupId,
|
||||
FilterBeginDate = FilterBeginDate
|
||||
}, new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore });
|
||||
GroupId = groupId,
|
||||
FilterBeginDate = filterBeginDate,
|
||||
UpdateDescription = updateDescription,
|
||||
}, new JsonSerializerSettings());
|
||||
}
|
||||
|
||||
public abstract void Dispose();
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
|
||||
private static ActiveDirectoryContext context;
|
||||
private static ActiveDirectoryGroupCache groupCache;
|
||||
private static object contextInitializingLock = new object();
|
||||
private static readonly object contextInitializingLock = new object();
|
||||
|
||||
public static void Initialize(DiscoDataContext Database)
|
||||
{
|
||||
@@ -157,6 +157,7 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
#endregion
|
||||
|
||||
#region Organisational Units
|
||||
[Obsolete("Retrieve as needed using RetrieveADOrganisationUnits(parentDistinguishedName)")]
|
||||
public static IEnumerable<Tuple<ADDomain, List<ADOrganisationalUnit>>> RetrieveADOrganisationalUnitStructure()
|
||||
{
|
||||
return Context.Domains
|
||||
@@ -178,18 +179,6 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Actions
|
||||
|
||||
public static string OfflineDomainJoinProvision(string ComputerSamAccountName, string OrganisationalUnit, ref ADMachineAccount MachineAccount, out string DiagnosticInformation)
|
||||
{
|
||||
var domain = Context.GetDomainFromDistinguishedName(OrganisationalUnit);
|
||||
var writableDomainController = domain.GetAvailableDomainController(RequireWritable: true);
|
||||
|
||||
return writableDomainController.OfflineDomainJoinProvision(ComputerSamAccountName, OrganisationalUnit, ref MachineAccount, out DiagnosticInformation);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
public static string ParseDomainAccountId(string AccountId)
|
||||
|
||||
@@ -13,9 +13,9 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
|
||||
public class ActiveDirectoryManagedGroups : IDisposable
|
||||
{
|
||||
private ConcurrentDictionary<string, ADManagedGroup> managedGroups;
|
||||
private Subject<ADManagedGroupScheduledAction> actionBuffer;
|
||||
private IDisposable actionBufferSubscription;
|
||||
private readonly ConcurrentDictionary<string, ADManagedGroup> managedGroups;
|
||||
private readonly Subject<ADManagedGroupScheduledAction> actionBuffer;
|
||||
private readonly IDisposable actionBufferSubscription;
|
||||
|
||||
internal ActiveDirectoryManagedGroups()
|
||||
{
|
||||
@@ -379,13 +379,16 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
throw new InvalidOperationException($"This group [{adGroup.DistinguishedName}] is a Critical System Active Directory Object and Disco ICT refuses to modify it");
|
||||
|
||||
// Update Description
|
||||
var groupDescription = $"Disco ICT: {actionGroup.Item1.GroupDescription}";
|
||||
if (adGroupEntry.Entry.Properties.Value<string>("description") != groupDescription)
|
||||
if (actionGroup.Item1.Configuration.UpdateDescription)
|
||||
{
|
||||
var adGroupEntryDescription = adGroupEntry.Entry.Properties["description"];
|
||||
if (adGroupEntryDescription.Count > 0)
|
||||
adGroupEntryDescription.Clear();
|
||||
adGroupEntryDescription.Add(groupDescription);
|
||||
var groupDescription = $"Disco ICT: {actionGroup.Item1.GroupDescription}";
|
||||
if (adGroupEntry.Entry.Properties.Value<string>("description") != groupDescription)
|
||||
{
|
||||
var adGroupEntryDescription = adGroupEntry.Entry.Properties["description"];
|
||||
if (adGroupEntryDescription.Count > 0)
|
||||
adGroupEntryDescription.Clear();
|
||||
adGroupEntryDescription.Add(groupDescription);
|
||||
}
|
||||
}
|
||||
|
||||
// Sync Members
|
||||
@@ -430,7 +433,7 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
|
||||
internal class ADManagedGroupScheduledAction
|
||||
{
|
||||
private Func<DiscoDataContext, IEnumerable<string>> memberResolver;
|
||||
private readonly Func<DiscoDataContext, IEnumerable<string>> memberResolver;
|
||||
|
||||
public ADManagedGroup ManagedGroup { get; private set; }
|
||||
public ADManagedGroupScheduledActionType ActionType { get; private set; }
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace Disco.Services.Interop.DNS
|
||||
{
|
||||
public class ADnsRecord : DnsRecord
|
||||
{
|
||||
public IPAddress Address { get; }
|
||||
|
||||
public ADnsRecord(string name, TimeSpan timeToLive, uint address)
|
||||
: base(name, DnsRecordType.A, timeToLive, UIntToIPAddress(address).ToString())
|
||||
{
|
||||
Address = UIntToIPAddress(address);
|
||||
}
|
||||
|
||||
private static IPAddress UIntToIPAddress(uint address)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(address);
|
||||
if (BitConverter.IsLittleEndian)
|
||||
Array.Reverse(bytes);
|
||||
return new IPAddress(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Disco.Services.Interop.DNS
|
||||
{
|
||||
public class CnameDnsRecord : DnsRecord
|
||||
{
|
||||
public CnameDnsRecord(string name, TimeSpan timeToLive, string canonicalName)
|
||||
: base(name, DnsRecordType.Cname, timeToLive, canonicalName)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace Disco.Services.Interop.DNS
|
||||
{
|
||||
public abstract class DnsRecord
|
||||
{
|
||||
public string Name { get; }
|
||||
public DnsRecordType Type { get; }
|
||||
public TimeSpan TimeToLive { get; }
|
||||
public string Content { get; }
|
||||
|
||||
protected DnsRecord(string name, DnsRecordType type, TimeSpan timeToLive, string content)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
TimeToLive = timeToLive;
|
||||
Content = content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Disco.Services.Interop.DNS
|
||||
{
|
||||
public enum DnsRecordType
|
||||
{
|
||||
A = 0x01,
|
||||
Cname = 0x05,
|
||||
Txt = 0x10,
|
||||
Srv = 0x21
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Disco.Services.Interop.DNS
|
||||
{
|
||||
public class DnsService
|
||||
{
|
||||
public DnsService()
|
||||
{
|
||||
}
|
||||
|
||||
public static List<T> Query<T>(string name, bool bypassCache = false) where T : DnsRecord
|
||||
{
|
||||
DnsRecordType recordType;
|
||||
if (typeof(T) == typeof(ADnsRecord))
|
||||
recordType = DnsRecordType.A;
|
||||
else if (typeof(T) == typeof(CnameDnsRecord))
|
||||
recordType = DnsRecordType.Cname;
|
||||
else if (typeof(T) == typeof(TxtDnsRecord))
|
||||
recordType = DnsRecordType.Txt;
|
||||
else if (typeof(T) == typeof(SrvDnsRecord))
|
||||
recordType = DnsRecordType.Srv;
|
||||
else
|
||||
throw new NotSupportedException($"Unsupported DNS record type: {typeof(T).Name}");
|
||||
var records = NativeDns.QueryRecords(recordType, name, bypassCache);
|
||||
return records.ConvertAll(r => (T)r);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Disco.Services.Interop.DNS
|
||||
{
|
||||
internal static class NativeDns
|
||||
{
|
||||
|
||||
[DllImport("dnsapi", EntryPoint = "DnsQuery_W", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
|
||||
private static extern int DnsQuery([MarshalAs(UnmanagedType.VBByRefStr)] ref string pszName, NativeDnsQueryTypes wType, NativeDnsQueryOptions options, int aipServers, ref IntPtr ppQueryResults, int pReserved);
|
||||
|
||||
[DllImport("dnsapi", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern void DnsRecordListFree(IntPtr pRecordList, int FreeType);
|
||||
private const int DNS_ERROR_RCODE_NAME_ERROR = 0x232B;
|
||||
private const int DNS_ERROR_BAD_PACKET = 0x251E;
|
||||
|
||||
public static List<DnsRecord> QueryRecords(DnsRecordType type, string name, bool bypassCache)
|
||||
{
|
||||
NativeDnsQueryTypes queryType;
|
||||
Func<IntPtr, Tuple<DnsRecord, IntPtr>> marshaller;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case DnsRecordType.A:
|
||||
queryType = NativeDnsQueryTypes.DNS_TYPE_A;
|
||||
marshaller = MarshalARecord;
|
||||
break;
|
||||
case DnsRecordType.Cname:
|
||||
queryType = NativeDnsQueryTypes.DNS_TYPE_CNAME;
|
||||
marshaller = MarshalCnameRecord;
|
||||
break;
|
||||
case DnsRecordType.Txt:
|
||||
queryType = NativeDnsQueryTypes.DNS_TYPE_TEXT;
|
||||
marshaller = MarshalTxtRecord;
|
||||
break;
|
||||
case DnsRecordType.Srv:
|
||||
queryType = NativeDnsQueryTypes.DNS_TYPE_SRV;
|
||||
marshaller = MarshalSrvRecord;
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"Unsupported DNS record type: {type}");
|
||||
}
|
||||
|
||||
IntPtr rrPointers = IntPtr.Zero;
|
||||
var records = new List<DnsRecord>();
|
||||
var retry = 5;
|
||||
retry:
|
||||
try
|
||||
{
|
||||
int queryResult = DnsQuery(ref name, queryType, bypassCache ? NativeDnsQueryOptions.DNS_QUERY_BYPASS_CACHE : NativeDnsQueryOptions.DNS_QUERY_STANDARD, 0, ref rrPointers, 0);
|
||||
if (queryResult != 0)
|
||||
{
|
||||
if (queryResult == DNS_ERROR_RCODE_NAME_ERROR)
|
||||
return records;
|
||||
else if (queryResult == DNS_ERROR_BAD_PACKET && retry > 0)
|
||||
{
|
||||
// Sometimes a BAD_PACKET error is returned, retry a few times
|
||||
Thread.Sleep(100);
|
||||
retry--;
|
||||
goto retry;
|
||||
}
|
||||
else
|
||||
throw new Win32Exception(queryResult);
|
||||
}
|
||||
for (var rrPointer = rrPointers; !rrPointer.Equals(IntPtr.Zero);)
|
||||
{
|
||||
var (record, rrPointerNext) = marshaller(rrPointer);
|
||||
records.Add(record);
|
||||
rrPointer = rrPointerNext;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (rrPointers != IntPtr.Zero)
|
||||
DnsRecordListFree(rrPointers, 0);
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
private static Tuple<DnsRecord, IntPtr> MarshalARecord(IntPtr pointer)
|
||||
{
|
||||
var native = Marshal.PtrToStructure<NativeDnsAData>(pointer);
|
||||
var record = new ADnsRecord(native.pName, TimeSpan.FromSeconds(native.dwTtl), native.IpAddress);
|
||||
return Tuple.Create((DnsRecord)record, native.pNext);
|
||||
}
|
||||
|
||||
private static Tuple<DnsRecord, IntPtr> MarshalCnameRecord(IntPtr pointer)
|
||||
{
|
||||
var native = Marshal.PtrToStructure<NativeDnsPtrData>(pointer);
|
||||
var record = new CnameDnsRecord(native.pName, TimeSpan.FromSeconds(native.dwTtl), native.pNameHost);
|
||||
return Tuple.Create((DnsRecord)record, native.pNext);
|
||||
}
|
||||
|
||||
private static Tuple<DnsRecord, IntPtr> MarshalTxtRecord(IntPtr pointer)
|
||||
{
|
||||
var native = Marshal.PtrToStructure<NativeDnsTxtData>(pointer);
|
||||
var record = new TxtDnsRecord(native.pName, TimeSpan.FromSeconds(native.dwTtl), native.pStringArray);
|
||||
return Tuple.Create((DnsRecord)record, native.pNext);
|
||||
}
|
||||
|
||||
private static Tuple<DnsRecord, IntPtr> MarshalSrvRecord(IntPtr pointer)
|
||||
{
|
||||
var native = Marshal.PtrToStructure<NativeDnsSrvData>(pointer);
|
||||
var record = new SrvDnsRecord(native.pName, TimeSpan.FromSeconds(native.dwTtl), native.pNameTarget, native.wPriority, native.wWeight, native.wPort);
|
||||
return Tuple.Create((DnsRecord)record, native.pNext);
|
||||
}
|
||||
|
||||
private enum NativeDnsQueryOptions
|
||||
{
|
||||
DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE = 1,
|
||||
DNS_QUERY_BYPASS_CACHE = 8,
|
||||
DNS_QUERY_DONT_RESET_TTL_VALUES = 0x100000,
|
||||
DNS_QUERY_NO_HOSTS_FILE = 0x40,
|
||||
DNS_QUERY_NO_LOCAL_NAME = 0x20,
|
||||
DNS_QUERY_NO_NETBT = 0x80,
|
||||
DNS_QUERY_NO_RECURSION = 4,
|
||||
DNS_QUERY_NO_WIRE_QUERY = 0x10,
|
||||
DNS_QUERY_RESERVED = -16777216,
|
||||
DNS_QUERY_RETURN_MESSAGE = 0x200,
|
||||
DNS_QUERY_STANDARD = 0,
|
||||
DNS_QUERY_TREAT_AS_FQDN = 0x1000,
|
||||
DNS_QUERY_USE_TCP_ONLY = 2,
|
||||
DNS_QUERY_WIRE_ONLY = 0x100
|
||||
}
|
||||
|
||||
private enum NativeDnsQueryTypes
|
||||
{
|
||||
DNS_TYPE_A = 0x0001,
|
||||
DNS_TYPE_CNAME = 0x0005,
|
||||
DNS_TYPE_TEXT = 0x0010,
|
||||
DNS_TYPE_SRV = 0x0021
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct NativeDnsSrvData
|
||||
{
|
||||
public IntPtr pNext;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string pName;
|
||||
public ushort wType;
|
||||
public ushort wDataLength;
|
||||
public int flags;
|
||||
public int dwTtl;
|
||||
public int dwReserved;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string pNameTarget;
|
||||
public ushort wPriority;
|
||||
public ushort wWeight;
|
||||
public ushort wPort;
|
||||
public ushort Pad;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct NativeDnsTxtData
|
||||
{
|
||||
public IntPtr pNext;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string pName;
|
||||
public ushort wType;
|
||||
public ushort wDataLength;
|
||||
public int flags;
|
||||
public int dwTtl;
|
||||
public int dwReserved;
|
||||
public uint dwStringLength;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string pStringArray;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct NativeDnsPtrData
|
||||
{
|
||||
public IntPtr pNext;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string pName;
|
||||
public ushort wType;
|
||||
public ushort wDataLength;
|
||||
public int flags;
|
||||
public int dwTtl;
|
||||
public int dwReserved;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string pNameHost;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct NativeDnsAData
|
||||
{
|
||||
public IntPtr pNext;
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
public string pName;
|
||||
public ushort wType;
|
||||
public ushort wDataLength;
|
||||
public int flags;
|
||||
public int dwTtl;
|
||||
public int dwReserved;
|
||||
public uint IpAddress;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace Disco.Services.Interop.DNS
|
||||
{
|
||||
public class SrvDnsRecord : DnsRecord
|
||||
{
|
||||
public string Target { get; }
|
||||
public ushort Priority { get; }
|
||||
public ushort Weight { get; }
|
||||
public ushort Port { get; }
|
||||
|
||||
public SrvDnsRecord(string name, TimeSpan timeToLive, string target, ushort priority, ushort weight, ushort port)
|
||||
: base(name, DnsRecordType.Srv, timeToLive, $"{priority} {weight} {port} {target}")
|
||||
{
|
||||
Target = target;
|
||||
Priority = priority;
|
||||
Weight = weight;
|
||||
Port = port;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Disco.Services.Interop.DNS
|
||||
{
|
||||
public class TxtDnsRecord : DnsRecord
|
||||
{
|
||||
public TxtDnsRecord(string name, TimeSpan timeToLive, string text)
|
||||
: base(name, DnsRecordType.Txt, timeToLive, text)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
{
|
||||
public class ActivationService
|
||||
{
|
||||
private static readonly HttpClient httpClient;
|
||||
private static readonly byte[] onlineServicesActivationKey;
|
||||
private readonly DiscoDataContext database;
|
||||
|
||||
@@ -29,6 +30,10 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
resourceStream.Read(key, 0, key.Length);
|
||||
onlineServicesActivationKey = key;
|
||||
}
|
||||
httpClient = new HttpClient(new OnlineServicesAuthenticatedHandler())
|
||||
{
|
||||
BaseAddress = DiscoServiceHelpers.ActivationServiceUrl
|
||||
};
|
||||
}
|
||||
|
||||
public ActivationService(DiscoDataContext database)
|
||||
@@ -42,6 +47,24 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
public Uri GetCallbackUrl()
|
||||
=> new Uri(DiscoServiceHelpers.ActivationServiceUrl, "/api/callback");
|
||||
|
||||
public string CalculateCallbackProof(Guid correlationId, string userId, long timestamp)
|
||||
{
|
||||
var deploymentId = Guid.Parse(database.DiscoConfiguration.DeploymentId);
|
||||
var secret = Guid.Parse(database.DiscoConfiguration.DeploymentSecret);
|
||||
using (var hmac = new HMACSHA256(secret.ToByteArray()))
|
||||
{
|
||||
var data = new MemoryStream();
|
||||
data.Write(deploymentId.ToByteArray(), 0, 16);
|
||||
data.Write(correlationId.ToByteArray(), 0, 16);
|
||||
var userIdBytes = Encoding.UTF8.GetBytes(userId);
|
||||
data.Write(BitConverter.GetBytes(userIdBytes.Length), 0, 4);
|
||||
data.Write(userIdBytes, 0, userIdBytes.Length);
|
||||
data.Write(BitConverter.GetBytes(timestamp), 0, 8);
|
||||
var hash = hmac.ComputeHash(data.ToArray());
|
||||
return Convert.ToBase64String(hash).TrimEnd('=').Replace('+', '-').Replace('/', '_');
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begin the activation process
|
||||
/// </summary>
|
||||
@@ -229,6 +252,38 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<T> Post<T>(string url, object request)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
using (var jsonWriter = new StreamWriter(stream, new UTF8Encoding(false), 1024, true))
|
||||
{
|
||||
var serializer = new JsonSerializer();
|
||||
serializer.Serialize(jsonWriter, request);
|
||||
}
|
||||
stream.Position = 0;
|
||||
using (var content = new StreamContent(stream))
|
||||
{
|
||||
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
|
||||
|
||||
using (var response = await httpClient.PostAsync(url, content))
|
||||
{
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
using (var responseContent = await response.Content.ReadAsStreamAsync())
|
||||
{
|
||||
using (var reader = new StreamReader(responseContent, Encoding.UTF8))
|
||||
{
|
||||
using (var jsonReader = new JsonTextReader(reader))
|
||||
{
|
||||
var serializer = new JsonSerializer();
|
||||
return serializer.Deserialize<T>(jsonReader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ValidateSignature(ChallengeResponse response)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
@@ -237,7 +292,7 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
stream.Write(response.Challenge, 0, response.Challenge.Length);
|
||||
stream.Write(response.ChallengeIv, 0, response.ChallengeIv.Length);
|
||||
|
||||
return ValidateSignature(stream.ToArray(), response.Signature);
|
||||
return ValidateOnlineServicesSignature(stream.ToArray(), response.Signature);
|
||||
}
|
||||
|
||||
private static bool ValidateSignature(Guid activationId, byte[] challenge, byte[] challengeIv, byte[] signature)
|
||||
@@ -246,10 +301,10 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
stream.Write(activationId.ToByteArray(), 0, 16);
|
||||
stream.Write(challenge, 0, challenge.Length);
|
||||
stream.Write(challengeIv, 0, challengeIv.Length);
|
||||
return ValidateSignature(stream.ToArray(), signature);
|
||||
return ValidateOnlineServicesSignature(stream.ToArray(), signature);
|
||||
}
|
||||
|
||||
private static bool ValidateSignature(byte[] bytes, byte[] signature)
|
||||
public static bool ValidateOnlineServicesSignature(byte[] bytes, byte[] signature)
|
||||
{
|
||||
byte[] hash;
|
||||
using (var hasher = SHA256.Create())
|
||||
@@ -295,6 +350,11 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] SignSHA256Hash(byte[] hash)
|
||||
{
|
||||
return SignHash(OnlineServicesAuthentication.Key, hash);
|
||||
}
|
||||
|
||||
private static byte[] Encrypt(byte[] privateKey, byte[] iv, long timeStamp, byte[] data)
|
||||
{
|
||||
var key = DeriveEncryptionKey(privateKey, iv, timeStamp);
|
||||
@@ -318,6 +378,17 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] Encrypt(byte[] data, out byte[] iv, out long timeStamp)
|
||||
{
|
||||
iv = new byte[16];
|
||||
using (var rng = RandomNumberGenerator.Create())
|
||||
rng.GetBytes(iv);
|
||||
|
||||
timeStamp = DateTime.UtcNow.Ticks;
|
||||
|
||||
return Encrypt(OnlineServicesAuthentication.Key, iv, timeStamp, data);
|
||||
}
|
||||
|
||||
private static byte[] Decrypt(byte[] privateKey, byte[] iv, long timeStamp, byte[] data)
|
||||
{
|
||||
var key = DeriveEncryptionKey(privateKey, iv, timeStamp);
|
||||
@@ -342,6 +413,63 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] Decrypt(byte[] data, byte[] iv, long timeStamp)
|
||||
{
|
||||
return Decrypt(OnlineServicesAuthentication.Key, iv, timeStamp, data);
|
||||
}
|
||||
|
||||
public static byte[] OneWayDecrypt(byte[] data)
|
||||
{
|
||||
var span = data.AsSpan();
|
||||
|
||||
if (span.Length < 13)
|
||||
throw new ArgumentException("Data is too short", nameof(data));
|
||||
Span<byte> magicBytes = new byte[] { 0xF0, 0x9F, 0x94, 0x8F, 0x44, 0x69, 0x73, 0x63, 0x6F, 0x49, 0x43, 0x54 }.AsSpan();
|
||||
if (!MemoryExtensions.SequenceEqual(magicBytes, span.Slice(0, 12)))
|
||||
throw new InvalidOperationException("Invalid format signature");
|
||||
|
||||
span = span.Slice(12);
|
||||
|
||||
byte[] readChunk(ref Span<byte> source)
|
||||
{
|
||||
var length = source[0];
|
||||
if (source.Length < (length + 1))
|
||||
throw new ArgumentException("Data is too short", nameof(data));
|
||||
var buffer = new byte[length];
|
||||
source.Slice(1, length).CopyTo(buffer);
|
||||
source = source.Slice(length + 1);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
var publicKey = readChunk(ref span);
|
||||
var iv = readChunk(ref span);
|
||||
var signature = readChunk(ref span);
|
||||
|
||||
var key = DeriveOneWayDecryptingKey(OnlineServicesAuthentication.Key, publicKey, iv);
|
||||
|
||||
var outputStream = new MemoryStream();
|
||||
using (var aes = Aes.Create())
|
||||
{
|
||||
aes.Key = key;
|
||||
aes.IV = iv;
|
||||
aes.Mode = CipherMode.CBC;
|
||||
aes.Padding = PaddingMode.PKCS7;
|
||||
|
||||
using (var decryptor = aes.CreateDecryptor())
|
||||
{
|
||||
var ms = new MemoryStream(span.ToArray());
|
||||
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
|
||||
{
|
||||
cs.CopyTo(outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
var output = outputStream.ToArray();
|
||||
if (!ValidateOnlineServicesSignature(output, signature))
|
||||
throw new InvalidOperationException("Invalid encrypted data signature");
|
||||
return output;
|
||||
}
|
||||
|
||||
private static byte[] DeriveEncryptionKey(byte[] privateKey, byte[] iv, long timeStamp)
|
||||
{
|
||||
using (var serverKey = CngKey.Import(onlineServicesActivationKey, CngKeyBlobFormat.EccPublicBlob))
|
||||
@@ -359,6 +487,23 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] DeriveOneWayDecryptingKey(byte[] privateKey, byte[] publicKey, byte[] iv)
|
||||
{
|
||||
using (var ephemeralKey = CngKey.Import(publicKey, CngKeyBlobFormat.EccPublicBlob))
|
||||
{
|
||||
using (var clientKey = CngKey.Import(privateKey, CngKeyBlobFormat.EccPrivateBlob))
|
||||
{
|
||||
using (var ephemeralEcdh = new ECDiffieHellmanCng(ephemeralKey))
|
||||
{
|
||||
using (var ecdh = new ECDiffieHellmanCng(clientKey))
|
||||
{
|
||||
return ecdh.DeriveKeyFromHash(ephemeralEcdh.PublicKey, HashAlgorithmName.SHA256, iv, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static (byte[] privateKey, byte[] publicKey) GenerateActivationKey()
|
||||
{
|
||||
using (var key = CngKey.Create(CngAlgorithm.ECDiffieHellmanP521, null, new CngKeyCreationParameters
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Disco.Services.Interop.DiscoServices
|
||||
{
|
||||
[Flags]
|
||||
public enum AuthenticationSessionScope
|
||||
{
|
||||
Ping = 1,
|
||||
Host = 2,
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
@@ -14,7 +16,7 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
{
|
||||
public static class OnlineServicesAuthentication
|
||||
{
|
||||
private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
|
||||
private static readonly SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
|
||||
private static readonly Guid deploymentId;
|
||||
private static Guid? activationId;
|
||||
private static byte[] key;
|
||||
@@ -31,11 +33,12 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
}
|
||||
|
||||
public static bool IsActivated => activationId.HasValue;
|
||||
internal static byte[] Key => key.ToArray() ?? throw new InvalidOperationException("Not activated");
|
||||
|
||||
public static string GetToken()
|
||||
=> GetTokenAsync().Result;
|
||||
|
||||
public async static Task<string> GetTokenAsync()
|
||||
public static async Task<string> GetTokenAsync()
|
||||
{
|
||||
var localExpires = tokenExpires;
|
||||
var localToken = token;
|
||||
@@ -56,40 +59,40 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
if (!IsActivated)
|
||||
throw new InvalidOperationException("Not activated");
|
||||
|
||||
using (var httpClient = new HttpClient())
|
||||
var timeStamp = DateTime.UtcNow.ToUnixEpoc();
|
||||
var iv = new byte[32];
|
||||
using (var rng = RandomNumberGenerator.Create())
|
||||
rng.GetBytes(iv);
|
||||
|
||||
var dataStream = new MemoryStream(16 + 16 + 8 + iv.Length);
|
||||
dataStream.Write(deploymentId.ToByteArray(), 0, 16);
|
||||
dataStream.Write(activationId.Value.ToByteArray(), 0, 16);
|
||||
dataStream.Write(BitConverter.GetBytes(timeStamp), 0, 8);
|
||||
dataStream.Write(iv, 0, iv.Length);
|
||||
byte[] hash;
|
||||
using (var hasher = SHA256.Create())
|
||||
hash = hasher.ComputeHash(dataStream.ToArray());
|
||||
|
||||
var signature = ActivationService.SignHash(key, hash);
|
||||
|
||||
var body = new AuthenticationRequest()
|
||||
{
|
||||
httpClient.BaseAddress = DiscoServiceHelpers.ActivationServiceUrl;
|
||||
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
DeploymentId = deploymentId,
|
||||
ActivationId = activationId.Value,
|
||||
TimeStamp = timeStamp,
|
||||
IV = iv,
|
||||
Signature = signature,
|
||||
};
|
||||
var requestJson = JsonConvert.SerializeObject(body);
|
||||
|
||||
var timeStamp = DateTime.UtcNow.ToUnixEpoc();
|
||||
var iv = new byte[32];
|
||||
using (var rng = RandomNumberGenerator.Create())
|
||||
rng.GetBytes(iv);
|
||||
using (var request = new ByteArrayContent(Encoding.UTF8.GetBytes(requestJson)))
|
||||
{
|
||||
request.Headers.ContentType = new MediaTypeHeaderValue("application/json");
|
||||
|
||||
var dataStream = new MemoryStream(16 + 16 + 8 + iv.Length);
|
||||
dataStream.Write(deploymentId.ToByteArray(), 0, 16);
|
||||
dataStream.Write(activationId.Value.ToByteArray(), 0, 16);
|
||||
dataStream.Write(BitConverter.GetBytes(timeStamp), 0, 8);
|
||||
dataStream.Write(iv, 0, iv.Length);
|
||||
byte[] hash;
|
||||
using (var hasher = SHA256.Create())
|
||||
hash = hasher.ComputeHash(dataStream.ToArray());
|
||||
|
||||
var signature = ActivationService.SignHash(key, hash);
|
||||
|
||||
var body = new AuthenticationRequest()
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
DeploymentId = deploymentId,
|
||||
ActivationId = activationId.Value,
|
||||
TimeStamp = timeStamp,
|
||||
IV = iv,
|
||||
Signature = signature,
|
||||
};
|
||||
var requestJson = JsonConvert.SerializeObject(body);
|
||||
|
||||
using (var request = new ByteArrayContent(Encoding.UTF8.GetBytes(requestJson)))
|
||||
{
|
||||
request.Headers.ContentType = new MediaTypeHeaderValue("application/json");
|
||||
httpClient.BaseAddress = DiscoServiceHelpers.ActivationServiceUrl;
|
||||
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
|
||||
var response = await httpClient.PostAsync("/api/authenticate", request);
|
||||
|
||||
@@ -120,6 +123,32 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<Uri> CreateSession(AuthenticationSessionScope scope, User user, Uri returnUrl)
|
||||
{
|
||||
if (!IsActivated)
|
||||
throw new InvalidOperationException("Not activated");
|
||||
|
||||
var request = new AuthenticationSessionRequest()
|
||||
{
|
||||
DeploymentId = deploymentId,
|
||||
ActivationId = activationId.Value,
|
||||
TimeStamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
Scope = scope,
|
||||
UserId = user.UserId,
|
||||
UserName = user.DisplayName ?? string.Empty,
|
||||
UserEmail = user.EmailAddress,
|
||||
UserPhone = user.PhoneNumber,
|
||||
ReturnUrl = returnUrl.ToString(),
|
||||
};
|
||||
|
||||
var response = await ActivationService.Post<AuthenticationSessionResponse>($"/api/authenticate/session", request);
|
||||
|
||||
if (response.Success)
|
||||
return new Uri(response.Endpoint);
|
||||
else
|
||||
throw new InvalidOperationException($"Failed to create authentication session: {response.ErrorMessage}");
|
||||
}
|
||||
|
||||
internal static void UpdateActivation(DiscoDataContext database)
|
||||
{
|
||||
semaphore.Wait();
|
||||
@@ -165,5 +194,25 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
public int? ExpiresInSeconds { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
|
||||
private class AuthenticationSessionRequest
|
||||
{
|
||||
public Guid DeploymentId { get; set; }
|
||||
public Guid ActivationId { get; set; }
|
||||
public long TimeStamp { get; set; }
|
||||
public AuthenticationSessionScope Scope { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string UserEmail { get; set; }
|
||||
public string UserPhone { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
}
|
||||
|
||||
private class AuthenticationSessionResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Endpoint { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,10 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
|
||||
connection.Closed += ex =>
|
||||
{
|
||||
SystemLog.LogException("Online Services: Connection Closed", ex);
|
||||
if (ex != null)
|
||||
SystemLog.LogException("Online Services: Connection Closed", ex);
|
||||
else
|
||||
SystemLog.LogInformation("Online Services: Connection Closed");
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
connection.Reconnected += connectionId =>
|
||||
@@ -53,7 +56,11 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
};
|
||||
connection.Reconnecting += ex =>
|
||||
{
|
||||
SystemLog.LogInformation("Online Services: Connection Reconnecting");
|
||||
if (ex != null)
|
||||
SystemLog.LogException("Online Services: Connection Reconnecting", ex);
|
||||
else
|
||||
SystemLog.LogInformation("Online Services: Connection Reconnecting");
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Interop.DiscoServices;
|
||||
using Disco.Services.Devices.Enrolment;
|
||||
using Disco.Services.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
@@ -221,6 +222,10 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
RepairerLogged = j.JobType == JobType.JobTypeIds.HWar ? j.WarrantyRepairerLoggedDate : j.RepairerLoggedDate,
|
||||
RepairerCompleted = j.JobType == JobType.JobTypeIds.HWar ? j.WarrantyRepairerCompletedDate : j.RepairerCompletedDate
|
||||
}).ToList();
|
||||
|
||||
m.Stat_EnrollmentDiscovery = WindowsDeviceEnrolment.GetDiscoveryMethodStatistics()
|
||||
.Where(s => s.Value != 0)
|
||||
.Select(s => new StatisticInt() { Key = s.Key.ToString(), Value = s.Value }).ToList();
|
||||
}
|
||||
|
||||
m.InstalledPlugins = Plugins.Plugins.GetPlugins().Select(manifest => new StatisticString() { Key = manifest.Id, Value = manifest.VersionFormatted }).ToList();
|
||||
|
||||
@@ -1,13 +1,51 @@
|
||||
using Disco.Services.Interop.DiscoServices;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Disco.Services.Interop.VicEduDept
|
||||
{
|
||||
public class VicSmart
|
||||
{
|
||||
public static bool IsVicSmartDeployment()
|
||||
{
|
||||
var nics = NetworkInterface.GetAllNetworkInterfaces()
|
||||
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
|
||||
.ToList();
|
||||
|
||||
bool found10Net = false;
|
||||
foreach (var nic in nics)
|
||||
{
|
||||
if (nic.Supports(NetworkInterfaceComponent.IPv4))
|
||||
{
|
||||
var ipProps = nic.GetIPProperties();
|
||||
var ipv4Props = ipProps.GetIPv4Properties();
|
||||
if (ipv4Props.IsAutomaticPrivateAddressingActive)
|
||||
continue;
|
||||
|
||||
found10Net = ipProps.UnicastAddresses
|
||||
.Where(ua =>
|
||||
ua.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork &&
|
||||
ua.Address.GetAddressBytes()[0] == 10)
|
||||
.Any();
|
||||
if (found10Net)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found10Net)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var entry = Dns.GetHostEntry("broadband.doe.wan");
|
||||
return entry.AddressList.Length > 0;
|
||||
}
|
||||
catch (Exception)
|
||||
{ return false; } // Fail on error
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries DoE VicSmart Service to detect the current site.
|
||||
|
||||
@@ -16,20 +16,22 @@ namespace Disco.Services.Jobs.Noticeboards
|
||||
public string DeviceSerialNumber { get; set; }
|
||||
public string DeviceComputerNameFriendly
|
||||
{
|
||||
get
|
||||
{
|
||||
return DeviceComputerName == null ? null : ActiveDirectory.FriendlyAccountId(DeviceComputerName);
|
||||
}
|
||||
get => DeviceComputerName == null ? null : ActiveDirectory.FriendlyAccountId(DeviceComputerName);
|
||||
set { } // for XML Serialization
|
||||
}
|
||||
public string DeviceComputerName { get; set; }
|
||||
public string DeviceName
|
||||
{
|
||||
get => DeviceComputerNameFriendly ?? DeviceSerialNumber;
|
||||
set { }
|
||||
}
|
||||
|
||||
public string DeviceLocation { get; set; }
|
||||
public string DeviceDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(DeviceComputerNameFriendly);
|
||||
StringBuilder sb = new StringBuilder(DeviceName);
|
||||
|
||||
if (UserId != null)
|
||||
sb.Append(" - ").Append(UserDisplayName).Append(" (").Append(UserIdFriendly).Append(")");
|
||||
@@ -60,6 +62,7 @@ namespace Disco.Services.Jobs.Noticeboards
|
||||
}
|
||||
set { } // for XML Serialization
|
||||
}
|
||||
public IEnumerable<int> JobQueueIds { get; set; }
|
||||
|
||||
public string UserId { get; set; }
|
||||
public string UserIdFriendly
|
||||
@@ -130,6 +133,7 @@ namespace Disco.Services.Jobs.Noticeboards
|
||||
DeviceLocation = j.Device.Location,
|
||||
DeviceProfileId = j.Device.DeviceProfileId,
|
||||
DeviceAddressId = j.Device.DeviceProfile.DefaultOrganisationAddress,
|
||||
JobQueueIds = j.JobQueues.Where(q => q.RemovedDate == null).Select(q => q.JobQueueId),
|
||||
UserId = j.Device.AssignedUserId,
|
||||
UserDisplayName = j.Device.AssignedUser.DisplayName,
|
||||
WaitingForUserAction = j.WaitingForUserAction.HasValue || ((j.JobMetaNonWarranty.AccountingChargeRequiredDate.HasValue || j.JobMetaNonWarranty.AccountingChargeAddedDate.HasValue) && !j.JobMetaNonWarranty.AccountingChargePaidDate.HasValue),
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Disco.Services.Jobs.Noticeboards
|
||||
{
|
||||
public const string Name = "HeldDevices";
|
||||
|
||||
private readonly static List<string> MonitorJobProperties = new List<string>() {
|
||||
private static readonly List<string> MonitorJobProperties = new List<string>() {
|
||||
"DeviceSerialNumber",
|
||||
"UserId",
|
||||
"ExpectedClosedDate",
|
||||
@@ -25,25 +25,25 @@ namespace Disco.Services.Jobs.Noticeboards
|
||||
"DeviceReadyForReturn",
|
||||
"DeviceReturnedDate"
|
||||
};
|
||||
private readonly static List<string> MonitorJobMetaNonWarrantyProperties = new List<string>(){
|
||||
private static readonly List<string> MonitorJobMetaNonWarrantyProperties = new List<string>(){
|
||||
"AccountingChargeRequiredDate",
|
||||
"AccountingChargeAddedDate",
|
||||
"AccountingChargePaidDate"
|
||||
};
|
||||
private readonly static List<string> MonitorDeviceProperties = new List<string>(){
|
||||
private static readonly List<string> MonitorDeviceProperties = new List<string>(){
|
||||
"Location",
|
||||
"DeviceProfileId",
|
||||
"DeviceDomainId",
|
||||
"AssignedUserId",
|
||||
};
|
||||
private readonly static List<string> MonitorDeviceProfileProperties = new List<string>(){
|
||||
private static readonly List<string> MonitorDeviceProfileProperties = new List<string>(){
|
||||
"DefaultOrganisationAddress"
|
||||
};
|
||||
private readonly static List<string> MonitorUserProperties = new List<string>(){
|
||||
private static readonly List<string> MonitorUserProperties = new List<string>(){
|
||||
"DisplayName"
|
||||
};
|
||||
|
||||
private static Subject<Tuple<List<string>, List<string>>> BufferedUpdateStream;
|
||||
private static readonly Subject<Tuple<List<string>, List<string>>> BufferedUpdateStream;
|
||||
|
||||
static HeldDevices()
|
||||
{
|
||||
@@ -74,7 +74,9 @@ namespace Disco.Services.Jobs.Noticeboards
|
||||
) ||
|
||||
(e.EntityType == typeof(User) &&
|
||||
(e.EventType == RepositoryMonitorEventType.Modified && e.ModifiedProperties.Any(p => MonitorUserProperties.Contains(p)))
|
||||
)
|
||||
) ||
|
||||
(e.EntityType == typeof(JobQueueJob) &&
|
||||
(e.EventType == RepositoryMonitorEventType.Added || e.EventType == RepositoryMonitorEventType.Modified))
|
||||
)
|
||||
.Subscribe(RepositoryEvent);
|
||||
}
|
||||
@@ -147,6 +149,15 @@ namespace Disco.Services.Jobs.Noticeboards
|
||||
.Select(j => j.DeviceSerialNumber)
|
||||
);
|
||||
}
|
||||
else if (i.EntityType == typeof(JobQueueJob))
|
||||
{
|
||||
var jqj = (JobQueueJob)i.Entity;
|
||||
var j = i.Database.Jobs.Find(jqj.JobId);
|
||||
if (j != null && j.DeviceSerialNumber != null)
|
||||
{
|
||||
deviceSerialNumbers.Add(j.DeviceSerialNumber);
|
||||
}
|
||||
}
|
||||
|
||||
if (deviceSerialNumbers.Count > 0 || userIds.Count > 0)
|
||||
{
|
||||
|
||||
@@ -53,6 +53,14 @@ namespace Disco.Services.Plugins.Features.DetailsProvider
|
||||
{
|
||||
using (var originalImage = Image.FromStream(originalStream))
|
||||
{
|
||||
if (originalImage.PropertyIdList.Contains(0x112))
|
||||
{
|
||||
var orientation = BitConverter.ToUInt16(originalImage.GetPropertyItem(0x112).Value, 0);
|
||||
if (orientation > 1 && orientation <= 8)
|
||||
{
|
||||
originalImage.RotateFlip(GetRotateFlipTypeByOrientation(orientation));
|
||||
}
|
||||
}
|
||||
using (var resizedImage = originalImage.ResizeImage(192, Brushes.White))
|
||||
{
|
||||
using (var savedResizedImage = (MemoryStream)resizedImage.SaveJpg(85))
|
||||
@@ -99,5 +107,30 @@ namespace Disco.Services.Plugins.Features.DetailsProvider
|
||||
.ToDictionary(d => d.Key, d => d.Value, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
private RotateFlipType GetRotateFlipTypeByOrientation(ushort orientation)
|
||||
{
|
||||
switch (orientation)
|
||||
{
|
||||
case 1:
|
||||
return RotateFlipType.RotateNoneFlipNone;
|
||||
case 2:
|
||||
return RotateFlipType.RotateNoneFlipX;
|
||||
case 3:
|
||||
return RotateFlipType.Rotate180FlipNone;
|
||||
case 4:
|
||||
return RotateFlipType.RotateNoneFlipY;
|
||||
case 5:
|
||||
return RotateFlipType.Rotate270FlipX;
|
||||
case 6:
|
||||
return RotateFlipType.Rotate90FlipNone;
|
||||
case 7:
|
||||
return RotateFlipType.Rotate90FlipX;
|
||||
case 8:
|
||||
return RotateFlipType.Rotate270FlipNone;
|
||||
default:
|
||||
return RotateFlipType.RotateNoneFlipNone;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<package id="LumenWorks.Framework.IO" version="3.8.0" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.SignalR.Core" version="2.1.2" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.SignalR.Core" version="2.4.3" targetFramework="net462" />
|
||||
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNetCore.Connections.Abstractions" version="9.0.0" targetFramework="net462" />
|
||||
<package id="Microsoft.AspNetCore.Http.Connections.Client" version="9.0.0" targetFramework="net462" />
|
||||
@@ -63,6 +63,6 @@
|
||||
<package id="System.Text.Json" version="9.0.0" targetFramework="net462" />
|
||||
<package id="System.Threading.Channels" version="9.0.0" targetFramework="net462" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net462" />
|
||||
<package id="System.ValueTuple" version="4.5.0" targetFramework="net462" />
|
||||
<package id="System.ValueTuple" version="4.6.2" targetFramework="net462" />
|
||||
<package id="WebActivatorEx" version="2.0.5" targetFramework="net45" />
|
||||
</packages>
|
||||
@@ -56,6 +56,10 @@
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
|
||||
@@ -12,10 +12,22 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
[DiscoAuthorize(Claims.DiscoAdminAccount)]
|
||||
public partial class ActivationController : AuthorizedDatabaseController
|
||||
{
|
||||
[HttpPost]
|
||||
public virtual ActionResult TestCallback(CallbackModel model)
|
||||
[HttpGet]
|
||||
public virtual async Task<ActionResult> Begin(CallbackModel model)
|
||||
{
|
||||
return this.PrecompiledPartialView<Views.Activation._ActivateCallback>(model);
|
||||
// validate timestamp
|
||||
var thresholdStart = DateTimeOffset.UtcNow.AddSeconds(-20).ToUnixTimeMilliseconds();
|
||||
var thresholdEnd = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
if (model.Timestamp < thresholdStart || model.Timestamp > thresholdEnd)
|
||||
return new HttpStatusCodeResult(400, "Invalid timestamp");
|
||||
|
||||
// validate proof
|
||||
var service = new ActivationService(Database);
|
||||
var expectedProof = service.CalculateCallbackProof(model.CorrelationId, model.UserId, model.Timestamp);
|
||||
if (model.Proof != expectedProof)
|
||||
return new HttpStatusCodeResult(400, "Invalid proof");
|
||||
|
||||
return await Begin();
|
||||
}
|
||||
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
@@ -33,7 +45,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
RedirectUrl = challengeModel.RedirectUrl
|
||||
};
|
||||
|
||||
return View(model);
|
||||
return View(MVC.API.Activation.Views.Begin, model);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
||||
@@ -18,22 +18,20 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
{
|
||||
public partial class DeviceBatchController : AuthorizedDatabaseController
|
||||
{
|
||||
const string pName = "name";
|
||||
const string pPurchaseDate = "purchasedate";
|
||||
const string pSupplier = "supplier";
|
||||
const string pPurchaseDetails = "purchasedetails";
|
||||
const string pUnitCost = "unitcost";
|
||||
const string pUnitQuantity = "unitquantity";
|
||||
const string pDefaultDeviceModelId = "defaultdevicemodelid";
|
||||
const string pWarrantyValidUntil = "warrantyvaliduntil";
|
||||
const string pWarrantyDetails = "warrantydetails";
|
||||
const string pInsuredDate = "insureddate";
|
||||
const string pInsuranceSupplier = "insurancesupplier";
|
||||
const string pInsuredUntil = "insureduntil";
|
||||
const string pInsuranceDetails = "insurancedetails";
|
||||
const string pComments = "comments";
|
||||
const string pDevicesLinkedGroup = "deviceslinkedgroup";
|
||||
const string pAssignedUsersLinkedGroup = "assigneduserslinkedgroup";
|
||||
private const string pName = "name";
|
||||
private const string pPurchaseDate = "purchasedate";
|
||||
private const string pSupplier = "supplier";
|
||||
private const string pPurchaseDetails = "purchasedetails";
|
||||
private const string pUnitCost = "unitcost";
|
||||
private const string pUnitQuantity = "unitquantity";
|
||||
private const string pDefaultDeviceModelId = "defaultdevicemodelid";
|
||||
private const string pWarrantyValidUntil = "warrantyvaliduntil";
|
||||
private const string pWarrantyDetails = "warrantydetails";
|
||||
private const string pInsuredDate = "insureddate";
|
||||
private const string pInsuranceSupplier = "insurancesupplier";
|
||||
private const string pInsuredUntil = "insureduntil";
|
||||
private const string pInsuranceDetails = "insurancedetails";
|
||||
private const string pComments = "comments";
|
||||
|
||||
[DiscoAuthorize(Claims.Config.DeviceBatch.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
@@ -94,12 +92,6 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
case pComments:
|
||||
UpdateComments(deviceBatch, value);
|
||||
break;
|
||||
case pDevicesLinkedGroup:
|
||||
UpdateDevicesLinkedGroup(deviceBatch, value);
|
||||
break;
|
||||
case pAssignedUsersLinkedGroup:
|
||||
UpdateAssignedUsersLinkedGroup(deviceBatch, value);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Invalid Update Key");
|
||||
}
|
||||
@@ -224,18 +216,17 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
|
||||
[DiscoAuthorize(Claims.Config.DeviceBatch.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult UpdateDevicesLinkedGroup(int id, string GroupId = null, bool redirect = false)
|
||||
public virtual ActionResult UpdateDevicesLinkedGroup(int id, string groupId = null, bool? updateDescription = null, bool redirect = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id < 0)
|
||||
throw new ArgumentOutOfRangeException("id");
|
||||
throw new ArgumentOutOfRangeException(nameof(id));
|
||||
|
||||
var deviceBatch = Database.DeviceBatches.Find(id);
|
||||
if (deviceBatch == null)
|
||||
throw new ArgumentException("Invalid Device Batch Id", "id");
|
||||
var deviceBatch = Database.DeviceBatches.Find(id)
|
||||
?? throw new ArgumentException("Invalid Device Batch Id", nameof(id));
|
||||
|
||||
var syncTaskStatus = UpdateDevicesLinkedGroup(deviceBatch, GroupId);
|
||||
var syncTaskStatus = UpdateDevicesLinkedGroup(deviceBatch, groupId, updateDescription ?? true);
|
||||
if (redirect)
|
||||
if (syncTaskStatus == null)
|
||||
return RedirectToAction(MVC.Config.DeviceBatch.Index(deviceBatch.Id));
|
||||
@@ -257,18 +248,17 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
[DiscoAuthorize(Claims.Config.DeviceBatch.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult UpdateAssignedUsersLinkedGroup(int id, string GroupId = null, bool redirect = false)
|
||||
public virtual ActionResult UpdateAssignedUsersLinkedGroup(int id, string groupId = null, bool? updateDescription = null, bool redirect = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id < 0)
|
||||
throw new ArgumentOutOfRangeException("id");
|
||||
throw new ArgumentOutOfRangeException(nameof(id));
|
||||
|
||||
var deviceBatch = Database.DeviceBatches.Find(id);
|
||||
if (deviceBatch == null)
|
||||
throw new ArgumentException("Invalid Device Batch Id", "id");
|
||||
var deviceBatch = Database.DeviceBatches.Find(id)
|
||||
?? throw new ArgumentException("Invalid Device Batch Id", nameof(id));
|
||||
|
||||
var syncTaskStatus = UpdateAssignedUsersLinkedGroup(deviceBatch, GroupId);
|
||||
var syncTaskStatus = UpdateAssignedUsersLinkedGroup(deviceBatch, groupId, updateDescription ?? true);
|
||||
if (redirect)
|
||||
if (syncTaskStatus == null)
|
||||
return RedirectToAction(MVC.Config.DeviceBatch.Index(deviceBatch.Id));
|
||||
@@ -486,9 +476,9 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
Database.SaveChanges();
|
||||
}
|
||||
|
||||
private ScheduledTaskStatus UpdateDevicesLinkedGroup(DeviceBatch DeviceBatch, string devicesLinkedGroup)
|
||||
private ScheduledTaskStatus UpdateDevicesLinkedGroup(DeviceBatch DeviceBatch, string devicesLinkedGroup, bool updateDescription)
|
||||
{
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DeviceBatchDevicesManagedGroup.GetKey(DeviceBatch), devicesLinkedGroup, null);
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DeviceBatchDevicesManagedGroup.GetKey(DeviceBatch), devicesLinkedGroup, null, updateDescription);
|
||||
|
||||
if (DeviceBatch.DevicesLinkedGroup != configJson)
|
||||
{
|
||||
@@ -503,9 +493,9 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
return null;
|
||||
}
|
||||
|
||||
private ScheduledTaskStatus UpdateAssignedUsersLinkedGroup(DeviceBatch DeviceBatch, string assignedUsersLinkedGroup)
|
||||
private ScheduledTaskStatus UpdateAssignedUsersLinkedGroup(DeviceBatch DeviceBatch, string assignedUsersLinkedGroup, bool updateDescription)
|
||||
{
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DeviceBatchAssignedUsersManagedGroup.GetKey(DeviceBatch), assignedUsersLinkedGroup, null);
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DeviceBatchAssignedUsersManagedGroup.GetKey(DeviceBatch), assignedUsersLinkedGroup, null, updateDescription);
|
||||
|
||||
if (DeviceBatch.AssignedUsersLinkedGroup != configJson)
|
||||
{
|
||||
@@ -582,6 +572,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
|
||||
var batchesInformation = Database.DeviceBatches.Select(db => new
|
||||
{
|
||||
Id = db.Id,
|
||||
Name = db.Name,
|
||||
Comments = db.Comments,
|
||||
PurchaseDate = db.PurchaseDate,
|
||||
@@ -614,7 +605,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
description = bi.Comments ?? string.Empty,
|
||||
color = ColorTranslator.ToHtml(color),
|
||||
image = Url.Action(MVC.API.DeviceModel.Image(bi.DefaultModelId)),
|
||||
link = Url.Action(MVC.Config.DeviceBatch.Index(bi.DefaultModelId))
|
||||
link = Url.Action(MVC.Config.DeviceBatch.Index(bi.Id))
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ using Disco.Web.Models.Device;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
@@ -26,15 +28,15 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
{
|
||||
public partial class DeviceController : AuthorizedDatabaseController
|
||||
{
|
||||
const string pDeviceProfileId = "deviceprofileid";
|
||||
const string pDeviceBatchId = "devicebatchid";
|
||||
const string pAssetNumber = "assetnumber";
|
||||
const string pAssignedUserId = "assigneduserid";
|
||||
const string pLocation = "location";
|
||||
const string pAllowUnauthenticatedEnrol = "allowunauthenticatedenrol";
|
||||
const string pDetailACAdapter = "detailacadapter";
|
||||
const string pDetailBattery = "detailbattery";
|
||||
const string pDetailKeyboard = "detailkeyboard";
|
||||
private const string pDeviceProfileId = "deviceprofileid";
|
||||
private const string pDeviceBatchId = "devicebatchid";
|
||||
private const string pAssetNumber = "assetnumber";
|
||||
private const string pAssignedUserId = "assigneduserid";
|
||||
private const string pLocation = "location";
|
||||
private const string pAllowUnauthenticatedEnrol = "allowunauthenticatedenrol";
|
||||
private const string pDetailACAdapter = "detailacadapter";
|
||||
private const string pDetailBattery = "detailbattery";
|
||||
private const string pDetailKeyboard = "detailkeyboard";
|
||||
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult Update(string id, string key, string value = null, bool redirect = false)
|
||||
@@ -545,7 +547,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
#region Device Attachments
|
||||
|
||||
[DiscoAuthorize(Claims.Device.ShowAttachments), OutputCache(Location = System.Web.UI.OutputCacheLocation.Client, Duration = 172800)]
|
||||
public virtual ActionResult AttachmentDownload(int id)
|
||||
public virtual ActionResult AttachmentDownload(int id, bool? inline = null)
|
||||
{
|
||||
var da = Database.DeviceAttachments.Find(id);
|
||||
if (da != null)
|
||||
@@ -553,7 +555,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
var filePath = da.RepositoryFilename(Database);
|
||||
if (System.IO.File.Exists(filePath))
|
||||
{
|
||||
return File(filePath, da.MimeType, da.Filename);
|
||||
return File(filePath, da.MimeType, (inline ?? false) ? null : da.Filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -563,6 +565,44 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
return HttpNotFound("Invalid Attachment Number");
|
||||
}
|
||||
|
||||
[DiscoAuthorize(Claims.Device.ShowAttachments)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult AttachmentDownloadAll(string id)
|
||||
{
|
||||
var device = Database.Devices
|
||||
.Include(u => u.DeviceAttachments)
|
||||
.Where(u => u.SerialNumber == id)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (device == null || device.DeviceAttachments.Count == 0)
|
||||
return NotFound();
|
||||
|
||||
var responseStream = new MemoryStream();
|
||||
using (var archive = new ZipArchive(responseStream, ZipArchiveMode.Create, true))
|
||||
{
|
||||
foreach (var attachment in device.DeviceAttachments)
|
||||
{
|
||||
var repoFileName = attachment.RepositoryFilename(Database);
|
||||
if (System.IO.File.Exists(repoFileName))
|
||||
{
|
||||
var fileName = $"{Path.GetFileNameWithoutExtension(attachment.Filename)}-{attachment.Timestamp:yyyyMMdd-HHmmss}{Path.GetExtension(attachment.Filename)}";
|
||||
|
||||
var entry = archive.CreateEntry(fileName, CompressionLevel.Fastest);
|
||||
entry.LastWriteTime = attachment.Timestamp;
|
||||
using (var entryStream = entry.Open())
|
||||
{
|
||||
using (var attachmentStream = System.IO.File.OpenRead(repoFileName))
|
||||
{
|
||||
attachmentStream.CopyTo(entryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
responseStream.Position = 0;
|
||||
return File(responseStream, "application/zip", $"{device.SerialNumber}_DeviceAttachments_{DateTime.Now:yyyyMMdd-HHmmss}.zip");
|
||||
}
|
||||
|
||||
[DiscoAuthorize(Claims.Device.ShowAttachments), OutputCache(Location = System.Web.UI.OutputCacheLocation.Client, Duration = 172800)]
|
||||
public virtual ActionResult AttachmentThumbnail(int id)
|
||||
{
|
||||
|
||||
@@ -166,19 +166,17 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
[DiscoAuthorize(Claims.Config.DeviceFlag.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult UpdateDevicesLinkedGroup(int id, string GroupId = null, DateTime? FilterBeginDate = null, bool redirect = false)
|
||||
public virtual ActionResult UpdateDevicesLinkedGroup(int id, string groupId = null, DateTime? filterBeginDate = null, bool? updateDescription = null, bool redirect = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id < 0)
|
||||
throw new ArgumentOutOfRangeException("id");
|
||||
throw new ArgumentOutOfRangeException(nameof(id));
|
||||
|
||||
var deviceFlag = Database.DeviceFlags.Find(id);
|
||||
if (deviceFlag == null)
|
||||
throw new ArgumentException("Invalid Device Flag Id", "id");
|
||||
var deviceFlag = Database.DeviceFlags.Find(id)
|
||||
?? throw new ArgumentException("Invalid Device Flag Id", nameof(id));
|
||||
|
||||
|
||||
var syncTaskStatus = UpdateDevicesLinkedGroup(deviceFlag, GroupId, FilterBeginDate);
|
||||
var syncTaskStatus = UpdateDevicesLinkedGroup(deviceFlag, groupId, filterBeginDate, updateDescription ?? true);
|
||||
if (redirect)
|
||||
if (syncTaskStatus == null)
|
||||
return RedirectToAction(MVC.Config.DeviceFlag.Index(deviceFlag.Id));
|
||||
@@ -200,25 +198,23 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
[DiscoAuthorize(Claims.Config.DeviceFlag.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult UpdateAssignedUserLinkedGroup(int id, string GroupId = null, DateTime? FilterBeginDate = null, bool redirect = false)
|
||||
public virtual ActionResult UpdateAssignedUserLinkedGroup(int id, string groupId = null, DateTime? filterBeginDate = null, bool? updateDescription = null, bool redirect = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id < 0)
|
||||
throw new ArgumentOutOfRangeException("id");
|
||||
throw new ArgumentOutOfRangeException(nameof(id));
|
||||
|
||||
var DeviceFlag = Database.DeviceFlags.Find(id);
|
||||
if (DeviceFlag == null)
|
||||
throw new ArgumentException("Invalid Device Flag Id", "id");
|
||||
var deviceFlag = Database.DeviceFlags.Find(id)
|
||||
?? throw new ArgumentException("Invalid Device Flag Id", nameof(id));
|
||||
|
||||
|
||||
var syncTaskStatus = UpdateAssignedUserLinkedGroup(DeviceFlag, GroupId, FilterBeginDate);
|
||||
var syncTaskStatus = UpdateAssignedUserLinkedGroup(deviceFlag, groupId, filterBeginDate, updateDescription ?? true);
|
||||
if (redirect)
|
||||
if (syncTaskStatus == null)
|
||||
return RedirectToAction(MVC.Config.DeviceFlag.Index(DeviceFlag.Id));
|
||||
return RedirectToAction(MVC.Config.DeviceFlag.Index(deviceFlag.Id));
|
||||
else
|
||||
{
|
||||
syncTaskStatus.SetFinishedUrl(Url.Action(MVC.Config.DeviceFlag.Index(DeviceFlag.Id)));
|
||||
syncTaskStatus.SetFinishedUrl(Url.Action(MVC.Config.DeviceFlag.Index(deviceFlag.Id)));
|
||||
return RedirectToAction(MVC.Config.Logging.TaskStatus(syncTaskStatus.SessionId));
|
||||
}
|
||||
else
|
||||
@@ -340,9 +336,9 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
DeviceFlagService.Update(Database, deviceFlag);
|
||||
}
|
||||
|
||||
private ScheduledTaskStatus UpdateDevicesLinkedGroup(DeviceFlag deviceFlag, string devicesLinkedGroup, DateTime? filterBeginDate)
|
||||
private ScheduledTaskStatus UpdateDevicesLinkedGroup(DeviceFlag deviceFlag, string devicesLinkedGroup, DateTime? filterBeginDate, bool updateDescription)
|
||||
{
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DeviceFlagDevicesManagedGroup.GetKey(deviceFlag), devicesLinkedGroup, filterBeginDate);
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DeviceFlagDevicesManagedGroup.GetKey(deviceFlag), devicesLinkedGroup, filterBeginDate, updateDescription);
|
||||
|
||||
if (deviceFlag.DevicesLinkedGroup != configJson)
|
||||
{
|
||||
@@ -358,9 +354,9 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
|
||||
return null;
|
||||
}
|
||||
private ScheduledTaskStatus UpdateAssignedUserLinkedGroup(DeviceFlag deviceFlag, string assignedUserLinkedGroup, DateTime? filterBeginDate)
|
||||
private ScheduledTaskStatus UpdateAssignedUserLinkedGroup(DeviceFlag deviceFlag, string assignedUserLinkedGroup, DateTime? filterBeginDate, bool updateDescription)
|
||||
{
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DeviceFlagDeviceAssignedUsersManagedGroup.GetKey(deviceFlag), assignedUserLinkedGroup, filterBeginDate);
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DeviceFlagDeviceAssignedUsersManagedGroup.GetKey(deviceFlag), assignedUserLinkedGroup, filterBeginDate, updateDescription);
|
||||
|
||||
if (deviceFlag.DeviceUsersLinkedGroup != configJson)
|
||||
{
|
||||
|
||||
@@ -38,8 +38,6 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
private const string pAssignedUserLocalAdmin = "assigneduserlocaladmin";
|
||||
private const string pSetAssignedUserForLogon = "setassigneduserforlogon";
|
||||
private const string pAllowUntrustedReimageJobEnrolment = "allowuntrustedreimagejobrnrolment";
|
||||
private const string pDevicesLinkedGroup = "deviceslinkedgroup";
|
||||
private const string pAssignedUsersLinkedGroup = "assigneduserslinkedgroup";
|
||||
|
||||
[DiscoAuthorize(Claims.Config.DeviceProfile.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
@@ -106,12 +104,6 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
case pAllowUntrustedReimageJobEnrolment:
|
||||
UpdateAllowUntrustedReimageJobEnrolment(deviceProfile, value);
|
||||
break;
|
||||
case pDevicesLinkedGroup:
|
||||
UpdateDevicesLinkedGroup(deviceProfile, value);
|
||||
break;
|
||||
case pAssignedUsersLinkedGroup:
|
||||
UpdateAssignedUsersLinkedGroup(deviceProfile, value);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Invalid Update Key");
|
||||
}
|
||||
@@ -385,18 +377,17 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
|
||||
[DiscoAuthorize(Claims.Config.DeviceProfile.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult UpdateDevicesLinkedGroup(int id, string GroupId = null, bool redirect = false)
|
||||
public virtual ActionResult UpdateDevicesLinkedGroup(int id, string groupId = null, bool? updateDescription = null, bool redirect = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id < 0)
|
||||
throw new ArgumentOutOfRangeException("id");
|
||||
throw new ArgumentOutOfRangeException(nameof(id));
|
||||
|
||||
var deviceProfile = Database.DeviceProfiles.Find(id);
|
||||
if (deviceProfile == null)
|
||||
throw new ArgumentException("Invalid Device Profile Id", "id");
|
||||
var deviceProfile = Database.DeviceProfiles.Find(id)
|
||||
?? throw new ArgumentException("Invalid Device Profile Id", nameof(id));
|
||||
|
||||
var syncTaskStatus = UpdateDevicesLinkedGroup(deviceProfile, GroupId);
|
||||
var syncTaskStatus = UpdateDevicesLinkedGroup(deviceProfile, groupId, updateDescription ?? true);
|
||||
if (redirect)
|
||||
if (syncTaskStatus == null)
|
||||
return RedirectToAction(MVC.Config.DeviceProfile.Index(deviceProfile.Id));
|
||||
@@ -418,18 +409,17 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
[DiscoAuthorize(Claims.Config.DeviceProfile.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult UpdateAssignedUsersLinkedGroup(int id, string GroupId = null, bool redirect = false)
|
||||
public virtual ActionResult UpdateAssignedUsersLinkedGroup(int id, string groupId = null, bool? updateDescription = null, bool redirect = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id < 0)
|
||||
throw new ArgumentOutOfRangeException("id");
|
||||
throw new ArgumentOutOfRangeException(nameof(id));
|
||||
|
||||
var deviceProfile = Database.DeviceProfiles.Find(id);
|
||||
if (deviceProfile == null)
|
||||
throw new ArgumentException("Invalid Device Profile Id", "id");
|
||||
var deviceProfile = Database.DeviceProfiles.Find(id)
|
||||
?? throw new ArgumentException("Invalid Device Profile Id", nameof(id));
|
||||
|
||||
var syncTaskStatus = UpdateAssignedUsersLinkedGroup(deviceProfile, GroupId);
|
||||
var syncTaskStatus = UpdateAssignedUsersLinkedGroup(deviceProfile, groupId, updateDescription ?? true);
|
||||
if (redirect)
|
||||
if (syncTaskStatus == null)
|
||||
return RedirectToAction(MVC.Config.DeviceProfile.Index(deviceProfile.Id));
|
||||
@@ -725,9 +715,9 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
throw new Exception("Invalid Boolean Value");
|
||||
}
|
||||
|
||||
private ScheduledTaskStatus UpdateDevicesLinkedGroup(DeviceProfile deviceProfile, string devicesLinkedGroup)
|
||||
private ScheduledTaskStatus UpdateDevicesLinkedGroup(DeviceProfile deviceProfile, string devicesLinkedGroup, bool updateDescription)
|
||||
{
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DeviceProfileDevicesManagedGroup.GetKey(deviceProfile), devicesLinkedGroup, null);
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DeviceProfileDevicesManagedGroup.GetKey(deviceProfile), devicesLinkedGroup, null, updateDescription);
|
||||
|
||||
if (deviceProfile.DevicesLinkedGroup != configJson)
|
||||
{
|
||||
@@ -742,9 +732,9 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
return null;
|
||||
}
|
||||
|
||||
private ScheduledTaskStatus UpdateAssignedUsersLinkedGroup(DeviceProfile deviceProfile, string assignedUsersLinkedGroup)
|
||||
private ScheduledTaskStatus UpdateAssignedUsersLinkedGroup(DeviceProfile deviceProfile, string assignedUsersLinkedGroup, bool updateDescription)
|
||||
{
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DeviceProfileAssignedUsersManagedGroup.GetKey(deviceProfile), assignedUsersLinkedGroup, null);
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DeviceProfileAssignedUsersManagedGroup.GetKey(deviceProfile), assignedUsersLinkedGroup, null, updateDescription);
|
||||
|
||||
if (deviceProfile.AssignedUsersLinkedGroup != configJson)
|
||||
{
|
||||
|
||||
@@ -20,6 +20,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Data.Entity;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
@@ -254,18 +255,17 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
|
||||
[DiscoAuthorize(Claims.Config.DocumentTemplate.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult UpdateDevicesLinkedGroup(string id, string GroupId = null, DateTime? FilterBeginDate = null, bool redirect = false)
|
||||
public virtual ActionResult UpdateDevicesLinkedGroup(string id, string groupId = null, DateTime? filterBeginDate = null, bool? updateDescription = null, bool redirect = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
throw new ArgumentNullException("id");
|
||||
throw new ArgumentNullException(nameof(id));
|
||||
|
||||
var documentTemplate = Database.DocumentTemplates.Find(id);
|
||||
if (documentTemplate == null)
|
||||
throw new ArgumentException("Invalid Document Template Id", "id");
|
||||
var documentTemplate = Database.DocumentTemplates.Find(id)
|
||||
?? throw new ArgumentException("Invalid Document Template Id", nameof(id));
|
||||
|
||||
var syncTaskStatus = UpdateDevicesLinkedGroup(documentTemplate, GroupId, FilterBeginDate);
|
||||
var syncTaskStatus = UpdateDevicesLinkedGroup(documentTemplate, groupId, filterBeginDate, updateDescription ?? true);
|
||||
if (redirect)
|
||||
if (syncTaskStatus == null)
|
||||
return RedirectToAction(MVC.Config.DocumentTemplate.Index(documentTemplate.Id));
|
||||
@@ -288,18 +288,17 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
|
||||
[DiscoAuthorize(Claims.Config.DocumentTemplate.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult UpdateUsersLinkedGroup(string id, string GroupId = null, DateTime? FilterBeginDate = null, bool redirect = false)
|
||||
public virtual ActionResult UpdateUsersLinkedGroup(string id, string groupId = null, DateTime? filterBeginDate = null, bool? updateDescription = null, bool redirect = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
throw new ArgumentNullException("id");
|
||||
throw new ArgumentNullException(nameof(id));
|
||||
|
||||
var documentTemplate = Database.DocumentTemplates.Find(id);
|
||||
if (documentTemplate == null)
|
||||
throw new ArgumentException("Invalid Document Template Id", "id");
|
||||
var documentTemplate = Database.DocumentTemplates.Find(id)
|
||||
?? throw new ArgumentException("Invalid Document Template Id", nameof(id));
|
||||
|
||||
var syncTaskStatus = UpdateUsersLinkedGroup(documentTemplate, GroupId, FilterBeginDate);
|
||||
var syncTaskStatus = UpdateUsersLinkedGroup(documentTemplate, groupId, filterBeginDate, updateDescription ?? true);
|
||||
if (redirect)
|
||||
if (syncTaskStatus == null)
|
||||
return RedirectToAction(MVC.Config.DocumentTemplate.Index(documentTemplate.Id));
|
||||
@@ -469,16 +468,16 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
Database.SaveChanges();
|
||||
}
|
||||
|
||||
private ScheduledTaskStatus UpdateDevicesLinkedGroup(DocumentTemplate DocumentTemplate, string DevicesLinkedGroup, DateTime? FilterBeginDate)
|
||||
private ScheduledTaskStatus UpdateDevicesLinkedGroup(DocumentTemplate documentTemplate, string devicesLinkedGroup, DateTime? filterBeginDate, bool updateDescription)
|
||||
{
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DocumentTemplateDevicesManagedGroup.GetKey(DocumentTemplate), DevicesLinkedGroup, FilterBeginDate);
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DocumentTemplateDevicesManagedGroup.GetKey(documentTemplate), devicesLinkedGroup, filterBeginDate, updateDescription);
|
||||
|
||||
if (DocumentTemplate.DevicesLinkedGroup != configJson)
|
||||
if (documentTemplate.DevicesLinkedGroup != configJson)
|
||||
{
|
||||
DocumentTemplate.DevicesLinkedGroup = configJson;
|
||||
documentTemplate.DevicesLinkedGroup = configJson;
|
||||
Database.SaveChanges();
|
||||
|
||||
var managedGroup = DocumentTemplateDevicesManagedGroup.Initialize(DocumentTemplate);
|
||||
var managedGroup = DocumentTemplateDevicesManagedGroup.Initialize(documentTemplate);
|
||||
if (managedGroup != null) // Sync Group
|
||||
return ADManagedGroupsSyncTask.ScheduleSync(managedGroup);
|
||||
}
|
||||
@@ -486,16 +485,16 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
return null;
|
||||
}
|
||||
|
||||
private ScheduledTaskStatus UpdateUsersLinkedGroup(DocumentTemplate DocumentTemplate, string UsersLinkedGroup, DateTime? FilterBeginDate)
|
||||
private ScheduledTaskStatus UpdateUsersLinkedGroup(DocumentTemplate documentTemplate, string usersLinkedGroup, DateTime? filterBeginDate, bool updateDescription)
|
||||
{
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DocumentTemplateUsersManagedGroup.GetKey(DocumentTemplate), UsersLinkedGroup, FilterBeginDate);
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(DocumentTemplateUsersManagedGroup.GetKey(documentTemplate), usersLinkedGroup, filterBeginDate, updateDescription);
|
||||
|
||||
if (DocumentTemplate.UsersLinkedGroup != configJson)
|
||||
if (documentTemplate.UsersLinkedGroup != configJson)
|
||||
{
|
||||
DocumentTemplate.UsersLinkedGroup = configJson;
|
||||
documentTemplate.UsersLinkedGroup = configJson;
|
||||
Database.SaveChanges();
|
||||
|
||||
var managedGroup = DocumentTemplateUsersManagedGroup.Initialize(DocumentTemplate);
|
||||
var managedGroup = DocumentTemplateUsersManagedGroup.Initialize(documentTemplate);
|
||||
if (managedGroup != null) // Sync Group
|
||||
return ADManagedGroupsSyncTask.ScheduleSync(managedGroup);
|
||||
}
|
||||
@@ -1775,7 +1774,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult Generate(string id, string targetId)
|
||||
public virtual ActionResult Generate(string id, string targetId, bool? inline = null)
|
||||
{
|
||||
Disco.Services.DocumentTemplateExtensions.GetTemplateAndTarget(Database, Authorization, id, targetId, out var template, out var target, out _);
|
||||
|
||||
@@ -1788,7 +1787,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
Database.SaveChanges();
|
||||
|
||||
return File(document, "application/pdf", $"{template.Id}_{target.AttachmentReferenceId.Replace('\\', '_')}_{timestamp:yyyyMMdd-HHmmss}.pdf");
|
||||
return File(document, "application/pdf", (inline ?? false) ? null : $"{template.Id}_{target.AttachmentReferenceId.Replace('\\', '_')}_{timestamp:yyyyMMdd-HHmmss}.pdf");
|
||||
}
|
||||
|
||||
[DiscoAuthorize(Claims.Config.DocumentTemplate.Delete)]
|
||||
@@ -1880,13 +1879,89 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[DiscoAuthorize(Claims.Config.DocumentTemplate.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult BulkDownload([Required] string id, bool? latestOnly = null, DateTime? threshold = null)
|
||||
{
|
||||
var template = Database.DocumentTemplates.FirstOrDefault(t => t.Id == id)
|
||||
?? throw new ArgumentException("Unknown document template", nameof(id));
|
||||
|
||||
var attachments = BulkDownloadRetrieveAttachments(template, latestOnly ?? false, threshold);
|
||||
|
||||
var responseStream = new MemoryStream();
|
||||
using (var archive = new ZipArchive(responseStream, ZipArchiveMode.Create, true))
|
||||
{
|
||||
foreach (var attachment in attachments)
|
||||
{
|
||||
var repoFileName = attachment.RepositoryFilename(Database);
|
||||
if (System.IO.File.Exists(repoFileName))
|
||||
{
|
||||
var entry = archive.CreateEntry($"{attachment.Reference.ToString().Replace('\\', '_')}-{attachment.Timestamp:yyyyMMdd-HHmmss}_{attachment.Filename}", CompressionLevel.Fastest);
|
||||
entry.LastWriteTime = attachment.Timestamp;
|
||||
using (var entryStream = entry.Open())
|
||||
{
|
||||
using (var attachmentStream = System.IO.File.OpenRead(repoFileName))
|
||||
{
|
||||
attachmentStream.CopyTo(entryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
responseStream.Position = 0;
|
||||
return File(responseStream, "application/zip", $"{template.Id}_Attachments_{DateTime.Now:yyyyMMdd-HHmmss}.zip");
|
||||
}
|
||||
private List<IAttachment> BulkDownloadRetrieveAttachments(DocumentTemplate template, bool latestOnly, DateTime? threshold)
|
||||
{
|
||||
List<IAttachment> attachments;
|
||||
|
||||
switch (template.Scope)
|
||||
{
|
||||
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||
Authorization.Require(Claims.Device.ShowAttachments);
|
||||
var deviceQuery = Database.DeviceAttachments
|
||||
.Where(a => a.DocumentTemplateId == template.Id);
|
||||
if (threshold.HasValue)
|
||||
deviceQuery = deviceQuery.Where(a => a.Timestamp >= threshold.Value);
|
||||
attachments = deviceQuery.OrderBy(a => a.Timestamp).ToList<IAttachment>();
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||
Authorization.Require(Claims.Job.ShowAttachments);
|
||||
var jobQuery = Database.JobAttachments
|
||||
.Where(a => a.DocumentTemplateId == template.Id);
|
||||
if (threshold.HasValue)
|
||||
jobQuery = jobQuery.Where(a => a.Timestamp >= threshold.Value);
|
||||
attachments = jobQuery.OrderBy(a => a.Timestamp).ToList<IAttachment>();
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||
Authorization.Require(Claims.User.ShowAttachments);
|
||||
var userQuery = Database.UserAttachments
|
||||
.Where(a => a.DocumentTemplateId == template.Id);
|
||||
if (threshold.HasValue)
|
||||
userQuery = userQuery.Where(a => a.Timestamp >= threshold.Value);
|
||||
attachments = userQuery.OrderBy(a => a.Timestamp).ToList<IAttachment>();
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
if (latestOnly)
|
||||
{
|
||||
attachments.Reverse();
|
||||
attachments = attachments.GroupBy(a => a.Reference).Select(a => a.First()).OrderBy(a => a.Timestamp).ToList();
|
||||
}
|
||||
|
||||
return attachments;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Handlers
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult GenerateDocumentHandlerUi(string templateId, string targetId, string handlerId)
|
||||
public virtual ActionResult GenerateDocumentHandlerUi(string id, string targetId, string handlerId)
|
||||
{
|
||||
Disco.Services.DocumentTemplateExtensions.GetTemplateAndTarget(Database, Authorization, templateId, targetId, out var template, out var target, out var targetUser);
|
||||
Disco.Services.DocumentTemplateExtensions.GetTemplateAndTarget(Database, Authorization, id, targetId, out var template, out var target, out var targetUser);
|
||||
|
||||
var handlerManifest = Plugins.GetPluginFeature(handlerId, typeof(DocumentHandlerProviderFeature));
|
||||
|
||||
@@ -1907,9 +1982,9 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult DocumentHandlers(string templateId, string targetId)
|
||||
public virtual ActionResult DocumentHandlers(string id, string targetId)
|
||||
{
|
||||
Disco.Services.DocumentTemplateExtensions.GetTemplateAndTarget(Database, Authorization, templateId, targetId, out var template, out var target, out _);
|
||||
Disco.Services.DocumentTemplateExtensions.GetTemplateAndTarget(Database, Authorization, id, targetId, out var template, out var target, out _);
|
||||
|
||||
var handlers = Plugins.GetPluginFeatures(typeof(DocumentHandlerProviderFeature))
|
||||
.SelectMany(f =>
|
||||
|
||||
@@ -4,8 +4,11 @@ using Disco.Models.Services.Documents;
|
||||
using Disco.Services;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Documents;
|
||||
using Disco.Services.Plugins;
|
||||
using Disco.Services.Plugins.Features.DocumentHandlerProvider;
|
||||
using Disco.Services.Users;
|
||||
using Disco.Services.Web;
|
||||
using Disco.Web.Areas.API.Models.DocumentTemplate;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -16,12 +19,12 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
{
|
||||
public partial class DocumentTemplatePackageController : AuthorizedDatabaseController
|
||||
{
|
||||
const string pDescription = "description";
|
||||
const string pScope = "scope";
|
||||
const string pFilterExpression = "filterexpression";
|
||||
const string pOnGenerateExpression = "ongenerateexpression";
|
||||
const string pIsHidden = "ishidden";
|
||||
const string pInsertBlankPages = "insertblankpages";
|
||||
private const string pDescription = "description";
|
||||
private const string pScope = "scope";
|
||||
private const string pFilterExpression = "filterexpression";
|
||||
private const string pOnGenerateExpression = "ongenerateexpression";
|
||||
private const string pIsHidden = "ishidden";
|
||||
private const string pInsertBlankPages = "insertblankpages";
|
||||
|
||||
[DiscoAuthorize(Claims.Config.DocumentTemplate.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
@@ -393,7 +396,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult Generate(string id, string targetId)
|
||||
public virtual ActionResult Generate(string id, string targetId, bool? inline = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
throw new ArgumentNullException(nameof(id));
|
||||
@@ -432,7 +435,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
Database.SaveChanges();
|
||||
|
||||
return File(document, "application/pdf", $"{package.Id}_{target.AttachmentReferenceId.Replace('\\', '_')}_{timestamp:yyyyMMdd-HHmmss}.pdf");
|
||||
return File(document, "application/pdf", (inline ?? false) ? null : $"{package.Id}_{target.AttachmentReferenceId.Replace('\\', '_')}_{timestamp:yyyyMMdd-HHmmss}.pdf");
|
||||
}
|
||||
|
||||
[DiscoAuthorize(Claims.Config.DocumentTemplate.Delete)]
|
||||
@@ -470,5 +473,65 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Handlers
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult GenerateDocumentHandlerUi(string id, string targetId, string handlerId)
|
||||
{
|
||||
DocumentTemplatePackageExtensions.GetPackageAndTarget(Database, Authorization, id, targetId, out var package, out var target, out var targetUser);
|
||||
|
||||
var handlerManifest = Plugins.GetPluginFeature(handlerId, typeof(DocumentHandlerProviderFeature));
|
||||
|
||||
using (var handler = handlerManifest.CreateInstance<DocumentHandlerProviderFeature>())
|
||||
{
|
||||
if (!handler.CanHandle(package, target))
|
||||
throw new NotSupportedException("Handler does not support this Document Template and Target");
|
||||
|
||||
var handlerPartialView = handler.GenerationOptionsUi;
|
||||
|
||||
if (handlerPartialView == null)
|
||||
throw new NotSupportedException("Handler does not have a Generation Options UI");
|
||||
|
||||
var model = handler.GetGenerationOptionsUiModel(package, target, targetUser, CurrentUser);
|
||||
|
||||
return this.PrecompiledPartialView(handlerPartialView, model);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult DocumentHandlers(string id, string targetId)
|
||||
{
|
||||
DocumentTemplatePackageExtensions.GetPackageAndTarget(Database, Authorization, id, targetId, out var package, out var target, out _);
|
||||
|
||||
var handlers = Plugins.GetPluginFeatures(typeof(DocumentHandlerProviderFeature))
|
||||
.SelectMany(f =>
|
||||
{
|
||||
using (var handler = f.CreateInstance<DocumentHandlerProviderFeature>())
|
||||
{
|
||||
if (handler.CanHandle(package, target))
|
||||
return OneOf.Create(new DocumentHandlerModel()
|
||||
{
|
||||
Id = f.Id,
|
||||
Title = handler.HandlerTitle,
|
||||
Description = handler.HandlerDescription,
|
||||
UiUrl = handler.GenerationOptionsUi == null ? null : Url.Action(MVC.API.DocumentTemplatePackage.GenerateDocumentHandlerUi(package.Id, target.AttachmentReferenceId, f.Id)),
|
||||
Icon = handler.GenerationOptionsIcon,
|
||||
});
|
||||
}
|
||||
return Enumerable.Empty<DocumentHandlerModel>();
|
||||
}).ToList();
|
||||
|
||||
var model = new DocumentHandlersModel()
|
||||
{
|
||||
TemplateId = package.Id,
|
||||
TemplateName = package.Description,
|
||||
TargetId = target.AttachmentReferenceId,
|
||||
TargetName = target.ToString(),
|
||||
Handlers = handlers,
|
||||
};
|
||||
|
||||
return Json(model);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -88,5 +88,21 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[DiscoAuthorize(Claims.Config.Enrolment.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult LegacyDiscovery(bool enabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
Database.DiscoConfiguration.Devices.EnrollmentLegacyDiscoveryDisabled = !enabled;
|
||||
Database.SaveChanges();
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ using Disco.Web.Models.Job;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Caching;
|
||||
@@ -1925,7 +1927,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
#region Job Attachments
|
||||
|
||||
[DiscoAuthorize(Claims.Job.ShowAttachments), OutputCache(Location = System.Web.UI.OutputCacheLocation.Client, Duration = 172800)]
|
||||
public virtual ActionResult AttachmentDownload(int id)
|
||||
public virtual ActionResult AttachmentDownload(int id, bool? inline = null)
|
||||
{
|
||||
var ja = Database.JobAttachments.Find(id);
|
||||
if (ja != null)
|
||||
@@ -1933,7 +1935,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
var filePath = ja.RepositoryFilename(Database);
|
||||
if (System.IO.File.Exists(filePath))
|
||||
{
|
||||
return File(filePath, ja.MimeType, ja.Filename);
|
||||
return File(filePath, ja.MimeType, (inline ?? false) ? null : ja.Filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1943,6 +1945,44 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
return HttpNotFound("Invalid Attachment Number");
|
||||
}
|
||||
|
||||
[DiscoAuthorize(Claims.Job.ShowAttachments)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult AttachmentDownloadAll(int id)
|
||||
{
|
||||
var job = Database.Jobs
|
||||
.Include(u => u.JobAttachments)
|
||||
.Where(u => u.Id == id)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (job == null || job.JobAttachments.Count == 0)
|
||||
return NotFound();
|
||||
|
||||
var responseStream = new MemoryStream();
|
||||
using (var archive = new ZipArchive(responseStream, ZipArchiveMode.Create, true))
|
||||
{
|
||||
foreach (var attachment in job.JobAttachments)
|
||||
{
|
||||
var repoFileName = attachment.RepositoryFilename(Database);
|
||||
if (System.IO.File.Exists(repoFileName))
|
||||
{
|
||||
var fileName = $"{Path.GetFileNameWithoutExtension(attachment.Filename)}-{attachment.Timestamp:yyyyMMdd-HHmmss}{Path.GetExtension(attachment.Filename)}";
|
||||
|
||||
var entry = archive.CreateEntry(fileName, CompressionLevel.Fastest);
|
||||
entry.LastWriteTime = attachment.Timestamp;
|
||||
using (var entryStream = entry.Open())
|
||||
{
|
||||
using (var attachmentStream = System.IO.File.OpenRead(repoFileName))
|
||||
{
|
||||
attachmentStream.CopyTo(entryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
responseStream.Position = 0;
|
||||
return File(responseStream, "application/zip", $"{job.Id}_JobAttachments_{DateTime.Now:yyyyMMdd-HHmmss}.zip");
|
||||
}
|
||||
|
||||
[DiscoAuthorize(Claims.Job.ShowAttachments), OutputCache(Location = System.Web.UI.OutputCacheLocation.Client, Duration = 172800)]
|
||||
public virtual ActionResult AttachmentThumbnail(int id)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ using Disco.Services.Messaging;
|
||||
using Disco.Services.Users;
|
||||
using Disco.Services.Web;
|
||||
using Disco.Web.Areas.API.Models.Shared;
|
||||
using Disco.Web.Models.Shared;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
@@ -309,16 +310,99 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
|
||||
[DiscoAuthorizeAny(Claims.Config.System.ConfigureActiveDirectory, Claims.Config.DeviceProfile.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult DomainOrganisationalUnits()
|
||||
public virtual ActionResult DomainOrganisationalUnitTree(string expandNode = null)
|
||||
{
|
||||
var domainOUs = ActiveDirectory.RetrieveADOrganisationalUnitStructure()
|
||||
.Select(d => new Models.System.DomainOrganisationalUnitsModel() { Domain = d.Item1, OrganisationalUnits = d.Item2 })
|
||||
.Select(ous => ous.ToFancyTreeNode()).ToList();
|
||||
List<FancyTreeNode> nodes;
|
||||
|
||||
nodes = ActiveDirectory.Context.Domains
|
||||
.Select(d => new FancyTreeNode()
|
||||
{
|
||||
key = d.DistinguishedName,
|
||||
title = d.NetBiosName,
|
||||
folder = true,
|
||||
tooltip = d.Name,
|
||||
children = d.GetAvailableDomainController().RetrieveADOrganisationUnits()
|
||||
.Select(ou => new FancyTreeNode()
|
||||
{
|
||||
key = ou.DistinguishedName,
|
||||
title = ou.Name,
|
||||
folder = true,
|
||||
tooltip = ou.DistinguishedName,
|
||||
unselectable = false,
|
||||
expanded = false,
|
||||
lazy = true,
|
||||
}).ToArray(),
|
||||
unselectable = true,
|
||||
expanded = true,
|
||||
lazy = false,
|
||||
}).ToList();
|
||||
if (!string.IsNullOrWhiteSpace(expandNode) && ActiveDirectory.Context.TryGetDomainFromDistinguishedName(expandNode, out var domain))
|
||||
{
|
||||
// domain node
|
||||
var node = nodes.FirstOrDefault(n => n.key.Equals(domain.DistinguishedName, StringComparison.OrdinalIgnoreCase));
|
||||
if (node != null)
|
||||
{
|
||||
var domainController = domain.GetAvailableDomainController();
|
||||
var ouIndex = expandNode.Length;
|
||||
do
|
||||
{
|
||||
ouIndex = expandNode.LastIndexOf("OU=", ouIndex - 1, StringComparison.OrdinalIgnoreCase);
|
||||
if (ouIndex >= 0)
|
||||
{
|
||||
var dn = expandNode.Substring(ouIndex);
|
||||
|
||||
node = node.children.FirstOrDefault(n => n.key.Equals(dn, StringComparison.OrdinalIgnoreCase));
|
||||
if (node != null)
|
||||
{
|
||||
node.children = domainController.RetrieveADOrganisationUnits(dn).Select(ou => new FancyTreeNode()
|
||||
{
|
||||
key = ou.DistinguishedName,
|
||||
title = ou.Name,
|
||||
folder = true,
|
||||
tooltip = ou.DistinguishedName,
|
||||
unselectable = false,
|
||||
expanded = false,
|
||||
lazy = true,
|
||||
}).ToArray();
|
||||
node.expanded = true;
|
||||
node.lazy = false;
|
||||
}
|
||||
}
|
||||
} while (node != null && ouIndex > 0);
|
||||
}
|
||||
}
|
||||
|
||||
return new JsonResult()
|
||||
{
|
||||
Data = domainOUs,
|
||||
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
|
||||
Data = nodes,
|
||||
MaxJsonLength = int.MaxValue
|
||||
};
|
||||
}
|
||||
|
||||
[DiscoAuthorizeAny(Claims.Config.System.ConfigureActiveDirectory, Claims.Config.DeviceProfile.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult DomainOrganisationalUnits(string node)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(node))
|
||||
throw new ArgumentNullException("node");
|
||||
if (!ActiveDirectory.Context.TryGetDomainFromDistinguishedName(node, out var domain))
|
||||
throw new ArgumentException("Invalid node distinguished name", "node");
|
||||
|
||||
var domainController = domain.GetAvailableDomainController();
|
||||
var nodes = domainController.RetrieveADOrganisationUnits(node).Select(ou => new FancyTreeNode()
|
||||
{
|
||||
key = ou.DistinguishedName,
|
||||
title = ou.Name,
|
||||
folder = true,
|
||||
tooltip = ou.DistinguishedName,
|
||||
unselectable = false,
|
||||
expanded = false,
|
||||
lazy = true,
|
||||
}).ToArray();
|
||||
|
||||
return new JsonResult()
|
||||
{
|
||||
Data = nodes,
|
||||
MaxJsonLength = int.MaxValue
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ using Disco.Services.Users;
|
||||
using Disco.Services.Web;
|
||||
using System;
|
||||
using System.Data.Entity;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Mvc;
|
||||
@@ -106,7 +108,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
|
||||
[DiscoAuthorize(Claims.User.ShowAttachments)]
|
||||
[OutputCache(Location = System.Web.UI.OutputCacheLocation.Client, Duration = 172800)]
|
||||
public virtual ActionResult AttachmentDownload(int id)
|
||||
public virtual ActionResult AttachmentDownload(int id, bool? inline = null)
|
||||
{
|
||||
var ua = Database.UserAttachments.Find(id);
|
||||
if (ua != null)
|
||||
@@ -114,7 +116,7 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
var filePath = ua.RepositoryFilename(Database);
|
||||
if (System.IO.File.Exists(filePath))
|
||||
{
|
||||
return File(filePath, ua.MimeType, ua.Filename);
|
||||
return File(filePath, ua.MimeType, (inline ?? false) ? null : ua.Filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -124,6 +126,46 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
return HttpNotFound("Invalid Attachment Number");
|
||||
}
|
||||
|
||||
[DiscoAuthorize(Claims.User.ShowAttachments)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult AttachmentDownloadAll(string id)
|
||||
{
|
||||
id = ActiveDirectory.ParseDomainAccountId(id);
|
||||
|
||||
var user = Database.Users
|
||||
.Include(u => u.UserAttachments)
|
||||
.Where(u => u.UserId == id)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (user == null || user.UserAttachments.Count == 0)
|
||||
return NotFound();
|
||||
|
||||
var responseStream = new MemoryStream();
|
||||
using (var archive = new ZipArchive(responseStream, ZipArchiveMode.Create, true))
|
||||
{
|
||||
foreach (var attachment in user.UserAttachments)
|
||||
{
|
||||
var repoFileName = attachment.RepositoryFilename(Database);
|
||||
if (System.IO.File.Exists(repoFileName))
|
||||
{
|
||||
var fileName = $"{Path.GetFileNameWithoutExtension(attachment.Filename)}-{attachment.Timestamp:yyyyMMdd-HHmmss}{Path.GetExtension(attachment.Filename)}";
|
||||
|
||||
var entry = archive.CreateEntry(fileName, CompressionLevel.Fastest);
|
||||
entry.LastWriteTime = attachment.Timestamp;
|
||||
using (var entryStream = entry.Open())
|
||||
{
|
||||
using (var attachmentStream = System.IO.File.OpenRead(repoFileName))
|
||||
{
|
||||
attachmentStream.CopyTo(entryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
responseStream.Position = 0;
|
||||
return File(responseStream, "application/zip", $"{user.UserId.Replace('\\', '_')}_UserAttachments_{DateTime.Now:yyyyMMdd-HHmmss}.zip");
|
||||
}
|
||||
|
||||
[DiscoAuthorize(Claims.User.ShowAttachments)]
|
||||
[OutputCache(Location = System.Web.UI.OutputCacheLocation.Client, Duration = 172800)]
|
||||
public virtual ActionResult AttachmentThumbnail(int id)
|
||||
|
||||
@@ -166,25 +166,23 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
[DiscoAuthorize(Claims.Config.UserFlag.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult UpdateAssignedUsersLinkedGroup(int id, string GroupId = null, DateTime? FilterBeginDate = null, bool redirect = false)
|
||||
public virtual ActionResult UpdateAssignedUsersLinkedGroup(int id, string groupId = null, DateTime? filterBeginDate = null, bool? updateDescription = null, bool redirect = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(id));
|
||||
|
||||
var UserFlag = Database.UserFlags.Find(id);
|
||||
if (UserFlag == null)
|
||||
throw new ArgumentException("Invalid User Flag Id", nameof(id));
|
||||
var userFlag = Database.UserFlags.Find(id)
|
||||
?? throw new ArgumentException("Invalid User Flag Id", nameof(id));
|
||||
|
||||
|
||||
var syncTaskStatus = UpdateAssignedUsersLinkedGroup(UserFlag, GroupId, FilterBeginDate);
|
||||
var syncTaskStatus = UpdateAssignedUsersLinkedGroup(userFlag, groupId, filterBeginDate, updateDescription ?? true);
|
||||
if (redirect)
|
||||
if (syncTaskStatus == null)
|
||||
return RedirectToAction(MVC.Config.UserFlag.Index(UserFlag.Id));
|
||||
return RedirectToAction(MVC.Config.UserFlag.Index(userFlag.Id));
|
||||
else
|
||||
{
|
||||
syncTaskStatus.SetFinishedUrl(Url.Action(MVC.Config.UserFlag.Index(UserFlag.Id)));
|
||||
syncTaskStatus.SetFinishedUrl(Url.Action(MVC.Config.UserFlag.Index(userFlag.Id)));
|
||||
return RedirectToAction(MVC.Config.Logging.TaskStatus(syncTaskStatus.SessionId));
|
||||
}
|
||||
else
|
||||
@@ -200,25 +198,23 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
}
|
||||
[DiscoAuthorize(Claims.Config.UserFlag.Configure)]
|
||||
[HttpPost, ValidateAntiForgeryToken]
|
||||
public virtual ActionResult UpdateAssignedUserDevicesLinkedGroup(int id, string GroupId = null, DateTime? FilterBeginDate = null, bool redirect = false)
|
||||
public virtual ActionResult UpdateAssignedUserDevicesLinkedGroup(int id, string groupId = null, DateTime? filterBeginDate = null, bool? updateDescription = null, bool redirect = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(id));
|
||||
|
||||
var UserFlag = Database.UserFlags.Find(id);
|
||||
if (UserFlag == null)
|
||||
throw new ArgumentException("Invalid User Flag Id", nameof(id));
|
||||
var userFlag = Database.UserFlags.Find(id)
|
||||
?? throw new ArgumentException("Invalid User Flag Id", nameof(id));
|
||||
|
||||
|
||||
var syncTaskStatus = UpdateAssignedUserDevicesLinkedGroup(UserFlag, GroupId, FilterBeginDate);
|
||||
var syncTaskStatus = UpdateAssignedUserDevicesLinkedGroup(userFlag, groupId, filterBeginDate, updateDescription ?? true);
|
||||
if (redirect)
|
||||
if (syncTaskStatus == null)
|
||||
return RedirectToAction(MVC.Config.UserFlag.Index(UserFlag.Id));
|
||||
return RedirectToAction(MVC.Config.UserFlag.Index(userFlag.Id));
|
||||
else
|
||||
{
|
||||
syncTaskStatus.SetFinishedUrl(Url.Action(MVC.Config.UserFlag.Index(UserFlag.Id)));
|
||||
syncTaskStatus.SetFinishedUrl(Url.Action(MVC.Config.UserFlag.Index(userFlag.Id)));
|
||||
return RedirectToAction(MVC.Config.Logging.TaskStatus(syncTaskStatus.SessionId));
|
||||
}
|
||||
else
|
||||
@@ -340,19 +336,19 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
UserFlagService.Update(Database, UserFlag);
|
||||
}
|
||||
|
||||
private ScheduledTaskStatus UpdateAssignedUsersLinkedGroup(UserFlag UserFlag, string AssignedUsersLinkedGroup, DateTime? FilterBeginDate)
|
||||
private ScheduledTaskStatus UpdateAssignedUsersLinkedGroup(UserFlag userFlag, string assignedUsersLinkedGroup, DateTime? filterBeginDate, bool updateDescription)
|
||||
{
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(UserFlagUsersManagedGroup.GetKey(UserFlag), AssignedUsersLinkedGroup, FilterBeginDate);
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(UserFlagUsersManagedGroup.GetKey(userFlag), assignedUsersLinkedGroup, filterBeginDate, updateDescription);
|
||||
|
||||
if (UserFlag.UsersLinkedGroup != configJson)
|
||||
if (userFlag.UsersLinkedGroup != configJson)
|
||||
{
|
||||
UserFlag.UsersLinkedGroup = configJson;
|
||||
UserFlagService.Update(Database, UserFlag);
|
||||
userFlag.UsersLinkedGroup = configJson;
|
||||
UserFlagService.Update(Database, userFlag);
|
||||
|
||||
if (UserFlag.UsersLinkedGroup != null)
|
||||
if (userFlag.UsersLinkedGroup != null)
|
||||
{
|
||||
// Sync Group
|
||||
if (UserFlagUsersManagedGroup.TryGetManagedGroup(UserFlag, out var managedGroup))
|
||||
if (UserFlagUsersManagedGroup.TryGetManagedGroup(userFlag, out var managedGroup))
|
||||
{
|
||||
return ADManagedGroupsSyncTask.ScheduleSync(managedGroup);
|
||||
}
|
||||
@@ -361,19 +357,19 @@ namespace Disco.Web.Areas.API.Controllers
|
||||
|
||||
return null;
|
||||
}
|
||||
private ScheduledTaskStatus UpdateAssignedUserDevicesLinkedGroup(UserFlag UserFlag, string AssignedUserDevicesLinkedGroup, DateTime? FilterBeginDate)
|
||||
private ScheduledTaskStatus UpdateAssignedUserDevicesLinkedGroup(UserFlag userFlag, string assignedUserDevicesLinkedGroup, DateTime? filterBeginDate, bool updateDescription)
|
||||
{
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(UserFlagUserDevicesManagedGroup.GetKey(UserFlag), AssignedUserDevicesLinkedGroup, FilterBeginDate);
|
||||
var configJson = ADManagedGroup.ValidConfigurationToJson(UserFlagUserDevicesManagedGroup.GetKey(userFlag), assignedUserDevicesLinkedGroup, filterBeginDate, updateDescription);
|
||||
|
||||
if (UserFlag.UserDevicesLinkedGroup != configJson)
|
||||
if (userFlag.UserDevicesLinkedGroup != configJson)
|
||||
{
|
||||
UserFlag.UserDevicesLinkedGroup = configJson;
|
||||
UserFlagService.Update(Database, UserFlag);
|
||||
userFlag.UserDevicesLinkedGroup = configJson;
|
||||
UserFlagService.Update(Database, userFlag);
|
||||
|
||||
if (UserFlag.UserDevicesLinkedGroup != null)
|
||||
if (userFlag.UserDevicesLinkedGroup != null)
|
||||
{
|
||||
// Sync Group
|
||||
if (UserFlagUserDevicesManagedGroup.TryGetManagedGroup(UserFlag, out var managedGroup))
|
||||
if (UserFlagUserDevicesManagedGroup.TryGetManagedGroup(userFlag, out var managedGroup))
|
||||
{
|
||||
return ADManagedGroupsSyncTask.ScheduleSync(managedGroup);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,12 @@ namespace Disco.Web.Areas.API.Models.Activation
|
||||
{
|
||||
public class CallbackModel
|
||||
{
|
||||
public string Origin { get; set; }
|
||||
public Guid DeploymentId { get; set; }
|
||||
public Guid CorrelationId { get; set; }
|
||||
[StringLength(50)]
|
||||
public string UserId { get; set; }
|
||||
public long Timestamp { get; set; }
|
||||
public string Proof { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using Disco.Web.Models.Shared;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Web.Areas.API.Models.System
|
||||
{
|
||||
public class DomainOrganisationalUnitsModel
|
||||
{
|
||||
public ADDomain Domain { get; set; }
|
||||
public List<ADOrganisationalUnit> OrganisationalUnits { get; set; }
|
||||
|
||||
public FancyTreeNode ToFancyTreeNode()
|
||||
{
|
||||
FancyTreeNode[] children = OrganisationalUnits.Select(ou => OrganisationalUnitToFancyTreeNode(ou)).ToArray();
|
||||
|
||||
return new FancyTreeNode()
|
||||
{
|
||||
key = Domain.DistinguishedName,
|
||||
title = Domain.NetBiosName,
|
||||
folder = true,
|
||||
tooltip = Domain.Name,
|
||||
children = children,
|
||||
unselectable = false,
|
||||
expanded = true
|
||||
};
|
||||
}
|
||||
private FancyTreeNode OrganisationalUnitToFancyTreeNode(ADOrganisationalUnit OrganisationalUnit)
|
||||
{
|
||||
FancyTreeNode[] children = OrganisationalUnit.Children == null
|
||||
? null
|
||||
: OrganisationalUnit.Children.Select(ou => OrganisationalUnitToFancyTreeNode(ou)).ToArray();
|
||||
|
||||
return new FancyTreeNode()
|
||||
{
|
||||
key = OrganisationalUnit.DistinguishedName,
|
||||
title = OrganisationalUnit.Name,
|
||||
folder = true,
|
||||
tooltip = OrganisationalUnit.DistinguishedName,
|
||||
children = children,
|
||||
unselectable = false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
@model Disco.Web.Areas.API.Models.Activation.CallbackModel
|
||||
@{
|
||||
Layout = null;
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
</head>
|
||||
<body data-deploymentid="@Model.DeploymentId" data-correlationid="@Model.CorrelationId" data-userid="@Model.UserId">
|
||||
<script>
|
||||
const deploymentId = document.body.dataset.deploymentid;
|
||||
const correlationId = document.body.dataset.correlationid;
|
||||
const userId = document.body.dataset.userid;
|
||||
window.parent.activateCallbackResponse(deploymentId, correlationId, userId);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,107 +0,0 @@
|
||||
#pragma warning disable 1591
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Disco.Web.Areas.API.Views.Activation
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Helpers;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Mvc.Ajax;
|
||||
using System.Web.Mvc.Html;
|
||||
using System.Web.Routing;
|
||||
using System.Web.Security;
|
||||
using System.Web.UI;
|
||||
using System.Web.WebPages;
|
||||
using Disco;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Web;
|
||||
using Disco.Web;
|
||||
using Disco.Web.Extensions;
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "2.0.0.0")]
|
||||
[System.Web.WebPages.PageVirtualPathAttribute("~/Areas/API/Views/Activation/_ActivateCallback.cshtml")]
|
||||
public partial class _ActivateCallback : Disco.Services.Web.WebViewPage<Disco.Web.Areas.API.Models.Activation.CallbackModel>
|
||||
{
|
||||
public _ActivateCallback()
|
||||
{
|
||||
}
|
||||
public override void Execute()
|
||||
{
|
||||
|
||||
#line 2 "..\..\Areas\API\Views\Activation\_ActivateCallback.cshtml"
|
||||
|
||||
Layout = null;
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\r\n<!DOCTYPE html>\r\n<html");
|
||||
|
||||
WriteLiteral(" lang=\"en\"");
|
||||
|
||||
WriteLiteral(">\r\n<head>\r\n</head>\r\n<body");
|
||||
|
||||
WriteLiteral(" data-deploymentid=\"");
|
||||
|
||||
|
||||
#line 9 "..\..\Areas\API\Views\Activation\_ActivateCallback.cshtml"
|
||||
Write(Model.DeploymentId);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(" data-correlationid=\"");
|
||||
|
||||
|
||||
#line 9 "..\..\Areas\API\Views\Activation\_ActivateCallback.cshtml"
|
||||
Write(Model.CorrelationId);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(" data-userid=\"");
|
||||
|
||||
|
||||
#line 9 "..\..\Areas\API\Views\Activation\_ActivateCallback.cshtml"
|
||||
Write(Model.UserId);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(@">
|
||||
<script>
|
||||
const deploymentId = document.body.dataset.deploymentid;
|
||||
const correlationId = document.body.dataset.correlationid;
|
||||
const userId = document.body.dataset.userid;
|
||||
window.parent.activateCallbackResponse(deploymentId, correlationId, userId);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
@@ -1,7 +1,13 @@
|
||||
using Disco.Models.UI.Config.Enrolment;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Devices.Enrolment;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Interop.DNS;
|
||||
using Disco.Services.Interop.VicEduDept;
|
||||
using Disco.Services.Plugins;
|
||||
using Disco.Services.Plugins.Features.UIExtension;
|
||||
using Disco.Services.Web;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
|
||||
@@ -12,10 +18,30 @@ namespace Disco.Web.Areas.Config.Controllers
|
||||
[DiscoAuthorize(Claims.Config.Enrolment.Show)]
|
||||
public virtual ActionResult Index()
|
||||
{
|
||||
var serverUrl = Request.Url;
|
||||
if ((serverUrl.HostNameType == UriHostNameType.Dns && serverUrl.Host.Equals("localhost", StringComparison.OrdinalIgnoreCase)) ||
|
||||
serverUrl.HostNameType == UriHostNameType.IPv4 || serverUrl.HostNameType == UriHostNameType.IPv6)
|
||||
{
|
||||
serverUrl = new UriBuilder(serverUrl)
|
||||
{
|
||||
Host = Environment.MachineName
|
||||
}.Uri;
|
||||
}
|
||||
|
||||
var srvRecord = DnsService.Query<SrvDnsRecord>(WindowsDeviceEnrolment.GetDnsServiceLocationRecordName(), true).FirstOrDefault();
|
||||
var srvValue = srvRecord == null ? null : (srvRecord.Port == 443 ? srvRecord.Target : $"{srvRecord.Target}:{srvRecord.Port}");
|
||||
|
||||
var m = new Models.Enrolment.IndexModel()
|
||||
{
|
||||
MacSshUsername = Database.DiscoConfiguration.Bootstrapper.MacSshUsername,
|
||||
PendingTimeoutMinutes = (int)Database.DiscoConfiguration.Bootstrapper.PendingTimeout.TotalMinutes,
|
||||
MacEnrolUrl = new Uri(serverUrl, Url.Action(MVC.Services.Client.Unauthenticated("MacSecureEnrol"))),
|
||||
HostingPluginInstalled = Plugins.PluginInstalled("Hosting"),
|
||||
IsServicesEducationVicGovAuDomain = ActiveDirectory.Context.PrimaryDomain.Name.Equals("services.education.vic.gov.au", StringComparison.OrdinalIgnoreCase),
|
||||
IsVicSmartDeployment = VicSmart.IsVicSmartDeployment(),
|
||||
DnsSrvRecordName = WindowsDeviceEnrolment.GetDnsServiceLocationRecordName(),
|
||||
DnsSrvRecordValue = srvValue,
|
||||
LegacyDiscoveryEnabled = !Database.DiscoConfiguration.Devices.EnrollmentLegacyDiscoveryDisabled,
|
||||
};
|
||||
|
||||
// UI Extensions
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Disco.Models.UI.Config.JobPreferences;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Jobs.JobQueues;
|
||||
using Disco.Services.Plugins.Features.UIExtension;
|
||||
using Disco.Services.Web;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
|
||||
namespace Disco.Web.Areas.Config.Controllers
|
||||
@@ -23,6 +25,9 @@ namespace Disco.Web.Areas.Config.Controllers
|
||||
OnCreateExpression = Database.DiscoConfiguration.JobPreferences.OnCreateExpression,
|
||||
OnDeviceReadyForReturnExpression = Database.DiscoConfiguration.JobPreferences.OnDeviceReadyForReturnExpression,
|
||||
OnCloseExpression = Database.DiscoConfiguration.JobPreferences.OnCloseExpression,
|
||||
DeviceProfiles = Database.DeviceProfiles.OrderBy(dp => dp.Name).ToList(),
|
||||
OrganisationAddresses = Database.DiscoConfiguration.OrganisationAddresses.Addresses.OrderBy(a => a.Name).ToList(),
|
||||
JobQueues = JobQueueService.GetQueues().Select(q => q.JobQueue).OrderBy(q => q.Name).ToList(),
|
||||
};
|
||||
|
||||
// UI Extensions
|
||||
|
||||
@@ -30,7 +30,9 @@ namespace Disco.Web.Areas.Config.Controllers
|
||||
DeploymentId = Guid.Parse(Database.DiscoConfiguration.DeploymentId),
|
||||
CorrelationId = Guid.NewGuid(),
|
||||
UserId = CurrentUser.UserId,
|
||||
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||
};
|
||||
model.Proof = service.CalculateCallbackProof(model.CorrelationId, model.UserId, model.Timestamp);
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Disco.Models.UI.Config.Enrolment;
|
||||
using System;
|
||||
|
||||
namespace Disco.Web.Areas.Config.Models.Enrolment
|
||||
{
|
||||
@@ -6,5 +7,12 @@ namespace Disco.Web.Areas.Config.Models.Enrolment
|
||||
{
|
||||
public string MacSshUsername { get; set; }
|
||||
public int PendingTimeoutMinutes { get; set; }
|
||||
public Uri MacEnrolUrl { get; set; }
|
||||
public bool HostingPluginInstalled { get; set; }
|
||||
public bool IsVicSmartDeployment { get; set; }
|
||||
public bool IsServicesEducationVicGovAuDomain { get; set; }
|
||||
public string DnsSrvRecordName { get; set; }
|
||||
public string DnsSrvRecordValue { get; set; }
|
||||
public bool LegacyDiscoveryEnabled { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Services.Jobs;
|
||||
using Disco.Models.Services.Jobs;
|
||||
using Disco.Models.UI.Config.JobPreferences;
|
||||
using Disco.Services.Extensions;
|
||||
using System;
|
||||
@@ -32,21 +31,9 @@ namespace Disco.Web.Areas.Config.Models.JobPreferences
|
||||
return UIHelpers.NoticeboardThemes.ToList();
|
||||
}
|
||||
|
||||
public Lazy<List<Disco.Models.Repository.DeviceProfile>> DeviceProfiles = new Lazy<List<Disco.Models.Repository.DeviceProfile>>(() =>
|
||||
{
|
||||
using (var database = new DiscoDataContext())
|
||||
{
|
||||
return database.DeviceProfiles.OrderBy(a => a.Description).ToList();
|
||||
}
|
||||
});
|
||||
|
||||
public Lazy<List<Disco.Models.BI.Config.OrganisationAddress>> OrganisationAddresses = new Lazy<List<Disco.Models.BI.Config.OrganisationAddress>>(() =>
|
||||
{
|
||||
using (var database = new DiscoDataContext())
|
||||
{
|
||||
return database.DiscoConfiguration.OrganisationAddresses.Addresses.OrderBy(a => a.Name).ToList();
|
||||
}
|
||||
});
|
||||
public List<Disco.Models.Repository.DeviceProfile> DeviceProfiles { get; set; }
|
||||
public List<Disco.Models.BI.Config.OrganisationAddress> OrganisationAddresses { get; set; }
|
||||
public List<Disco.Models.Repository.JobQueue> JobQueues { get; set; }
|
||||
|
||||
public List<KeyValuePair<int, string>> LongRunningJobDaysThresholdOptions()
|
||||
{
|
||||
|
||||
@@ -4,9 +4,11 @@ namespace Disco.Web.Areas.Config.Models.SystemConfig
|
||||
{
|
||||
public class ActivateModel
|
||||
{
|
||||
public Uri CallbackUrl { get; set; }
|
||||
public Guid DeploymentId { get; set; }
|
||||
public Guid CorrelationId { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public long Timestamp { get; set; }
|
||||
public string Proof { get; set; }
|
||||
public Uri CallbackUrl { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -620,7 +620,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<button id="changeOrganisationalUnit" type="button" class="button small">Change</button>@AjaxHelpers.AjaxLoader()
|
||||
<div id="dialogOrganisationalUnit" title="Organisational Unit" class="dialog" data-url="@(Url.Action(MVC.API.System.DomainOrganisationalUnits()))">
|
||||
<div id="dialogOrganisationalUnit" title="Organisational Unit" class="dialog" data-url="@(Url.Action(MVC.API.System.DomainOrganisationalUnitTree()))" data-urllazy="@(Url.Action(MVC.API.System.DomainOrganisationalUnits()))">
|
||||
<div id="dialogOrganisationalUnit_Loading">
|
||||
@AjaxHelpers.AjaxLoader() Loading Organisational Units
|
||||
</div>
|
||||
@@ -690,6 +690,7 @@
|
||||
$enforceCheckbox = $('#enforceOrganisationalUnit');
|
||||
|
||||
const body = new FormData();
|
||||
body.append('expandNode', ouValue);
|
||||
body.append('__RequestVerificationToken', document.body.dataset.antiforgery);
|
||||
const response = await fetch($dialog.attr('data-url'), {
|
||||
method: 'POST',
|
||||
@@ -698,17 +699,23 @@
|
||||
const data = await response.json();
|
||||
$loading.hide();
|
||||
|
||||
// Make 'Domains' unselectable
|
||||
$.each(data, function (i, node) {
|
||||
node.unselectable = true;
|
||||
});
|
||||
|
||||
ouTree = $ouTree.fancytree({
|
||||
source: data,
|
||||
checkbox: false,
|
||||
selectMode: 1,
|
||||
keyboard: false,
|
||||
fx: null
|
||||
fx: null,
|
||||
lazyload: function (event, data) {
|
||||
data.result = {
|
||||
url: $dialog.attr('data-urllazy'),
|
||||
method: 'POST',
|
||||
cache: false,
|
||||
data: {
|
||||
node: data.node.key,
|
||||
'__RequestVerificationToken': document.body.dataset.antiforgery
|
||||
}
|
||||
};
|
||||
}
|
||||
}).fancytree('getTree');
|
||||
|
||||
ouTree.$container.css('position', 'relative');
|
||||
|
||||
@@ -2041,7 +2041,18 @@ WriteLiteral(" data-url=\"");
|
||||
|
||||
|
||||
#line 623 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Url.Action(MVC.API.System.DomainOrganisationalUnits()));
|
||||
Write(Url.Action(MVC.API.System.DomainOrganisationalUnitTree()));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(" data-urllazy=\"");
|
||||
|
||||
|
||||
#line 623 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Url.Action(MVC.API.System.DomainOrganisationalUnits()));
|
||||
|
||||
|
||||
#line default
|
||||
@@ -2181,52 +2192,59 @@ WriteLiteral(">\r\n $(function () {\r\n
|
||||
" = $(\'#treeOrganisationalUnit\');\r\n $dialog.cs" +
|
||||
"s(\'overflow\', \'visible\');\r\n\r\n $enforceCheckbo" +
|
||||
"x = $(\'#enforceOrganisationalUnit\');\r\n\r\n cons" +
|
||||
"t body = new FormData();\r\n body.append(\'__Req" +
|
||||
"uestVerificationToken\', document.body.dataset.antiforgery);\r\n " +
|
||||
" const response = await fetch($dialog.attr(\'data-url\'), {\r\n " +
|
||||
" method: \'POST\',\r\n " +
|
||||
" body: body\r\n });\r\n " +
|
||||
" const data = await response.json();\r\n " +
|
||||
" $loading.hide();\r\n\r\n // Make \'D" +
|
||||
"omains\' unselectable\r\n $.each(data, function " +
|
||||
"(i, node) {\r\n node.unselectable = true;\r\n" +
|
||||
" });\r\n\r\n o" +
|
||||
"uTree = $ouTree.fancytree({\r\n source: dat" +
|
||||
"a,\r\n checkbox: false,\r\n " +
|
||||
" selectMode: 1,\r\n ke" +
|
||||
"yboard: false,\r\n fx: null\r\n " +
|
||||
" }).fancytree(\'getTree\');\r\n\r\n " +
|
||||
" ouTree.$container.css(\'position\', \'relative\');\r\n\r\n " +
|
||||
" // Set Buttons\r\n $dialog.dialog(\'" +
|
||||
"option\', \'buttons\', {\r\n \'Use Default Comp" +
|
||||
"uters Container\': function () {\r\n var" +
|
||||
" $this = $(this);\r\n $this.css(\'overfl" +
|
||||
"ow\', \'hidden\');\r\n $this.dialog(\"disab" +
|
||||
"le\");\r\n $this.dialog(\"option\", \"butto" +
|
||||
"ns\", null);\r\n ouSet(\'\');\r\n " +
|
||||
" },\r\n \'Save\':" +
|
||||
" function () {\r\n var node = ouTree.ge" +
|
||||
"tActiveNode();\r\n if (node && node.key" +
|
||||
".substr(0, 3).toLowerCase() == \'ou=\') {\r\n " +
|
||||
" var $this = $(this);\r\n $" +
|
||||
"this.css(\'overflow\', \'hidden\');\r\n " +
|
||||
" $this.dialog(\"disable\");\r\n $this" +
|
||||
".dialog(\"option\", \"buttons\", null);\r\n " +
|
||||
" ouSet(node.key);\r\n } else {\r\n " +
|
||||
" alert(\'Select an Organisational Uni" +
|
||||
"t to Save\')\r\n }\r\n " +
|
||||
" }\r\n });\r\n\r\n " +
|
||||
" // Expand\r\n expandAndFo" +
|
||||
"cusNode(ouValue);\r\n\r\n ouTree.options.fx = { h" +
|
||||
"eight: \"toggle\", duration: 200 };\r\n }\r\n " +
|
||||
" $dialog.dialog(\'open\');\r\n\r\n " +
|
||||
" $enforceCheckbox.prop(\'checked\', $(\'#DeviceProfile_EnforceOrganisationalUnit\')." +
|
||||
"prop(\'checked\'));\r\n };\r\n\r\n " +
|
||||
" $(\'#changeOrganisationalUnit\').click(ouChange);\r\n });\r\n " +
|
||||
" </script>\r\n");
|
||||
"t body = new FormData();\r\n body.append(\'expan" +
|
||||
"dNode\', ouValue);\r\n body.append(\'__RequestVer" +
|
||||
"ificationToken\', document.body.dataset.antiforgery);\r\n " +
|
||||
" const response = await fetch($dialog.attr(\'data-url\'), {\r\n " +
|
||||
" method: \'POST\',\r\n " +
|
||||
" body: body\r\n });\r\n " +
|
||||
" const data = await response.json();\r\n " +
|
||||
" $loading.hide();\r\n\r\n ouTree = $ouTree." +
|
||||
"fancytree({\r\n source: data,\r\n " +
|
||||
" checkbox: false,\r\n " +
|
||||
" selectMode: 1,\r\n keyboard: false,\r\n" +
|
||||
" fx: null,\r\n " +
|
||||
" lazyload: function (event, data) {\r\n " +
|
||||
" data.result = {\r\n url:" +
|
||||
" $dialog.attr(\'data-urllazy\'),\r\n " +
|
||||
"method: \'POST\',\r\n cache: false,\r\n" +
|
||||
" data: {\r\n " +
|
||||
" node: data.node.key,\r\n " +
|
||||
" \'__RequestVerificationToken\': document.body.dataset.antif" +
|
||||
"orgery\r\n }\r\n " +
|
||||
" };\r\n }\r\n " +
|
||||
" }).fancytree(\'getTree\');\r\n\r\n " +
|
||||
" ouTree.$container.css(\'position\', \'relative\');\r\n\r\n " +
|
||||
" // Set Buttons\r\n $dialog.dial" +
|
||||
"og(\'option\', \'buttons\', {\r\n \'Use Default " +
|
||||
"Computers Container\': function () {\r\n " +
|
||||
" var $this = $(this);\r\n $this.css(\'ov" +
|
||||
"erflow\', \'hidden\');\r\n $this.dialog(\"d" +
|
||||
"isable\");\r\n $this.dialog(\"option\", \"b" +
|
||||
"uttons\", null);\r\n ouSet(\'\');\r\n " +
|
||||
" },\r\n \'Sa" +
|
||||
"ve\': function () {\r\n var node = ouTre" +
|
||||
"e.getActiveNode();\r\n if (node && node" +
|
||||
".key.substr(0, 3).toLowerCase() == \'ou=\') {\r\n " +
|
||||
" var $this = $(this);\r\n " +
|
||||
" $this.css(\'overflow\', \'hidden\');\r\n " +
|
||||
" $this.dialog(\"disable\");\r\n $" +
|
||||
"this.dialog(\"option\", \"buttons\", null);\r\n " +
|
||||
" ouSet(node.key);\r\n } else {\r" +
|
||||
"\n alert(\'Select an Organisational" +
|
||||
" Unit to Save\')\r\n }\r\n " +
|
||||
" }\r\n });\r\n\r\n " +
|
||||
" // Expand\r\n expandA" +
|
||||
"ndFocusNode(ouValue);\r\n\r\n ouTree.options.fx =" +
|
||||
" { height: \"toggle\", duration: 200 };\r\n }\r\n " +
|
||||
" $dialog.dialog(\'open\');\r\n\r\n " +
|
||||
" $enforceCheckbox.prop(\'checked\', $(\'#DeviceProfile_EnforceOrganisationalUni" +
|
||||
"t\').prop(\'checked\'));\r\n };\r\n\r\n " +
|
||||
" $(\'#changeOrganisationalUnit\').click(ouChange);\r\n })" +
|
||||
";\r\n </script>\r\n");
|
||||
|
||||
|
||||
#line 752 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 759 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2245,7 +2263,7 @@ WriteLiteral(">\r\n <span>\r\n");
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 757 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 764 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Model.FriendlyOrganisationalUnitName);
|
||||
|
||||
|
||||
@@ -2254,7 +2272,7 @@ WriteLiteral(" ");
|
||||
WriteLiteral("\r\n </span>\r\n </div>\r\n");
|
||||
|
||||
|
||||
#line 760 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 767 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -2263,7 +2281,7 @@ WriteLiteral("\r\n </span>\r\n </div>\
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 761 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 768 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (!Model.OrganisationalUnitExists)
|
||||
{
|
||||
|
||||
@@ -2286,7 +2304,7 @@ WriteLiteral("></i>The Organisational Unit could not be found.\r\n
|
||||
" </div>\r\n");
|
||||
|
||||
|
||||
#line 768 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 775 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -2299,13 +2317,13 @@ WriteLiteral(" style=\"margin-top: 8px;\"");
|
||||
WriteLiteral(">\r\n");
|
||||
|
||||
|
||||
#line 770 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 777 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 770 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 777 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (canConfig)
|
||||
{
|
||||
|
||||
@@ -2321,7 +2339,7 @@ WriteLiteral(" type=\"checkbox\"");
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 772 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 779 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Model.DeviceProfile.EnforceOrganisationalUnit ? new MvcHtmlString("checked=\"checked\" ") : new MvcHtmlString(string.Empty));
|
||||
|
||||
|
||||
@@ -2341,7 +2359,7 @@ WriteLiteral(@">
|
||||
'");
|
||||
|
||||
|
||||
#line 778 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 785 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Url.Action(MVC.API.DeviceProfile.UpdateEnforceOrganisationalUnit(Model.DeviceProfile.Id)));
|
||||
|
||||
|
||||
@@ -2352,7 +2370,7 @@ WriteLiteral("\',\r\n \'EnforceOrganisational
|
||||
" </script>\r\n");
|
||||
|
||||
|
||||
#line 783 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 790 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2369,7 +2387,7 @@ WriteLiteral(" type=\"checkbox\"");
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 786 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 793 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Model.DeviceProfile.EnforceOrganisationalUnit ? new MvcHtmlString("checked=\"checked\" ") : new MvcHtmlString(string.Empty));
|
||||
|
||||
|
||||
@@ -2378,7 +2396,7 @@ WriteLiteral(" ");
|
||||
WriteLiteral(" disabled=\"disabled\" />\r\n");
|
||||
|
||||
|
||||
#line 787 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 794 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -2394,7 +2412,7 @@ WriteLiteral(">\r\n Enforce Organisational Unit\r\n
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 791 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 798 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(AjaxHelpers.AjaxLoader());
|
||||
|
||||
|
||||
@@ -2425,13 +2443,13 @@ WriteLiteral(@"></i>When an Active Directory account is provisioned it will be p
|
||||
");
|
||||
|
||||
|
||||
#line 805 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 812 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 805 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 812 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (canConfig && (Model.CertificateProviders.Count > 0 || Model.CertificateAuthorityProviders.Count > 0))
|
||||
{
|
||||
|
||||
@@ -2441,20 +2459,20 @@ WriteLiteral(@"></i>When an Active Directory account is provisioned it will be p
|
||||
WriteLiteral(" <br />\r\n");
|
||||
|
||||
|
||||
#line 808 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 815 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 808 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 815 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(AjaxHelpers.AjaxLoader("DeviceProfile_CertificateProviders"));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 808 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 815 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
}
|
||||
|
||||
@@ -2464,7 +2482,7 @@ WriteLiteral(" <br />\r\n");
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 810 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 817 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (canConfig && Model.CertificateProviders.Count > 0)
|
||||
{
|
||||
|
||||
@@ -2489,7 +2507,7 @@ WriteLiteral(@">
|
||||
fetch('");
|
||||
|
||||
|
||||
#line 823 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 830 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Url.Action(MVC.API.DeviceProfile.UpdateCertificateProviders(Model.DeviceProfile.Id)));
|
||||
|
||||
|
||||
@@ -2515,7 +2533,7 @@ WriteLiteral(@"', {
|
||||
");
|
||||
|
||||
|
||||
#line 840 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 847 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -2524,7 +2542,7 @@ WriteLiteral(@"', {
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 841 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 848 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (canConfig && Model.CertificateProviders.Count > 0)
|
||||
{
|
||||
|
||||
@@ -2549,7 +2567,7 @@ WriteLiteral(@">
|
||||
fetch('");
|
||||
|
||||
|
||||
#line 854 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 861 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Url.Action(MVC.API.DeviceProfile.UpdateCertificateAuthorityProviders(Model.DeviceProfile.Id)));
|
||||
|
||||
|
||||
@@ -2575,7 +2593,7 @@ WriteLiteral(@"', {
|
||||
");
|
||||
|
||||
|
||||
#line 871 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 878 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -2585,13 +2603,13 @@ WriteLiteral(" </th>\r\n <td>\r\n <h4>Devic
|
||||
"tes</h4>\r\n");
|
||||
|
||||
|
||||
#line 875 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 882 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 875 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 882 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (canConfig && Model.CertificateProviders.Count > 0)
|
||||
{
|
||||
|
||||
@@ -2599,14 +2617,14 @@ WriteLiteral(" </th>\r\n <td>\r\n <h4>Devic
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 877 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 884 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(CommonHelpers.CheckBoxList("DeviceProfile_CertificateProviders", "DeviceProfile_CertificateProviders", Model.CertificateProviders.ToSelectListItems(Model.DeviceProfile.GetCertificateProviders())));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 877 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 884 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
}
|
||||
else
|
||||
@@ -2626,7 +2644,7 @@ WriteLiteral(" class=\"smallMessage\"");
|
||||
WriteLiteral("><None Allocated></span>\r\n");
|
||||
|
||||
|
||||
#line 886 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 893 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2637,13 +2655,13 @@ WriteLiteral("><None Allocated></span>\r\n");
|
||||
WriteLiteral(" <ul>\r\n");
|
||||
|
||||
|
||||
#line 890 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 897 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 890 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 897 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
foreach (var certificateProvider in certificateProviders)
|
||||
{
|
||||
|
||||
@@ -2653,7 +2671,7 @@ WriteLiteral(" <ul>\r\n");
|
||||
WriteLiteral(" <li>");
|
||||
|
||||
|
||||
#line 892 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 899 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(certificateProvider.Name);
|
||||
|
||||
|
||||
@@ -2662,7 +2680,7 @@ WriteLiteral(" <li>");
|
||||
WriteLiteral("</li>\r\n");
|
||||
|
||||
|
||||
#line 893 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 900 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -2671,7 +2689,7 @@ WriteLiteral("</li>\r\n");
|
||||
WriteLiteral(" </ul>\r\n");
|
||||
|
||||
|
||||
#line 895 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 902 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2685,13 +2703,13 @@ WriteLiteral(" style=\"margin-top: 4px;\"");
|
||||
WriteLiteral(">Authority Certificates</h4>\r\n");
|
||||
|
||||
|
||||
#line 898 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 905 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 898 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 905 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (canConfig && Model.CertificateAuthorityProviders.Count > 0)
|
||||
{
|
||||
|
||||
@@ -2699,14 +2717,14 @@ WriteLiteral(">Authority Certificates</h4>\r\n");
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 900 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 907 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(CommonHelpers.CheckBoxList("DeviceProfile_CertificateAuthorityProviders", "DeviceProfile_CertificateAuthorityProviders", Model.CertificateAuthorityProviders.ToSelectListItems(Model.DeviceProfile.GetCertificateAuthorityProviders())));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 900 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 907 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
}
|
||||
else
|
||||
@@ -2726,7 +2744,7 @@ WriteLiteral(" class=\"smallMessage\"");
|
||||
WriteLiteral("><None Allocated></span>\r\n");
|
||||
|
||||
|
||||
#line 909 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 916 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2737,13 +2755,13 @@ WriteLiteral("><None Allocated></span>\r\n");
|
||||
WriteLiteral(" <ul>\r\n");
|
||||
|
||||
|
||||
#line 913 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 920 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 913 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 920 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
foreach (var certificateProvider in certificateProviders)
|
||||
{
|
||||
|
||||
@@ -2753,7 +2771,7 @@ WriteLiteral(" <ul>\r\n");
|
||||
WriteLiteral(" <li>");
|
||||
|
||||
|
||||
#line 915 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 922 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(certificateProvider.Name);
|
||||
|
||||
|
||||
@@ -2762,7 +2780,7 @@ WriteLiteral(" <li>");
|
||||
WriteLiteral("</li>\r\n");
|
||||
|
||||
|
||||
#line 916 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 923 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -2771,7 +2789,7 @@ WriteLiteral("</li>\r\n");
|
||||
WriteLiteral(" </ul>\r\n");
|
||||
|
||||
|
||||
#line 918 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 925 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2781,7 +2799,7 @@ WriteLiteral(" </ul>\r\n");
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 920 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 927 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (canViewPlugins)
|
||||
{
|
||||
|
||||
@@ -2802,21 +2820,21 @@ WriteLiteral(" class=\"fa fa-info-circle\"");
|
||||
|
||||
WriteLiteral("></i>View the <a");
|
||||
|
||||
WriteAttribute("href", Tuple.Create(" href=\"", 53296), Tuple.Create("\"", 53346)
|
||||
WriteAttribute("href", Tuple.Create(" href=\"", 53936), Tuple.Create("\"", 53986)
|
||||
|
||||
#line 924 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 53303), Tuple.Create<System.Object, System.Int32>(Url.Action(MVC.Config.Plugins.Install())
|
||||
#line 931 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 53943), Tuple.Create<System.Object, System.Int32>(Url.Action(MVC.Config.Plugins.Install())
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 53303), false)
|
||||
, 53943), false)
|
||||
);
|
||||
|
||||
WriteLiteral(">Plugin Catalogue</a> to discover and install certificate provider plugins.\r\n " +
|
||||
" </p>\r\n </div>\r\n");
|
||||
|
||||
|
||||
#line 927 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 934 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -2826,13 +2844,13 @@ WriteLiteral(" </td>\r\n </tr>\r\n <tr>\r\n
|
||||
" Provision Wireless Profiles:\r\n");
|
||||
|
||||
|
||||
#line 933 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 940 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 933 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 940 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (canConfig && Model.WirelessProfileProviders.Count > 0)
|
||||
{
|
||||
|
||||
@@ -2842,20 +2860,20 @@ WriteLiteral(" </td>\r\n </tr>\r\n <tr>\r\n
|
||||
WriteLiteral(" <br />\r\n");
|
||||
|
||||
|
||||
#line 936 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 943 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 936 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 943 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(AjaxHelpers.AjaxLoader("DeviceProfile_WirelessProfileProviders"));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 936 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 943 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
|
||||
@@ -2879,7 +2897,7 @@ WriteLiteral(@">
|
||||
fetch('");
|
||||
|
||||
|
||||
#line 948 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 955 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Url.Action(MVC.API.DeviceProfile.UpdateWirelessProfileProviders(Model.DeviceProfile.Id)));
|
||||
|
||||
|
||||
@@ -2905,7 +2923,7 @@ WriteLiteral(@"', {
|
||||
");
|
||||
|
||||
|
||||
#line 965 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 972 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -2914,13 +2932,13 @@ WriteLiteral(@"', {
|
||||
WriteLiteral(" </th>\r\n <td>\r\n");
|
||||
|
||||
|
||||
#line 968 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 975 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 968 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 975 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (canConfig && Model.WirelessProfileProviders.Count > 0)
|
||||
{
|
||||
|
||||
@@ -2928,14 +2946,14 @@ WriteLiteral(" </th>\r\n <td>\r\n");
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 970 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 977 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(CommonHelpers.CheckBoxList("DeviceProfile_WirelessProfileProviders", "DeviceProfile_WirelessProfileProviders", Model.WirelessProfileProviders.ToSelectListItems(Model.DeviceProfile.GetWirelessProfileProviders())));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 970 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 977 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
}
|
||||
else
|
||||
@@ -2955,7 +2973,7 @@ WriteLiteral(" class=\"smallMessage\"");
|
||||
WriteLiteral("><None Allocated></span>\r\n");
|
||||
|
||||
|
||||
#line 979 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 986 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2966,13 +2984,13 @@ WriteLiteral("><None Allocated></span>\r\n");
|
||||
WriteLiteral(" <ul>\r\n");
|
||||
|
||||
|
||||
#line 983 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 990 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 983 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 990 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
foreach (var wirelessProfileProvider in wirelessProfileProviders)
|
||||
{
|
||||
|
||||
@@ -2982,7 +3000,7 @@ WriteLiteral(" <ul>\r\n");
|
||||
WriteLiteral(" <li>");
|
||||
|
||||
|
||||
#line 985 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 992 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(wirelessProfileProvider.Name);
|
||||
|
||||
|
||||
@@ -2991,7 +3009,7 @@ WriteLiteral(" <li>");
|
||||
WriteLiteral("</li>\r\n");
|
||||
|
||||
|
||||
#line 986 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 993 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -3000,7 +3018,7 @@ WriteLiteral("</li>\r\n");
|
||||
WriteLiteral(" </ul>\r\n");
|
||||
|
||||
|
||||
#line 988 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 995 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3010,7 +3028,7 @@ WriteLiteral(" </ul>\r\n");
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 990 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 997 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (canViewPlugins)
|
||||
{
|
||||
|
||||
@@ -3031,21 +3049,21 @@ WriteLiteral(" class=\"fa fa-info-circle\"");
|
||||
|
||||
WriteLiteral("></i>View the <a");
|
||||
|
||||
WriteAttribute("href", Tuple.Create(" href=\"", 56954), Tuple.Create("\"", 57004)
|
||||
WriteAttribute("href", Tuple.Create(" href=\"", 57594), Tuple.Create("\"", 57644)
|
||||
|
||||
#line 994 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 56961), Tuple.Create<System.Object, System.Int32>(Url.Action(MVC.Config.Plugins.Install())
|
||||
#line 1001 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 57601), Tuple.Create<System.Object, System.Int32>(Url.Action(MVC.Config.Plugins.Install())
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 56961), false)
|
||||
, 57601), false)
|
||||
);
|
||||
|
||||
WriteLiteral(">Plugin Catalogue</a> to discover and install wireless profile provider plugins.\r" +
|
||||
"\n </p>\r\n </div>\r\n");
|
||||
|
||||
|
||||
#line 997 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1004 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -3054,13 +3072,13 @@ WriteLiteral(">Plugin Catalogue</a> to discover and install wireless profile pro
|
||||
WriteLiteral(" </td>\r\n </tr>\r\n");
|
||||
|
||||
|
||||
#line 1000 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1007 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1000 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1007 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (hideAdvanced)
|
||||
{
|
||||
|
||||
@@ -3094,7 +3112,7 @@ WriteLiteral(@">Show Advanced Options</button>
|
||||
");
|
||||
|
||||
|
||||
#line 1016 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1023 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -3110,7 +3128,7 @@ WriteLiteral(">\r\n <th>\r\n Linked Groups:\r\n
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 1023 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1030 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Html.Partial(MVC.Config.Shared.Views.LinkedGroupInstance, new LinkedGroupModel()
|
||||
{
|
||||
CanConfigure = canConfig,
|
||||
@@ -3128,7 +3146,7 @@ WriteLiteral("\r\n");
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 1031 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1038 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Html.Partial(MVC.Config.Shared.Views.LinkedGroupInstance, new LinkedGroupModel()
|
||||
{
|
||||
CanConfigure = canConfig,
|
||||
@@ -3144,13 +3162,13 @@ WriteLiteral(" ");
|
||||
WriteLiteral("\r\n");
|
||||
|
||||
|
||||
#line 1039 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1046 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1039 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1046 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (canConfig)
|
||||
{
|
||||
|
||||
@@ -3158,14 +3176,14 @@ WriteLiteral("\r\n");
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1041 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1048 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Html.Partial(MVC.Config.Shared.Views.LinkedGroupShared));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1041 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1048 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
}
|
||||
|
||||
@@ -3175,7 +3193,7 @@ WriteLiteral("\r\n");
|
||||
WriteLiteral(" </div>\r\n </td>\r\n </tr>\r\n </table>\r\n</div>\r\n");
|
||||
|
||||
|
||||
#line 1048 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1055 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Html.Partial(MVC.Config.Shared.Views._DeviceGroupDocumentBulkGenerate, Model));
|
||||
|
||||
|
||||
@@ -3188,13 +3206,13 @@ WriteLiteral(" class=\"actionBar\"");
|
||||
WriteLiteral(">\r\n");
|
||||
|
||||
|
||||
#line 1050 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1057 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1050 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1057 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (Model.CanDecommission)
|
||||
{
|
||||
|
||||
@@ -3220,13 +3238,13 @@ WriteLiteral(" title=\"Profile Device Decommission\"");
|
||||
WriteLiteral(">\r\n");
|
||||
|
||||
|
||||
#line 1054 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1061 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1054 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1061 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
using (Html.BeginForm(MVC.API.Device.DeviceProfileDecommission(Model.DeviceProfile.Id)))
|
||||
{
|
||||
|
||||
@@ -3234,14 +3252,14 @@ WriteLiteral(">\r\n");
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1056 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1063 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Html.AntiForgeryToken());
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1056 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1063 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
|
||||
@@ -3266,13 +3284,13 @@ WriteLiteral(" class=\"none\"");
|
||||
WriteLiteral(">\r\n");
|
||||
|
||||
|
||||
#line 1062 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1069 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1062 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1069 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
foreach (DecommissionReasons decommissionReason in Enum.GetValues(typeof(DecommissionReasons)).Cast<DecommissionReasons>().OrderBy(r => r.ToString()))
|
||||
{
|
||||
|
||||
@@ -3283,33 +3301,33 @@ WriteLiteral(" <li>\r\n
|
||||
|
||||
WriteLiteral(" type=\"radio\"");
|
||||
|
||||
WriteAttribute("id", Tuple.Create(" id=\"", 60656), Tuple.Create("\"", 60728)
|
||||
, Tuple.Create(Tuple.Create("", 60661), Tuple.Create("DeviceProfile_Decommission_Dialog_Reason_", 60661), true)
|
||||
WriteAttribute("id", Tuple.Create(" id=\"", 61296), Tuple.Create("\"", 61368)
|
||||
, Tuple.Create(Tuple.Create("", 61301), Tuple.Create("DeviceProfile_Decommission_Dialog_Reason_", 61301), true)
|
||||
|
||||
#line 1065 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 60702), Tuple.Create<System.Object, System.Int32>((int)decommissionReason
|
||||
#line 1072 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 61342), Tuple.Create<System.Object, System.Int32>((int)decommissionReason
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 60702), false)
|
||||
, 61342), false)
|
||||
);
|
||||
|
||||
WriteLiteral("\r\n name=\"decommissionReason\"");
|
||||
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 60795), Tuple.Create("\"", 60829)
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 61435), Tuple.Create("\"", 61469)
|
||||
|
||||
#line 1066 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 60803), Tuple.Create<System.Object, System.Int32>((int)decommissionReason
|
||||
#line 1073 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 61443), Tuple.Create<System.Object, System.Int32>((int)decommissionReason
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 60803), false)
|
||||
, 61443), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 1066 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1073 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write((decommissionReason == DecommissionReasons.EndOfLife) ? "checked=\"checked\"" : string.Empty);
|
||||
|
||||
|
||||
@@ -3317,21 +3335,21 @@ WriteLiteral(" ");
|
||||
#line hidden
|
||||
WriteLiteral(" />\r\n <label");
|
||||
|
||||
WriteAttribute("for", Tuple.Create(" for=\"", 60969), Tuple.Create("\"", 61042)
|
||||
, Tuple.Create(Tuple.Create("", 60975), Tuple.Create("DeviceProfile_Decommission_Dialog_Reason_", 60975), true)
|
||||
WriteAttribute("for", Tuple.Create(" for=\"", 61609), Tuple.Create("\"", 61682)
|
||||
, Tuple.Create(Tuple.Create("", 61615), Tuple.Create("DeviceProfile_Decommission_Dialog_Reason_", 61615), true)
|
||||
|
||||
#line 1067 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 61016), Tuple.Create<System.Object, System.Int32>((int)decommissionReason
|
||||
#line 1074 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 61656), Tuple.Create<System.Object, System.Int32>((int)decommissionReason
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 61016), false)
|
||||
, 61656), false)
|
||||
);
|
||||
|
||||
WriteLiteral(">");
|
||||
|
||||
|
||||
#line 1067 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1074 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(decommissionReason.ReasonMessage());
|
||||
|
||||
|
||||
@@ -3340,7 +3358,7 @@ WriteLiteral(">");
|
||||
WriteLiteral("</label>\r\n </li>\r\n");
|
||||
|
||||
|
||||
#line 1069 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1076 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -3359,7 +3377,7 @@ WriteLiteral(" />\r\n Unassign devices users\r\n
|
||||
"\r\n </div>\r\n");
|
||||
|
||||
|
||||
#line 1077 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1084 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -3403,7 +3421,7 @@ WriteLiteral(@">
|
||||
");
|
||||
|
||||
|
||||
#line 1108 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1115 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -3412,7 +3430,7 @@ WriteLiteral(@">
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 1109 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1116 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (canDelete)
|
||||
{
|
||||
|
||||
@@ -3440,13 +3458,13 @@ WriteLiteral(" title=\"Delete this Device Profile?\"");
|
||||
WriteLiteral(">\r\n");
|
||||
|
||||
|
||||
#line 1113 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1120 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1113 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1120 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
using (Html.BeginForm(MVC.API.DeviceProfile.Delete(Model.DeviceProfile.Id, true)))
|
||||
{
|
||||
|
||||
@@ -3454,14 +3472,14 @@ WriteLiteral(">\r\n");
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1115 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1122 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Html.AntiForgeryToken());
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1115 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1122 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
}
|
||||
|
||||
@@ -3509,7 +3527,7 @@ WriteLiteral(@">
|
||||
");
|
||||
|
||||
|
||||
#line 1149 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1156 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -3518,7 +3536,7 @@ WriteLiteral(@">
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 1150 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1157 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (Authorization.Has(Claims.Device.Actions.Export))
|
||||
{
|
||||
|
||||
@@ -3526,14 +3544,14 @@ WriteLiteral(" ");
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1152 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1159 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Html.ActionLinkButton("Export Devices", MVC.Device.Export(null, Disco.Models.Services.Devices.DeviceExportTypes.Profile, Model.DeviceProfile.Id)));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1152 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1159 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
}
|
||||
|
||||
@@ -3543,7 +3561,7 @@ WriteLiteral(" ");
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 1154 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1161 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
if (Authorization.Has(Claims.Device.Search) && Model.DeviceCount > 0)
|
||||
{
|
||||
|
||||
@@ -3551,14 +3569,14 @@ WriteLiteral(" ");
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1156 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1163 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
Write(Html.ActionLinkButton(string.Format("View {0} Device{1}", Model.DeviceCount, (Model.DeviceCount != 1 ? "s" : null)), MVC.Search.Query(Model.DeviceProfile.Id.ToString(), "DeviceProfile")));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1156 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
#line 1163 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml"
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,26 +16,25 @@
|
||||
Model.TemplateExpressions.All(e => e.All(p => !p.ParseError) &&
|
||||
!Model.OnImportUserFlagRules.Any());
|
||||
|
||||
#region Can Bulk Generate
|
||||
var canBulkGenerate = Authorization.Has(Claims.Config.DocumentTemplate.BulkGenerate);
|
||||
if (canBulkGenerate)
|
||||
var canBulkGenerate = false;
|
||||
var canBulkDownload = false;
|
||||
switch (Model.DocumentTemplate.Scope)
|
||||
{
|
||||
switch (Model.DocumentTemplate.Scope)
|
||||
{
|
||||
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||
canBulkGenerate = Authorization.Has(Claims.Device.Actions.GenerateDocuments);
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||
canBulkGenerate = Authorization.Has(Claims.Job.Actions.GenerateDocuments);
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||
canBulkGenerate = Authorization.Has(Claims.User.Actions.GenerateDocuments);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException("Invalid DocumentType Scope");
|
||||
}
|
||||
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||
canBulkGenerate = Authorization.Has(Claims.Config.DocumentTemplate.BulkGenerate) && Authorization.Has(Claims.Device.Actions.GenerateDocuments);
|
||||
canBulkDownload = Authorization.Has(Claims.Device.ShowAttachments) && Model.StoredInstanceCount > 0;
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||
canBulkGenerate = Authorization.Has(Claims.Config.DocumentTemplate.BulkGenerate) && Authorization.Has(Claims.Job.Actions.GenerateDocuments);
|
||||
canBulkDownload = Authorization.Has(Claims.Job.ShowAttachments) && Model.StoredInstanceCount > 0;
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||
canBulkGenerate = Authorization.Has(Claims.Config.DocumentTemplate.BulkGenerate) && Authorization.Has(Claims.User.Actions.GenerateDocuments);
|
||||
canBulkDownload = Authorization.Has(Claims.User.ShowAttachments) && Model.StoredInstanceCount > 0;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException("Invalid DocumentType Scope");
|
||||
}
|
||||
#endregion
|
||||
|
||||
ViewBag.Title = Html.ToBreadcrumb("Configuration", MVC.Config.Config.Index(), "Document Templates", MVC.Config.DocumentTemplate.Index(null), Model.DocumentTemplate.Description);
|
||||
|
||||
@@ -1037,6 +1036,66 @@
|
||||
{
|
||||
@Html.ActionLinkButton("Export Instances", MVC.Config.DocumentTemplate.Export(Model.DocumentTemplate.Id, null))
|
||||
}
|
||||
@if (canBulkDownload)
|
||||
{
|
||||
<button id="dialogBulkDownloadButton" type="button" class="button">Download Instances</button>
|
||||
<div id="dialogBulkDownload" class="dialog" title="Download Instances: @(Model.DocumentTemplate.Id)">
|
||||
@using (Html.BeginForm(MVC.API.DocumentTemplate.BulkDownload(Model.DocumentTemplate.Id)))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<h3>Scope</h3>
|
||||
<ul class="none">
|
||||
<li>
|
||||
<label><input type="radio" name="latestOnly" value="True" checked /> Latest @Model.DocumentTemplate.Scope Attachment</label>
|
||||
</li>
|
||||
<li>
|
||||
<label><input type="radio" name="latestOnly" value="False" /> All @Model.DocumentTemplate.Scope Attachments</label>
|
||||
</li>
|
||||
</ul>
|
||||
<br />
|
||||
<h3>Threshold</h3>
|
||||
<div>
|
||||
<label>Only On or After <input type="date" name="threshold" value="@DateTime.Now.ToString("yyyy")-01-01" /></label>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
let dialog;
|
||||
$('#dialogBulkDownloadButton').on('click', function () {
|
||||
if (!dialog) {
|
||||
dialog = $('#dialogBulkDownload').dialog({
|
||||
resizable: false,
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 460,
|
||||
buttons: {
|
||||
Close: function () {
|
||||
$(this).dialog("close");
|
||||
},
|
||||
"Download Instances": function () {
|
||||
const $this = $(this);
|
||||
const $form = $this.find('form');
|
||||
$form.trigger('submit');
|
||||
$form.find('input').prop('disabled', true);
|
||||
$this.closest('.ui-dialog').find('.ui-dialog-buttonset button').prop('disabled', true).addClass('ui-state-disabled');
|
||||
window.setTimeout(function () {
|
||||
$this.dialog("close");
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dialog.dialog('open');
|
||||
dialog.find('form').find('input').prop('disabled', false);
|
||||
dialog.closest('.ui-dialog').find('.ui-dialog-buttonset button').prop('disabled', false).removeClass('ui-state-disabled');
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@if (canBulkGenerate)
|
||||
{
|
||||
if (Model.DocumentTemplate.Scope == DocumentTemplate.DocumentTemplateScopes.User || Model.DocumentTemplate.Scope == DocumentTemplate.DocumentTemplateScopes.Device)
|
||||
@@ -1045,7 +1104,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<a id="buttonBulkGenerate" href="#" class="button">Bulk Generate</a>
|
||||
<button id="buttonBulkGenerate" type="button" class="button">Bulk Generate</button>
|
||||
<div id="dialogBulkGenerate" class="dialog dialog-bulk-generate" title="Bulk Generate: @(Model.DocumentTemplate.Id)">
|
||||
<div class="brief">
|
||||
@switch (Model.DocumentTemplate.Scope)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -224,7 +224,7 @@
|
||||
<div>
|
||||
Add all devices in the selected batch
|
||||
</div>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
@using (Html.BeginForm(MVC.API.DocumentTemplate.BulkGenerateAddDeviceBatch()))
|
||||
{
|
||||
|
||||
@@ -1287,10 +1287,11 @@ WriteLiteral(" <div>\r\n Add all devices in th
|
||||
|
||||
#line 227 "..\..\Areas\Config\Views\DocumentTemplate\_BulkGenerateShared.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(")\r\n </div>\r\n");
|
||||
WriteLiteral(" </div>\r\n");
|
||||
|
||||
|
||||
#line 229 "..\..\Areas\Config\Views\DocumentTemplate\_BulkGenerateShared.cshtml"
|
||||
@@ -1336,15 +1337,15 @@ WriteLiteral(">\r\n");
|
||||
#line hidden
|
||||
WriteLiteral(" <div");
|
||||
|
||||
WriteAttribute("class", Tuple.Create(" class=\"", 9870), Tuple.Create("\"", 9922)
|
||||
, Tuple.Create(Tuple.Create("", 9878), Tuple.Create("item", 9878), true)
|
||||
WriteAttribute("class", Tuple.Create(" class=\"", 9869), Tuple.Create("\"", 9921)
|
||||
, Tuple.Create(Tuple.Create("", 9877), Tuple.Create("item", 9877), true)
|
||||
|
||||
#line 235 "..\..\Areas\Config\Views\DocumentTemplate\_BulkGenerateShared.cshtml"
|
||||
, Tuple.Create(Tuple.Create(" ", 9882), Tuple.Create<System.Object, System.Int32>(batch.Count == 0 ? "disabled" : null
|
||||
, Tuple.Create(Tuple.Create(" ", 9881), Tuple.Create<System.Object, System.Int32>(batch.Count == 0 ? "disabled" : null
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 9883), false)
|
||||
, 9882), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" data-id=\"");
|
||||
@@ -1413,14 +1414,14 @@ WriteLiteral(" type=\"hidden\"");
|
||||
|
||||
WriteLiteral(" name=\"scope\"");
|
||||
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 10226), Tuple.Create("\"", 10240)
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 10225), Tuple.Create("\"", 10239)
|
||||
|
||||
#line 240 "..\..\Areas\Config\Views\DocumentTemplate\_BulkGenerateShared.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 10234), Tuple.Create<System.Object, System.Int32>(scope
|
||||
, Tuple.Create(Tuple.Create("", 10233), Tuple.Create<System.Object, System.Int32>(scope
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 10234), false)
|
||||
, 10233), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" />\r\n");
|
||||
@@ -1471,19 +1472,19 @@ WriteLiteral(" id=\"DocumentTemplate_BulkGenerate_Dialog_AddDocumentAttachment\"
|
||||
|
||||
WriteLiteral(" class=\"dialog dialog-bulk-generate\"");
|
||||
|
||||
WriteAttribute("title", Tuple.Create(" title=\"", 10476), Tuple.Create("\"", 10549)
|
||||
WriteAttribute("title", Tuple.Create(" title=\"", 10475), Tuple.Create("\"", 10548)
|
||||
|
||||
#line 248 "..\..\Areas\Config\Views\DocumentTemplate\_BulkGenerateShared.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 10484), Tuple.Create<System.Object, System.Int32>(Model.DocumentTemplate.Description
|
||||
, Tuple.Create(Tuple.Create("", 10483), Tuple.Create<System.Object, System.Int32>(Model.DocumentTemplate.Description
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 10484), false)
|
||||
, Tuple.Create(Tuple.Create("", 10521), Tuple.Create(":", 10521), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 10522), Tuple.Create("Add", 10523), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 10526), Tuple.Create("by", 10527), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 10529), Tuple.Create("Document", 10530), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 10538), Tuple.Create("Attachment", 10539), true)
|
||||
, 10483), false)
|
||||
, Tuple.Create(Tuple.Create("", 10520), Tuple.Create(":", 10520), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 10521), Tuple.Create("Add", 10522), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 10525), Tuple.Create("by", 10526), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 10528), Tuple.Create("Document", 10529), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 10537), Tuple.Create("Attachment", 10538), true)
|
||||
);
|
||||
|
||||
WriteLiteral(">\r\n <div");
|
||||
@@ -1581,15 +1582,15 @@ WriteLiteral(">\r\n");
|
||||
#line hidden
|
||||
WriteLiteral(" <div");
|
||||
|
||||
WriteAttribute("class", Tuple.Create(" class=\"", 11364), Tuple.Create("\"", 11419)
|
||||
, Tuple.Create(Tuple.Create("", 11372), Tuple.Create("item", 11372), true)
|
||||
WriteAttribute("class", Tuple.Create(" class=\"", 11363), Tuple.Create("\"", 11418)
|
||||
, Tuple.Create(Tuple.Create("", 11371), Tuple.Create("item", 11371), true)
|
||||
|
||||
#line 269 "..\..\Areas\Config\Views\DocumentTemplate\_BulkGenerateShared.cshtml"
|
||||
, Tuple.Create(Tuple.Create(" ", 11376), Tuple.Create<System.Object, System.Int32>(template.Count == 0 ? "disabled" : null
|
||||
, Tuple.Create(Tuple.Create(" ", 11375), Tuple.Create<System.Object, System.Int32>(template.Count == 0 ? "disabled" : null
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 11377), false)
|
||||
, 11376), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" data-id=\"");
|
||||
@@ -1686,14 +1687,14 @@ WriteLiteral(" type=\"hidden\"");
|
||||
|
||||
WriteLiteral(" name=\"scope\"");
|
||||
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 12106), Tuple.Create("\"", 12120)
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 12105), Tuple.Create("\"", 12119)
|
||||
|
||||
#line 278 "..\..\Areas\Config\Views\DocumentTemplate\_BulkGenerateShared.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 12114), Tuple.Create<System.Object, System.Int32>(scope
|
||||
, Tuple.Create(Tuple.Create("", 12113), Tuple.Create<System.Object, System.Int32>(scope
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 12114), false)
|
||||
, 12113), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" />\r\n");
|
||||
@@ -1744,19 +1745,19 @@ WriteLiteral(" id=\"DocumentTemplate_BulkGenerate_Dialog_AddUserDetail\"");
|
||||
|
||||
WriteLiteral(" class=\"dialog dialog-bulk-generate\"");
|
||||
|
||||
WriteAttribute("title", Tuple.Create(" title=\"", 12342), Tuple.Create("\"", 12407)
|
||||
WriteAttribute("title", Tuple.Create(" title=\"", 12341), Tuple.Create("\"", 12406)
|
||||
|
||||
#line 286 "..\..\Areas\Config\Views\DocumentTemplate\_BulkGenerateShared.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 12350), Tuple.Create<System.Object, System.Int32>(Model.DocumentTemplate.Description
|
||||
, Tuple.Create(Tuple.Create("", 12349), Tuple.Create<System.Object, System.Int32>(Model.DocumentTemplate.Description
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 12350), false)
|
||||
, Tuple.Create(Tuple.Create("", 12387), Tuple.Create(":", 12387), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 12388), Tuple.Create("Add", 12389), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 12392), Tuple.Create("by", 12393), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 12395), Tuple.Create("User", 12396), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 12400), Tuple.Create("Detail", 12401), true)
|
||||
, 12349), false)
|
||||
, Tuple.Create(Tuple.Create("", 12386), Tuple.Create(":", 12386), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 12387), Tuple.Create("Add", 12388), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 12391), Tuple.Create("by", 12392), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 12394), Tuple.Create("User", 12395), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 12399), Tuple.Create("Detail", 12400), true)
|
||||
);
|
||||
|
||||
WriteLiteral(">\r\n <div");
|
||||
@@ -1874,15 +1875,15 @@ WriteLiteral(">\r\n");
|
||||
#line hidden
|
||||
WriteLiteral(" <div");
|
||||
|
||||
WriteAttribute("class", Tuple.Create(" class=\"", 13278), Tuple.Create("\"", 13328)
|
||||
, Tuple.Create(Tuple.Create("", 13286), Tuple.Create("item", 13286), true)
|
||||
WriteAttribute("class", Tuple.Create(" class=\"", 13277), Tuple.Create("\"", 13327)
|
||||
, Tuple.Create(Tuple.Create("", 13285), Tuple.Create("item", 13285), true)
|
||||
|
||||
#line 309 "..\..\Areas\Config\Views\DocumentTemplate\_BulkGenerateShared.cshtml"
|
||||
, Tuple.Create(Tuple.Create(" ", 13290), Tuple.Create<System.Object, System.Int32>(key.Count == 0 ? "disabled" : null
|
||||
, Tuple.Create(Tuple.Create(" ", 13289), Tuple.Create<System.Object, System.Int32>(key.Count == 0 ? "disabled" : null
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 13291), false)
|
||||
, 13290), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" data-id=\"");
|
||||
@@ -1951,14 +1952,14 @@ WriteLiteral(" type=\"hidden\"");
|
||||
|
||||
WriteLiteral(" name=\"scope\"");
|
||||
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 13635), Tuple.Create("\"", 13649)
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 13634), Tuple.Create("\"", 13648)
|
||||
|
||||
#line 314 "..\..\Areas\Config\Views\DocumentTemplate\_BulkGenerateShared.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 13643), Tuple.Create<System.Object, System.Int32>(scope
|
||||
, Tuple.Create(Tuple.Create("", 13642), Tuple.Create<System.Object, System.Int32>(scope
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 13643), false)
|
||||
, 13642), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" />\r\n");
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
able to connect to the requesting Apple Mac client via <a target="_blank" href="http://en.wikipedia.org/wiki/Secure_Shell">SSH</a>. Enter/Script the following command:
|
||||
</span>
|
||||
<div class="code">
|
||||
curl <a target="_blank" href="http://disco:9292/Services/Client/Unauthenticated/MacSecureEnrol">http://disco:9292/Services/Client/Unauthenticated/MacSecureEnrol</a>
|
||||
curl <a target="_blank" href="@Model.MacEnrolUrl">@Model.MacEnrolUrl</a>
|
||||
</div>
|
||||
<span class="smallText">This url will return a <a target="_blank" href="http://json.org/">JSON</a> response containing basic information about the enrolment.</span><br />
|
||||
<span class="smallMessage">
|
||||
@@ -133,6 +133,167 @@
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="form" style="width: 530px; margin-top: 15px">
|
||||
<h2>Bootstrapper Server Discovery</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<div>
|
||||
The Disco ICT
|
||||
@if (Authorization.Has(Claims.Config.Enrolment.DownloadBootstrapper))
|
||||
{
|
||||
@Html.ActionLink("Bootstrapper", MVC.Services.Client.Bootstrapper())
|
||||
}
|
||||
else
|
||||
{
|
||||
<text>Bootstrapper</text>
|
||||
}
|
||||
is used to enrol devices. It is strongly recommended that HTTPS be used for all communication.
|
||||
the
|
||||
The @Html.ActionLink("Hosting", Model.HostingPluginInstalled ? MVC.Config.Plugins.Configure("Hosting") : MVC.Config.Plugins.Install())
|
||||
plugin can be used to automate deployment of HTTPS certificates.
|
||||
</div>
|
||||
<div>
|
||||
The Bootstrapper discovers the server using the first successful method (in order):
|
||||
</div>
|
||||
<ol>
|
||||
<li>
|
||||
<h5>Manually Specified</h5>
|
||||
<div>
|
||||
The server url can be specified at the command line. The url must use HTTPS. For example:
|
||||
</div>
|
||||
<div class="code">Disco.ClientBootstrapper.exe https://@Request.Url.Authority</div>
|
||||
</li>
|
||||
<li>
|
||||
<h5>DNS Service Location (SRV) Record</h5>
|
||||
Expected Record Name: <strong><code>@Model.DnsSrvRecordName</code></strong>
|
||||
@if (Model.IsServicesEducationVicGovAuDomain)
|
||||
{
|
||||
<div class="smallText">
|
||||
This mechanism is not supported in the shared education.vic.gov.au domain and can be ignored.
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Model.DnsSrvRecordValue == null)
|
||||
{
|
||||
<div class="info-box">
|
||||
<span class="error">
|
||||
No Service Location (SRV) record found.
|
||||
</span>
|
||||
@if (Request.IsSecureConnection)
|
||||
{
|
||||
<span>
|
||||
Please create a DNS Service Location (SRV) record:
|
||||
</span>
|
||||
<table class="none">
|
||||
<tr>
|
||||
<th>Service:</th>
|
||||
<td><code>_discoict</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Protocol:</th>
|
||||
<td><code>_tcp</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Priority:</th>
|
||||
<td><code>0</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Weight:</th>
|
||||
<td><code>0</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Port:</th>
|
||||
<td><code>@Request.Url.Port</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Host offering this service:</th>
|
||||
<td><code>@Request.Url.Host</code></td>
|
||||
</tr>
|
||||
</table>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div>
|
||||
Please configure and connect with HTTPS.
|
||||
<span>
|
||||
You can enable HTTPS automation using the
|
||||
@Html.ActionLink("Hosting", Model.HostingPluginInstalled ? MVC.Config.Plugins.Configure("Hosting") : MVC.Config.Plugins.Install())
|
||||
plugin.
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div>
|
||||
Value: <strong><code>https://@Model.DnsSrvRecordValue</code></strong>
|
||||
@if (Request.IsSecureConnection && !string.Equals(Model.DnsSrvRecordValue, Request.Url.Authority, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
<div class="info-box error">
|
||||
<i class="fa fa-exclamation"></i> The Service Location (SRV) record does not match the way you are currently accessing the server: <code>@Request.Url.Authority</code>.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</li>
|
||||
@if (Model.IsVicSmartDeployment)
|
||||
{
|
||||
<li>
|
||||
<h5>Victorian Government Schools VicSmart Discovery</h5>
|
||||
If the Bootstrapper detects it is running inside the VicSmart network, it will query Online Services for the Disco ICT server address based on the subnets assigned to each school.
|
||||
This is configured in the @Html.ActionLink("Hosting", Model.HostingPluginInstalled ? MVC.Config.Plugins.Configure("Hosting") : MVC.Config.Plugins.Install())
|
||||
plugin.
|
||||
</li>
|
||||
}
|
||||
<li>
|
||||
<h5>Legacy Discovery</h5>
|
||||
<div>
|
||||
The Bootstrapper will attempt to send an ICMP ping to "<code>disco</code>". If the ping is successful, it will attempt to connect to <code>http://disco:9292/</code>.
|
||||
</div>
|
||||
<div>
|
||||
@if (canConfig)
|
||||
{
|
||||
<input id="Enrolment_LegacyDiscovery" type="checkbox" @(Model.LegacyDiscoveryEnabled ? "checked" : null) />
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
document.DiscoFunctions.PropertyChangeHelper(
|
||||
$('#Enrolment_LegacyDiscovery'),
|
||||
null,
|
||||
'@Url.Action(MVC.API.Enrolment.LegacyDiscovery())',
|
||||
'enabled'
|
||||
);
|
||||
});
|
||||
</script>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="Enrolment_LegacyDiscovery" type="checkbox" @(Model.LegacyDiscoveryEnabled ? "checked" : null) disabled="disabled" />
|
||||
}
|
||||
<label for="Enrolment_LegacyDiscovery">
|
||||
Legacy Discovery Enabled
|
||||
</label>
|
||||
@AjaxHelpers.AjaxLoader()
|
||||
</div>
|
||||
@if ((Model.IsServicesEducationVicGovAuDomain || Model.DnsSrvRecordValue != null) && Model.LegacyDiscoveryEnabled)
|
||||
{
|
||||
<div class="info-box error">
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
It is not recommended to have Legacy Discovery enabled. Please use the latest Bootstrapper and disable this option.
|
||||
</div>
|
||||
}
|
||||
<div>
|
||||
This method is not secure and is only provided for backwards compatibility. In time this method will be removed.
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@if (canShowStatus && Authorization.Has(Claims.Config.Logging.Show))
|
||||
{
|
||||
<h2>Live Enrolment Logging</h2>
|
||||
|
||||
@@ -451,10 +451,26 @@ WriteLiteral(">\r\n curl <a");
|
||||
|
||||
WriteLiteral(" target=\"_blank\"");
|
||||
|
||||
WriteLiteral(" href=\"http://disco:9292/Services/Client/Unauthenticated/MacSecureEnrol\"");
|
||||
WriteAttribute("href", Tuple.Create(" href=\"", 4881), Tuple.Create("\"", 4906)
|
||||
|
||||
#line 124 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 4888), Tuple.Create<System.Object, System.Int32>(Model.MacEnrolUrl
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 4888), false)
|
||||
);
|
||||
|
||||
WriteLiteral(">http://disco:9292/Services/Client/Unauthenticated/MacSecureEnrol</a>\r\n " +
|
||||
" </div>\r\n <span");
|
||||
WriteLiteral(">");
|
||||
|
||||
|
||||
#line 124 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Model.MacEnrolUrl);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</a>\r\n </div>\r\n <span");
|
||||
|
||||
WriteLiteral(" class=\"smallText\"");
|
||||
|
||||
@@ -486,10 +502,521 @@ WriteLiteral(" class=\"code\"");
|
||||
|
||||
WriteLiteral("><script></span>\r\n tag embedded on the organisation\'s in" +
|
||||
"tranet.\r\n </span>\r\n </td>\r\n </tr>\r\n </table>" +
|
||||
"\r\n</div>\r\n");
|
||||
"\r\n</div>\r\n<div");
|
||||
|
||||
WriteLiteral(" class=\"form\"");
|
||||
|
||||
WriteLiteral(" style=\"width: 530px; margin-top: 15px\"");
|
||||
|
||||
WriteLiteral(">\r\n <h2>Bootstrapper Server Discovery</h2>\r\n <table>\r\n <tr>\r\n " +
|
||||
" <td>\r\n <div>\r\n The Disco ICT\r\n");
|
||||
|
||||
|
||||
#line 136 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
#line 143 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 143 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
if (Authorization.Has(Claims.Config.Enrolment.DownloadBootstrapper))
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 145 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Html.ActionLink("Bootstrapper", MVC.Services.Client.Bootstrapper()));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 145 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" ");
|
||||
|
||||
WriteLiteral("Bootstrapper");
|
||||
|
||||
WriteLiteral("\r\n");
|
||||
|
||||
|
||||
#line 150 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" is used to enrol devices. It is strongly recommended that HTT" +
|
||||
"PS be used for all communication.\r\n the\r\n " +
|
||||
"The ");
|
||||
|
||||
|
||||
#line 153 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Html.ActionLink("Hosting", Model.HostingPluginInstalled ? MVC.Config.Plugins.Configure("Hosting") : MVC.Config.Plugins.Install()));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(@"
|
||||
plugin can be used to automate deployment of HTTPS certificates.
|
||||
</div>
|
||||
<div>
|
||||
The Bootstrapper discovers the server using the first successful method (in order):
|
||||
</div>
|
||||
<ol>
|
||||
<li>
|
||||
<h5>Manually Specified</h5>
|
||||
<div>
|
||||
The server url can be specified at the command line. The url must use HTTPS. For example:
|
||||
</div>
|
||||
<div");
|
||||
|
||||
WriteLiteral(" class=\"code\"");
|
||||
|
||||
WriteLiteral(">Disco.ClientBootstrapper.exe https://");
|
||||
|
||||
|
||||
#line 165 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Request.Url.Authority);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</div>\r\n </li>\r\n <li>\r\n " +
|
||||
" <h5>DNS Service Location (SRV) Record</h5>\r\n Expected" +
|
||||
" Record Name: <strong><code>");
|
||||
|
||||
|
||||
#line 169 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Model.DnsSrvRecordName);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code></strong>\r\n");
|
||||
|
||||
|
||||
#line 170 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 170 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
if (Model.IsServicesEducationVicGovAuDomain)
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <div");
|
||||
|
||||
WriteLiteral(" class=\"smallText\"");
|
||||
|
||||
WriteLiteral(">\r\n This mechanism is not supported in the shared " +
|
||||
"education.vic.gov.au domain and can be ignored.\r\n </d" +
|
||||
"iv>\r\n");
|
||||
|
||||
|
||||
#line 175 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Model.DnsSrvRecordValue == null)
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <div");
|
||||
|
||||
WriteLiteral(" class=\"info-box\"");
|
||||
|
||||
WriteLiteral(">\r\n <span");
|
||||
|
||||
WriteLiteral(" class=\"error\"");
|
||||
|
||||
WriteLiteral(">\r\n No Service Location (SRV) record found" +
|
||||
".\r\n </span>\r\n");
|
||||
|
||||
|
||||
#line 184 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 184 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
if (Request.IsSecureConnection)
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <span>\r\n " +
|
||||
" Please create a DNS Service Location (SRV) record:\r\n " +
|
||||
" </span>\r\n");
|
||||
|
||||
WriteLiteral(" <table");
|
||||
|
||||
WriteLiteral(" class=\"none\"");
|
||||
|
||||
WriteLiteral(@">
|
||||
<tr>
|
||||
<th>Service:</th>
|
||||
<td><code>_discoict</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Protocol:</th>
|
||||
<td><code>_tcp</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Priority:</th>
|
||||
<td><code>0</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Weight:</th>
|
||||
<td><code>0</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Port:</th>
|
||||
<td><code>");
|
||||
|
||||
|
||||
#line 208 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Request.Url.Port);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(@"</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Host offering this service:</th>
|
||||
<td><code>");
|
||||
|
||||
|
||||
#line 212 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Request.Url.Host);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code></td>\r\n </tr>\r\n " +
|
||||
" </table>\r\n");
|
||||
|
||||
|
||||
#line 215 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(@" <div>
|
||||
Please configure and connect with HTTPS.
|
||||
<span>
|
||||
You can enable HTTPS automation using the
|
||||
");
|
||||
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 222 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Html.ActionLink("Hosting", Model.HostingPluginInstalled ? MVC.Config.Plugins.Configure("Hosting") : MVC.Config.Plugins.Install()));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\r\n plugin.\r\n " +
|
||||
" </span>\r\n </div>\r\n");
|
||||
|
||||
|
||||
#line 226 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" </div>\r\n");
|
||||
|
||||
|
||||
#line 228 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <div>\r\n Value:" +
|
||||
" <strong><code>https://");
|
||||
|
||||
|
||||
#line 232 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Model.DnsSrvRecordValue);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code></strong>\r\n");
|
||||
|
||||
|
||||
#line 233 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 233 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
if (Request.IsSecureConnection && !string.Equals(Model.DnsSrvRecordValue, Request.Url.Authority, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <div");
|
||||
|
||||
WriteLiteral(" class=\"info-box error\"");
|
||||
|
||||
WriteLiteral(">\r\n <i");
|
||||
|
||||
WriteLiteral(" class=\"fa fa-exclamation\"");
|
||||
|
||||
WriteLiteral("></i> The Service Location (SRV) record does not match the way you are currently " +
|
||||
"accessing the server: <code>");
|
||||
|
||||
|
||||
#line 236 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Request.Url.Authority);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</code>.\r\n </div>\r\n");
|
||||
|
||||
|
||||
#line 238 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" </div>\r\n");
|
||||
|
||||
|
||||
#line 240 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" </li>\r\n");
|
||||
|
||||
|
||||
#line 243 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 243 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
if (Model.IsVicSmartDeployment)
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(@" <li>
|
||||
<h5>Victorian Government Schools VicSmart Discovery</h5>
|
||||
If the Bootstrapper detects it is running inside the VicSmart network, it will query Online Services for the Disco ICT server address based on the subnets assigned to each school.
|
||||
This is configured in the ");
|
||||
|
||||
|
||||
#line 248 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Html.ActionLink("Hosting", Model.HostingPluginInstalled ? MVC.Config.Plugins.Configure("Hosting") : MVC.Config.Plugins.Install()));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\r\n plugin.\r\n </li>\r\n");
|
||||
|
||||
|
||||
#line 251 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(@" <li>
|
||||
<h5>Legacy Discovery</h5>
|
||||
<div>
|
||||
The Bootstrapper will attempt to send an ICMP ping to "<code>disco</code>". If the ping is successful, it will attempt to connect to <code>http://disco:9292/</code>.
|
||||
</div>
|
||||
<div>
|
||||
");
|
||||
|
||||
|
||||
#line 258 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 258 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
if (canConfig)
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <input");
|
||||
|
||||
WriteLiteral(" id=\"Enrolment_LegacyDiscovery\"");
|
||||
|
||||
WriteLiteral(" type=\"checkbox\"");
|
||||
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 260 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Model.LegacyDiscoveryEnabled ? "checked" : null);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" />\r\n");
|
||||
|
||||
WriteLiteral(" <script");
|
||||
|
||||
WriteLiteral(" type=\"text/javascript\"");
|
||||
|
||||
WriteLiteral(@">
|
||||
$(function () {
|
||||
document.DiscoFunctions.PropertyChangeHelper(
|
||||
$('#Enrolment_LegacyDiscovery'),
|
||||
null,
|
||||
'");
|
||||
|
||||
|
||||
#line 266 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Url.Action(MVC.API.Enrolment.LegacyDiscovery()));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\',\r\n \'enabled\'\r\n " +
|
||||
" );\r\n });\r\n " +
|
||||
" </script>\r\n");
|
||||
|
||||
|
||||
#line 271 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <input");
|
||||
|
||||
WriteLiteral(" id=\"Enrolment_LegacyDiscovery\"");
|
||||
|
||||
WriteLiteral(" type=\"checkbox\"");
|
||||
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 274 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Model.LegacyDiscoveryEnabled ? "checked" : null);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" disabled=\"disabled\" />\r\n");
|
||||
|
||||
|
||||
#line 275 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <label");
|
||||
|
||||
WriteLiteral(" for=\"Enrolment_LegacyDiscovery\"");
|
||||
|
||||
WriteLiteral(">\r\n Legacy Discovery Enabled\r\n " +
|
||||
" </label>\r\n");
|
||||
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 279 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(AjaxHelpers.AjaxLoader());
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\r\n </div>\r\n");
|
||||
|
||||
|
||||
#line 281 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 281 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
if ((Model.IsServicesEducationVicGovAuDomain || Model.DnsSrvRecordValue != null) && Model.LegacyDiscoveryEnabled)
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <div");
|
||||
|
||||
WriteLiteral(" class=\"info-box error\"");
|
||||
|
||||
WriteLiteral(">\r\n <i");
|
||||
|
||||
WriteLiteral(" class=\"fa fa-exclamation-triangle\"");
|
||||
|
||||
WriteLiteral("></i>\r\n It is not recommended to have Legacy Disco" +
|
||||
"very enabled. Please use the latest Bootstrapper and disable this option.\r\n " +
|
||||
" </div>\r\n");
|
||||
|
||||
|
||||
#line 287 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(@" <div>
|
||||
This method is not secure and is only provided for backwards compatibility. In time this method will be removed.
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
");
|
||||
|
||||
|
||||
#line 297 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
if (canShowStatus && Authorization.Has(Claims.Config.Logging.Show))
|
||||
{
|
||||
|
||||
@@ -499,13 +1026,13 @@ WriteLiteral("><script></span>\r\n tag embedded on the
|
||||
WriteLiteral(" <h2>Live Enrolment Logging</h2>\r\n");
|
||||
|
||||
|
||||
#line 139 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
#line 300 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 139 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
#line 300 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Html.Partial(MVC.Config.Shared.Views.LogEvents, new Disco.Web.Areas.Config.Models.Shared.LogEventsModel()
|
||||
{
|
||||
IsLive = true,
|
||||
@@ -519,7 +1046,7 @@ Write(Html.Partial(MVC.Config.Shared.Views.LogEvents, new Disco.Web.Areas.Config
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 146 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
#line 307 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
|
||||
}
|
||||
|
||||
@@ -533,13 +1060,13 @@ WriteLiteral(" class=\"actionBar\"");
|
||||
WriteLiteral(">\r\n");
|
||||
|
||||
|
||||
#line 149 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
#line 310 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 149 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
#line 310 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
if (Authorization.Has(Claims.Config.Enrolment.DownloadBootstrapper))
|
||||
{
|
||||
|
||||
@@ -547,14 +1074,14 @@ WriteLiteral(">\r\n");
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 151 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
#line 312 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Html.ActionLinkButton("Download Bootstrapper", MVC.Services.Client.Bootstrapper()));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 151 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
#line 312 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
|
||||
}
|
||||
|
||||
@@ -564,7 +1091,7 @@ WriteLiteral(">\r\n");
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 153 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
#line 314 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
if (canShowStatus)
|
||||
{
|
||||
|
||||
@@ -572,14 +1099,14 @@ WriteLiteral(" ");
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 155 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
#line 316 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
Write(Html.ActionLinkButton("Enrolment Status", MVC.Config.Enrolment.Status()));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 155 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
#line 316 "..\..\Areas\Config\Views\Enrolment\Index.cshtml"
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -77,23 +77,12 @@ else
|
||||
{
|
||||
<div class="form" style="width: 450px; padding: 100px 0;">
|
||||
<h2>No saved exports are configured</h2>
|
||||
<div>
|
||||
@if (Authorization.Has(Claims.Device.Actions.Export))
|
||||
{
|
||||
<a href="@Url.Action(MVC.Device.Export())" class="button small">Device Export</a>
|
||||
}
|
||||
@if (Authorization.Has(Claims.Job.Actions.Export))
|
||||
{
|
||||
<a href="@Url.Action(MVC.Job.Export())" class="button small">Job Export</a>
|
||||
}
|
||||
@if (Authorization.Has(Claims.Config.UserFlag.Export))
|
||||
{
|
||||
<a href="@Url.Action(MVC.Config.UserFlag.Export())" class="button small">User Flag Export</a>
|
||||
}
|
||||
@if (Authorization.Has(Claims.Config.DeviceFlag.Export))
|
||||
{
|
||||
<a href="@Url.Action(MVC.Config.DeviceFlag.Export())" class="button small">Device Flag Export</a>
|
||||
}
|
||||
<div class="info-box">
|
||||
<p class="fa-p">
|
||||
<i class="fa fa-fw fa-info-circle"></i>
|
||||
Visit any export page (eg. <a href="@Url.Action(MVC.Device.Export())">Device Export</a>)
|
||||
and click "Save Export" to create a saved export that can be scheduled or run on demand.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -420,148 +420,36 @@ WriteLiteral(" class=\"form\"");
|
||||
|
||||
WriteLiteral(" style=\"width: 450px; padding: 100px 0;\"");
|
||||
|
||||
WriteLiteral(">\r\n <h2>No saved exports are configured</h2>\r\n <div>\r\n");
|
||||
WriteLiteral(">\r\n <h2>No saved exports are configured</h2>\r\n <div");
|
||||
|
||||
|
||||
#line 81 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 81 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
if (Authorization.Has(Claims.Device.Actions.Export))
|
||||
{
|
||||
WriteLiteral(" class=\"info-box\"");
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <a");
|
||||
WriteLiteral(">\r\n <p");
|
||||
|
||||
WriteAttribute("href", Tuple.Create(" href=\"", 3626), Tuple.Create("\"", 3665)
|
||||
WriteLiteral(" class=\"fa-p\"");
|
||||
|
||||
WriteLiteral(">\r\n <i");
|
||||
|
||||
WriteLiteral(" class=\"fa fa-fw fa-info-circle\"");
|
||||
|
||||
WriteLiteral("></i>\r\n Visit any export page (eg. <a");
|
||||
|
||||
WriteAttribute("href", Tuple.Create(" href=\"", 3675), Tuple.Create("\"", 3714)
|
||||
|
||||
#line 83 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 3633), Tuple.Create<System.Object, System.Int32>(Url.Action(MVC.Device.Export())
|
||||
, Tuple.Create(Tuple.Create("", 3682), Tuple.Create<System.Object, System.Int32>(Url.Action(MVC.Device.Export())
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 3633), false)
|
||||
, 3682), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" class=\"button small\"");
|
||||
|
||||
WriteLiteral(">Device Export</a>\r\n");
|
||||
|
||||
|
||||
#line 84 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 85 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
if (Authorization.Has(Claims.Job.Actions.Export))
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <a");
|
||||
|
||||
WriteAttribute("href", Tuple.Create(" href=\"", 3819), Tuple.Create("\"", 3855)
|
||||
|
||||
#line 87 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 3826), Tuple.Create<System.Object, System.Int32>(Url.Action(MVC.Job.Export())
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 3826), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" class=\"button small\"");
|
||||
|
||||
WriteLiteral(">Job Export</a>\r\n");
|
||||
WriteLiteral(">Device Export</a>)\r\n and click \"Save Export\" to create a saved ex" +
|
||||
"port that can be scheduled or run on demand.\r\n </p>\r\n </div>\r\n" +
|
||||
" </div>\r\n");
|
||||
|
||||
|
||||
#line 88 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 89 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
if (Authorization.Has(Claims.Config.UserFlag.Export))
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <a");
|
||||
|
||||
WriteAttribute("href", Tuple.Create(" href=\"", 4010), Tuple.Create("\"", 4058)
|
||||
|
||||
#line 91 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 4017), Tuple.Create<System.Object, System.Int32>(Url.Action(MVC.Config.UserFlag.Export())
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 4017), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" class=\"button small\"");
|
||||
|
||||
WriteLiteral(">User Flag Export</a>\r\n");
|
||||
|
||||
|
||||
#line 92 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 93 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
if (Authorization.Has(Claims.Config.DeviceFlag.Export))
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <a");
|
||||
|
||||
WriteAttribute("href", Tuple.Create(" href=\"", 4221), Tuple.Create("\"", 4271)
|
||||
|
||||
#line 95 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 4228), Tuple.Create<System.Object, System.Int32>(Url.Action(MVC.Config.DeviceFlag.Export())
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 4228), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" class=\"button small\"");
|
||||
|
||||
WriteLiteral(">Device Flag Export</a>\r\n");
|
||||
|
||||
|
||||
#line 96 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" </div>\r\n </div>\r\n");
|
||||
|
||||
|
||||
#line 99 "..\..\Areas\Config\Views\Export\Index.cshtml"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -93,8 +93,18 @@
|
||||
<h3>Filter</h3>
|
||||
<select id="Config_ReportPrefs_Builder_Filter">
|
||||
<option value=""><None></option>
|
||||
<option value="DeviceProfile">Device Profile</option>
|
||||
<option value="DeviceAddress">Device Profile Address</option>
|
||||
@if (Model.DeviceProfiles.Any())
|
||||
{
|
||||
<option value="DeviceProfile">Device Profile</option>
|
||||
}
|
||||
@if (Model.OrganisationAddresses.Any())
|
||||
{
|
||||
<option value="DeviceAddress">Device Profile Address</option>
|
||||
}
|
||||
@if (Model.JobQueues.Any())
|
||||
{
|
||||
<option value="JobQueue">Job Queue</option>
|
||||
}
|
||||
</select>
|
||||
<div class="options">
|
||||
<div class="method">
|
||||
@@ -103,17 +113,17 @@
|
||||
</div>
|
||||
<div class="filter-option filter-DeviceProfile">
|
||||
<ul class="none">
|
||||
@foreach (var deviceProfile in Model.DeviceProfiles.Value)
|
||||
@foreach (var deviceProfile in Model.DeviceProfiles)
|
||||
{
|
||||
<li>
|
||||
<input id="Config_ReportPrefs_Builder_DP_@(deviceProfile.Id)" type="checkbox" value="@deviceProfile.Id" /><label for="Config_ReportPrefs_Builder_DP_@(deviceProfile.Id)">@deviceProfile.Description</label>
|
||||
<input id="Config_ReportPrefs_Builder_DP_@(deviceProfile.Id)" type="checkbox" value="@deviceProfile.Id" /><label for="Config_ReportPrefs_Builder_DP_@(deviceProfile.Id)">@deviceProfile.Name</label>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="filter-option filter-DeviceAddress">
|
||||
<ul class="none">
|
||||
@foreach (var address in Model.OrganisationAddresses.Value)
|
||||
@foreach (var address in Model.OrganisationAddresses)
|
||||
{
|
||||
<li>
|
||||
<input id="Config_ReportPrefs_Builder_OA_@(address.Id)" type="checkbox" value="@address.ShortName" /><label for="Config_ReportPrefs_Builder_OA_@(address.Id)">@address.Name (@address.ShortName)</label>
|
||||
@@ -121,6 +131,16 @@
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="filter-option filter-JobQueue">
|
||||
<ul class="none">
|
||||
@foreach (var queue in Model.JobQueues)
|
||||
{
|
||||
<li>
|
||||
<input id="Config_ReportPrefs_Builder_JQ_@(queue.Id)" type="checkbox" value="@queue.Id" /><label for="Config_ReportPrefs_Builder_JQ_@(queue.Id)"><i class="fa fa-@(queue.Icon) d-@(queue.IconColour)"></i> @queue.Name</label>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -436,16 +436,82 @@ WriteLiteral(">\r\n <option");
|
||||
|
||||
WriteLiteral(" value=\"\"");
|
||||
|
||||
WriteLiteral("><None></option>\r\n <option");
|
||||
WriteLiteral("><None></option>\r\n");
|
||||
|
||||
|
||||
#line 96 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 96 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
if (Model.DeviceProfiles.Any())
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <option");
|
||||
|
||||
WriteLiteral(" value=\"DeviceProfile\"");
|
||||
|
||||
WriteLiteral(">Device Profile</option>\r\n <option");
|
||||
WriteLiteral(">Device Profile</option>\r\n");
|
||||
|
||||
|
||||
#line 99 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 100 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
if (Model.OrganisationAddresses.Any())
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <option");
|
||||
|
||||
WriteLiteral(" value=\"DeviceAddress\"");
|
||||
|
||||
WriteLiteral(">Device Profile Address</option>\r\n </select>\r\n " +
|
||||
" <div");
|
||||
WriteLiteral(">Device Profile Address</option>\r\n");
|
||||
|
||||
|
||||
#line 103 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" ");
|
||||
|
||||
|
||||
#line 104 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
if (Model.JobQueues.Any())
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <option");
|
||||
|
||||
WriteLiteral(" value=\"JobQueue\"");
|
||||
|
||||
WriteLiteral(">Job Queue</option>\r\n");
|
||||
|
||||
|
||||
#line 107 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" </select>\r\n <div");
|
||||
|
||||
WriteLiteral(" class=\"options\"");
|
||||
|
||||
@@ -495,14 +561,14 @@ WriteLiteral(" class=\"none\"");
|
||||
WriteLiteral(">\r\n");
|
||||
|
||||
|
||||
#line 106 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
#line 116 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 106 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
foreach (var deviceProfile in Model.DeviceProfiles.Value)
|
||||
#line 116 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
foreach (var deviceProfile in Model.DeviceProfiles)
|
||||
{
|
||||
|
||||
|
||||
@@ -511,47 +577,47 @@ WriteLiteral(">\r\n");
|
||||
WriteLiteral(" <li>\r\n " +
|
||||
" <input");
|
||||
|
||||
WriteAttribute("id", Tuple.Create(" id=\"", 7313), Tuple.Create("\"", 7367)
|
||||
, Tuple.Create(Tuple.Create("", 7318), Tuple.Create("Config_ReportPrefs_Builder_DP_", 7318), true)
|
||||
WriteAttribute("id", Tuple.Create(" id=\"", 7806), Tuple.Create("\"", 7860)
|
||||
, Tuple.Create(Tuple.Create("", 7811), Tuple.Create("Config_ReportPrefs_Builder_DP_", 7811), true)
|
||||
|
||||
#line 109 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 7348), Tuple.Create<System.Object, System.Int32>(deviceProfile.Id
|
||||
#line 119 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 7841), Tuple.Create<System.Object, System.Int32>(deviceProfile.Id
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 7348), false)
|
||||
, 7841), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" type=\"checkbox\"");
|
||||
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 7384), Tuple.Create("\"", 7409)
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 7877), Tuple.Create("\"", 7902)
|
||||
|
||||
#line 109 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 7392), Tuple.Create<System.Object, System.Int32>(deviceProfile.Id
|
||||
#line 119 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 7885), Tuple.Create<System.Object, System.Int32>(deviceProfile.Id
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 7392), false)
|
||||
, 7885), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" /><label");
|
||||
|
||||
WriteAttribute("for", Tuple.Create(" for=\"", 7419), Tuple.Create("\"", 7474)
|
||||
, Tuple.Create(Tuple.Create("", 7425), Tuple.Create("Config_ReportPrefs_Builder_DP_", 7425), true)
|
||||
WriteAttribute("for", Tuple.Create(" for=\"", 7912), Tuple.Create("\"", 7967)
|
||||
, Tuple.Create(Tuple.Create("", 7918), Tuple.Create("Config_ReportPrefs_Builder_DP_", 7918), true)
|
||||
|
||||
#line 109 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 7455), Tuple.Create<System.Object, System.Int32>(deviceProfile.Id
|
||||
#line 119 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 7948), Tuple.Create<System.Object, System.Int32>(deviceProfile.Id
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 7455), false)
|
||||
, 7948), false)
|
||||
);
|
||||
|
||||
WriteLiteral(">");
|
||||
|
||||
|
||||
#line 109 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
Write(deviceProfile.Description);
|
||||
#line 119 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
Write(deviceProfile.Name);
|
||||
|
||||
|
||||
#line default
|
||||
@@ -559,7 +625,7 @@ WriteLiteral(">");
|
||||
WriteLiteral("</label>\r\n </li>\r\n");
|
||||
|
||||
|
||||
#line 111 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
#line 121 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
}
|
||||
|
||||
|
||||
@@ -577,14 +643,14 @@ WriteLiteral(" class=\"none\"");
|
||||
WriteLiteral(">\r\n");
|
||||
|
||||
|
||||
#line 116 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
#line 126 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 116 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
foreach (var address in Model.OrganisationAddresses.Value)
|
||||
#line 126 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
foreach (var address in Model.OrganisationAddresses)
|
||||
{
|
||||
|
||||
|
||||
@@ -593,46 +659,46 @@ WriteLiteral(">\r\n");
|
||||
WriteLiteral(" <li>\r\n " +
|
||||
" <input");
|
||||
|
||||
WriteAttribute("id", Tuple.Create(" id=\"", 8074), Tuple.Create("\"", 8122)
|
||||
, Tuple.Create(Tuple.Create("", 8079), Tuple.Create("Config_ReportPrefs_Builder_OA_", 8079), true)
|
||||
WriteAttribute("id", Tuple.Create(" id=\"", 8554), Tuple.Create("\"", 8602)
|
||||
, Tuple.Create(Tuple.Create("", 8559), Tuple.Create("Config_ReportPrefs_Builder_OA_", 8559), true)
|
||||
|
||||
#line 119 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 8109), Tuple.Create<System.Object, System.Int32>(address.Id
|
||||
#line 129 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 8589), Tuple.Create<System.Object, System.Int32>(address.Id
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 8109), false)
|
||||
, 8589), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" type=\"checkbox\"");
|
||||
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 8139), Tuple.Create("\"", 8165)
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 8619), Tuple.Create("\"", 8645)
|
||||
|
||||
#line 119 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 8147), Tuple.Create<System.Object, System.Int32>(address.ShortName
|
||||
#line 129 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 8627), Tuple.Create<System.Object, System.Int32>(address.ShortName
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 8147), false)
|
||||
, 8627), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" /><label");
|
||||
|
||||
WriteAttribute("for", Tuple.Create(" for=\"", 8175), Tuple.Create("\"", 8224)
|
||||
, Tuple.Create(Tuple.Create("", 8181), Tuple.Create("Config_ReportPrefs_Builder_OA_", 8181), true)
|
||||
WriteAttribute("for", Tuple.Create(" for=\"", 8655), Tuple.Create("\"", 8704)
|
||||
, Tuple.Create(Tuple.Create("", 8661), Tuple.Create("Config_ReportPrefs_Builder_OA_", 8661), true)
|
||||
|
||||
#line 119 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 8211), Tuple.Create<System.Object, System.Int32>(address.Id
|
||||
#line 129 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 8691), Tuple.Create<System.Object, System.Int32>(address.Id
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 8211), false)
|
||||
, 8691), false)
|
||||
);
|
||||
|
||||
WriteLiteral(">");
|
||||
|
||||
|
||||
#line 119 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
#line 129 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
Write(address.Name);
|
||||
|
||||
|
||||
@@ -641,7 +707,7 @@ WriteLiteral(">");
|
||||
WriteLiteral(" (");
|
||||
|
||||
|
||||
#line 119 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
#line 129 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
Write(address.ShortName);
|
||||
|
||||
|
||||
@@ -650,7 +716,111 @@ WriteLiteral(" (");
|
||||
WriteLiteral(")</label>\r\n </li>\r\n");
|
||||
|
||||
|
||||
#line 121 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
#line 131 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" </ul>\r\n </div>" +
|
||||
"\r\n <div");
|
||||
|
||||
WriteLiteral(" class=\"filter-option filter-JobQueue\"");
|
||||
|
||||
WriteLiteral(">\r\n <ul");
|
||||
|
||||
WriteLiteral(" class=\"none\"");
|
||||
|
||||
WriteLiteral(">\r\n");
|
||||
|
||||
|
||||
#line 136 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 136 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
foreach (var queue in Model.JobQueues)
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral(" <li>\r\n " +
|
||||
" <input");
|
||||
|
||||
WriteAttribute("id", Tuple.Create(" id=\"", 9287), Tuple.Create("\"", 9333)
|
||||
, Tuple.Create(Tuple.Create("", 9292), Tuple.Create("Config_ReportPrefs_Builder_JQ_", 9292), true)
|
||||
|
||||
#line 139 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 9322), Tuple.Create<System.Object, System.Int32>(queue.Id
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 9322), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" type=\"checkbox\"");
|
||||
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 9350), Tuple.Create("\"", 9367)
|
||||
|
||||
#line 139 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 9358), Tuple.Create<System.Object, System.Int32>(queue.Id
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 9358), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" /><label");
|
||||
|
||||
WriteAttribute("for", Tuple.Create(" for=\"", 9377), Tuple.Create("\"", 9424)
|
||||
, Tuple.Create(Tuple.Create("", 9383), Tuple.Create("Config_ReportPrefs_Builder_JQ_", 9383), true)
|
||||
|
||||
#line 139 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 9413), Tuple.Create<System.Object, System.Int32>(queue.Id
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 9413), false)
|
||||
);
|
||||
|
||||
WriteLiteral("><i");
|
||||
|
||||
WriteAttribute("class", Tuple.Create(" class=\"", 9428), Tuple.Create("\"", 9477)
|
||||
, Tuple.Create(Tuple.Create("", 9436), Tuple.Create("fa", 9436), true)
|
||||
, Tuple.Create(Tuple.Create(" ", 9438), Tuple.Create("fa-", 9439), true)
|
||||
|
||||
#line 139 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 9442), Tuple.Create<System.Object, System.Int32>(queue.Icon
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 9442), false)
|
||||
, Tuple.Create(Tuple.Create(" ", 9455), Tuple.Create("d-", 9456), true)
|
||||
|
||||
#line 139 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 9458), Tuple.Create<System.Object, System.Int32>(queue.IconColour
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 9458), false)
|
||||
);
|
||||
|
||||
WriteLiteral("></i> ");
|
||||
|
||||
|
||||
#line 139 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
Write(queue.Name);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("</label>\r\n </li>\r\n");
|
||||
|
||||
|
||||
#line 141 "..\..\Areas\Config\Views\JobPreferences\Parts\Reports.cshtml"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<div class="code" title="@group.Id">
|
||||
<i class="fa fa-fw fa-lg fa-link success"></i>@group.Domain.FriendlyDistinguishedNamePath(group.DistinguishedName)
|
||||
</div>
|
||||
<button type="button" class="button small Config_LinkedGroup_LinkButton" data-linkedgroupid="@(Model.ManagedGroup.Configuration.GroupId)" data-linkedgroupfilterdateoption="@(Model.ManagedGroup.IncludeFilterBeginDate)" data-linkedgroupfilterdate="@(Model.ManagedGroup.Configuration.FilterBeginDate)" data-linkedroupdescription="@(Model.CategoryDescription)" data-linkedroupupdateurl="@(Model.UpdateUrl)">Change Link</button>
|
||||
<button type="button" class="button small Config_LinkedGroup_LinkButton" data-linkedgroupid="@(Model.ManagedGroup.Configuration.GroupId)" data-linkedgroupfilterdateoption="@(Model.ManagedGroup.IncludeFilterBeginDate)" data-linkedgroupfilterdate="@(Model.ManagedGroup.Configuration.FilterBeginDate)" data-linkedgroupdescription="@(Model.CategoryDescription)" data-linkedgroupupdateurl="@(Model.UpdateUrl)" data-linkedgroupupdatedescription="@(Model.ManagedGroup.Configuration.UpdateDescription)">Change Link</button>
|
||||
using (Html.BeginForm(MVC.API.System.SyncActiveDirectoryManagedGroup(Model.ManagedGroup.Key, Context.Request.Path)))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
@@ -34,12 +34,12 @@
|
||||
<div class="code error">
|
||||
<i class="fa fa-fw fa-lg fa-unlink error"></i>Group Not Found: <strong class="code">@Model.ManagedGroup.Configuration.GroupId</strong>
|
||||
</div>
|
||||
<button type="button" class="button small Config_LinkedGroup_LinkButton" data-linkedgroupid="@(Model.ManagedGroup.Configuration.GroupId)" data-linkedgroupfilterdateoption="@(Model.ManagedGroup.IncludeFilterBeginDate)" data-linkedgroupfilterdate="@(Model.ManagedGroup.Configuration.FilterBeginDate)" data-linkedroupdescription="@(Model.CategoryDescription)" data-linkedroupupdateurl="@(Model.UpdateUrl)">Change Link</button>
|
||||
<button type="button" class="button small Config_LinkedGroup_LinkButton" data-linkedgroupid="@(Model.ManagedGroup.Configuration.GroupId)" data-linkedgroupfilterdateoption="@(Model.ManagedGroup.IncludeFilterBeginDate)" data-linkedgroupfilterdate="@(Model.ManagedGroup.Configuration.FilterBeginDate)" data-linkedgroupdescription="@(Model.CategoryDescription)" data-linkedgroupupdateurl="@(Model.UpdateUrl)" data-linkedgroupupdatedescription="@(Model.ManagedGroup.Configuration.UpdateDescription)">Change Link</button>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="button small Config_LinkedGroup_LinkButton" data-linkedgroupid="" data-linkedgroupfilterdateoption="@(Model.IncludeFilterBeginDate)" data-linkedroupdescription="@(Model.CategoryDescription)" data-linkedroupupdateurl="@(Model.UpdateUrl)">Link Group</button>
|
||||
<button type="button" class="button small Config_LinkedGroup_LinkButton" data-linkedgroupid="" data-linkedgroupfilterdateoption="@(Model.IncludeFilterBeginDate)" data-linkedgroupdescription="@(Model.CategoryDescription)" data-linkedgroupupdateurl="@(Model.UpdateUrl)" data-linkedgroupupdatedescription="True">Link Group</button>
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -177,22 +177,33 @@ WriteLiteral(" data-linkedgroupfilterdate=\"");
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(" data-linkedroupdescription=\"");
|
||||
WriteLiteral(" data-linkedgroupdescription=\"");
|
||||
|
||||
|
||||
#line 25 "..\..\Areas\Config\Views\Shared\LinkedGroupInstance.cshtml"
|
||||
Write(Model.CategoryDescription);
|
||||
Write(Model.CategoryDescription);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(" data-linkedroupupdateurl=\"");
|
||||
WriteLiteral(" data-linkedgroupupdateurl=\"");
|
||||
|
||||
|
||||
#line 25 "..\..\Areas\Config\Views\Shared\LinkedGroupInstance.cshtml"
|
||||
Write(Model.UpdateUrl);
|
||||
Write(Model.UpdateUrl);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(" data-linkedgroupupdatedescription=\"");
|
||||
|
||||
|
||||
#line 25 "..\..\Areas\Config\Views\Shared\LinkedGroupInstance.cshtml"
|
||||
Write(Model.ManagedGroup.Configuration.UpdateDescription);
|
||||
|
||||
|
||||
#line default
|
||||
@@ -303,22 +314,33 @@ WriteLiteral(" data-linkedgroupfilterdate=\"");
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(" data-linkedroupdescription=\"");
|
||||
WriteLiteral(" data-linkedgroupdescription=\"");
|
||||
|
||||
|
||||
#line 37 "..\..\Areas\Config\Views\Shared\LinkedGroupInstance.cshtml"
|
||||
Write(Model.CategoryDescription);
|
||||
Write(Model.CategoryDescription);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(" data-linkedroupupdateurl=\"");
|
||||
WriteLiteral(" data-linkedgroupupdateurl=\"");
|
||||
|
||||
|
||||
#line 37 "..\..\Areas\Config\Views\Shared\LinkedGroupInstance.cshtml"
|
||||
Write(Model.UpdateUrl);
|
||||
Write(Model.UpdateUrl);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(" data-linkedgroupupdatedescription=\"");
|
||||
|
||||
|
||||
#line 37 "..\..\Areas\Config\Views\Shared\LinkedGroupInstance.cshtml"
|
||||
Write(Model.ManagedGroup.Configuration.UpdateDescription);
|
||||
|
||||
|
||||
#line default
|
||||
@@ -356,28 +378,30 @@ WriteLiteral(" data-linkedgroupfilterdateoption=\"");
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(" data-linkedroupdescription=\"");
|
||||
WriteLiteral(" data-linkedgroupdescription=\"");
|
||||
|
||||
|
||||
#line 42 "..\..\Areas\Config\Views\Shared\LinkedGroupInstance.cshtml"
|
||||
Write(Model.CategoryDescription);
|
||||
Write(Model.CategoryDescription);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(" data-linkedroupupdateurl=\"");
|
||||
WriteLiteral(" data-linkedgroupupdateurl=\"");
|
||||
|
||||
|
||||
#line 42 "..\..\Areas\Config\Views\Shared\LinkedGroupInstance.cshtml"
|
||||
Write(Model.UpdateUrl);
|
||||
Write(Model.UpdateUrl);
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(" data-linkedgroupupdatedescription=\"True\"");
|
||||
|
||||
WriteLiteral(">Link Group</button>\r\n");
|
||||
|
||||
|
||||
@@ -398,14 +422,14 @@ WriteLiteral(" <div");
|
||||
|
||||
WriteLiteral(" class=\"code\"");
|
||||
|
||||
WriteAttribute("title", Tuple.Create(" title=\"", 2966), Tuple.Create("\"", 2983)
|
||||
WriteAttribute("title", Tuple.Create(" title=\"", 3193), Tuple.Create("\"", 3210)
|
||||
|
||||
#line 51 "..\..\Areas\Config\Views\Shared\LinkedGroupInstance.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 2974), Tuple.Create<System.Object, System.Int32>(group.Id
|
||||
, Tuple.Create(Tuple.Create("", 3201), Tuple.Create<System.Object, System.Int32>(group.Id
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 2974), false)
|
||||
, 3201), false)
|
||||
);
|
||||
|
||||
WriteLiteral(">\r\n <i");
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<label for="Config_LinkedGroup_Id">Linked Group:</label>
|
||||
</th>
|
||||
<td>
|
||||
<input id="Config_LinkedGroup_Id" type="text" name="GroupId" data-sourceurl="@(Url.Action(MVC.API.System.SearchGroupSubjects()))" />
|
||||
<input id="Config_LinkedGroup_Id" type="text" name="groupId" data-sourceurl="@(Url.Action(MVC.API.System.SearchGroupSubjects()))" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -20,7 +20,15 @@
|
||||
<label for="Config_LinkedGroup_FilterDate">Filter Date: </label>
|
||||
</th>
|
||||
<td>
|
||||
<input id="Config_LinkedGroup_FilterDate" type="text" name="FilterBeginDate" placeholder="No Filter" autocomplete="off" />
|
||||
<input id="Config_LinkedGroup_FilterDate" type="text" name="filterBeginDate" placeholder="No Filter" autocomplete="off" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<td>
|
||||
<input id="Config_LinkedGroup_UpdateDescriptionOff" type="hidden" name="updateDescription" value="False" disabled />
|
||||
<input id="Config_LinkedGroup_UpdateDescription" type="checkbox" name="updateDescription" value="True" checked />
|
||||
<label for="Config_LinkedGroup_UpdateDescription">Update Group Description</label>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -30,6 +38,7 @@
|
||||
<p class="fa-p">
|
||||
<i class="fa fa-exclamation-circle"></i><strong>Warning:</strong> This group will be managed by Disco ICT.<br />
|
||||
Any <strong>existing members will be removed from the group</strong>, and it will be automatically synchronized with related members.
|
||||
If the 'Update Group Description' option is selected, the group's description will also be updated to reflect its managed status.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -38,9 +47,10 @@
|
||||
let dialog;
|
||||
let dialogGroupId;
|
||||
let dialogFilterDate;
|
||||
let dialogUpdateDescription;
|
||||
let dialogTitle;
|
||||
|
||||
function showDialog(groupId, filterDateOption, filterDateValue, updateUrl, title) {
|
||||
function showDialog(groupId, filterDateOption, filterDateValue, updateUrl, title, updateDescription) {
|
||||
if (dialog == null) {
|
||||
dialog = $('#Config_LinkedGroup_Dialog').dialog({
|
||||
width: 450,
|
||||
@@ -57,6 +67,10 @@
|
||||
dateFormat: 'yy/mm/dd'
|
||||
});
|
||||
|
||||
dialogUpdateDescription = $('#Config_LinkedGroup_UpdateDescription').on('change', function () {
|
||||
$('#Config_LinkedGroup_UpdateDescriptionOff').prop('disabled', $(this).prop('checked'));
|
||||
});
|
||||
|
||||
dialogGroupId = $('#Config_LinkedGroup_Id');
|
||||
dialogGroupId.focus(function () { $(this).select(); });
|
||||
dialogGroupId.autocomplete({
|
||||
@@ -109,6 +123,8 @@
|
||||
dialogFilterDate.closest('tr').hide();
|
||||
}
|
||||
|
||||
dialogUpdateDescription.prop('checked', updateDescription);
|
||||
|
||||
dialogTitle.text(title);
|
||||
dialog.dialog('option', 'buttons', dialogButtons);
|
||||
dialog.dialog('option', 'title', 'Linked Group: ' + title);
|
||||
@@ -120,11 +136,12 @@
|
||||
|
||||
var configuredGroupId = $this.attr('data-linkedgroupid');
|
||||
var configuredFilterBeginDate = $this.attr('data-linkedgroupfilterdate');
|
||||
var filterDateOption = $this.attr('data-linkedgroupfilterdateoption') == 'True';
|
||||
var description = $this.attr('data-linkedroupdescription');
|
||||
var updateUrl = $this.attr('data-linkedroupupdateurl');
|
||||
var filterDateOption = $this.attr('data-linkedgroupfilterdateoption') === 'True';
|
||||
var description = $this.attr('data-linkedgroupdescription');
|
||||
var updateUrl = $this.attr('data-linkedgroupupdateurl');
|
||||
var updateDescription = $this.attr('data-linkedgroupupdatedescription') === 'True';
|
||||
|
||||
showDialog(configuredGroupId, filterDateOption, configuredFilterBeginDate, updateUrl, description);
|
||||
showDialog(configuredGroupId, filterDateOption, configuredFilterBeginDate, updateUrl, description, updateDescription);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -96,7 +96,7 @@ WriteLiteral(" id=\"Config_LinkedGroup_Id\"");
|
||||
|
||||
WriteLiteral(" type=\"text\"");
|
||||
|
||||
WriteLiteral(" name=\"GroupId\"");
|
||||
WriteLiteral(" name=\"groupId\"");
|
||||
|
||||
WriteLiteral(" data-sourceurl=\"");
|
||||
|
||||
@@ -121,14 +121,40 @@ WriteLiteral(" id=\"Config_LinkedGroup_FilterDate\"");
|
||||
|
||||
WriteLiteral(" type=\"text\"");
|
||||
|
||||
WriteLiteral(" name=\"FilterBeginDate\"");
|
||||
WriteLiteral(" name=\"filterBeginDate\"");
|
||||
|
||||
WriteLiteral(" placeholder=\"No Filter\"");
|
||||
|
||||
WriteLiteral(" autocomplete=\"off\"");
|
||||
|
||||
WriteLiteral(" />\r\n </td>\r\n </tr>\r\n </tbody>\r\n " +
|
||||
" </table>\r\n </form>\r\n <div");
|
||||
WriteLiteral(" />\r\n </td>\r\n </tr>\r\n <tr>\r\n " +
|
||||
" <th> </th>\r\n <td>\r\n " +
|
||||
" <input");
|
||||
|
||||
WriteLiteral(" id=\"Config_LinkedGroup_UpdateDescriptionOff\"");
|
||||
|
||||
WriteLiteral(" type=\"hidden\"");
|
||||
|
||||
WriteLiteral(" name=\"updateDescription\"");
|
||||
|
||||
WriteLiteral(" value=\"False\"");
|
||||
|
||||
WriteLiteral(" disabled />\r\n <input");
|
||||
|
||||
WriteLiteral(" id=\"Config_LinkedGroup_UpdateDescription\"");
|
||||
|
||||
WriteLiteral(" type=\"checkbox\"");
|
||||
|
||||
WriteLiteral(" name=\"updateDescription\"");
|
||||
|
||||
WriteLiteral(" value=\"True\"");
|
||||
|
||||
WriteLiteral(" checked />\r\n <label");
|
||||
|
||||
WriteLiteral(" for=\"Config_LinkedGroup_UpdateDescription\"");
|
||||
|
||||
WriteLiteral(">Update Group Description</label>\r\n </td>\r\n </t" +
|
||||
"r>\r\n </tbody>\r\n </table>\r\n </form>\r\n <div");
|
||||
|
||||
WriteLiteral(" class=\"info-box error\"");
|
||||
|
||||
@@ -142,56 +168,64 @@ WriteLiteral(" class=\"fa fa-exclamation-circle\"");
|
||||
|
||||
WriteLiteral("></i><strong>Warning:</strong> This group will be managed by Disco ICT.<br />\r\n " +
|
||||
" Any <strong>existing members will be removed from the group</strong>, " +
|
||||
"and it will be automatically synchronized with related members.\r\n </p>\r\n " +
|
||||
" </div>\r\n</div>\r\n<script>\r\n $(function () {\r\n let dialog;\r\n l" +
|
||||
"et dialogGroupId;\r\n let dialogFilterDate;\r\n let dialogTitle;\r\n\r\n " +
|
||||
" function showDialog(groupId, filterDateOption, filterDateValue, updateUrl," +
|
||||
" title) {\r\n if (dialog == null) {\r\n dialog = $(\'#Confi" +
|
||||
"g_LinkedGroup_Dialog\').dialog({\r\n width: 450,\r\n " +
|
||||
" resizable: false,\r\n modal: true,\r\n a" +
|
||||
"utoOpen: false\r\n });\r\n\r\n dialogFilterDate = $(\'#Co" +
|
||||
"nfig_LinkedGroup_FilterDate\');\r\n dialogFilterDate.datetimepicker(" +
|
||||
"{\r\n ampm: true,\r\n changeYear: true,\r\n " +
|
||||
" changeMonth: true,\r\n dateFormat: \'yy/mm/dd\'\r\n " +
|
||||
" });\r\n\r\n dialogGroupId = $(\'#Config_LinkedGroup_Id\'" +
|
||||
");\r\n dialogGroupId.focus(function () { $(this).select(); });\r\n " +
|
||||
" dialogGroupId.autocomplete({\r\n source: dialogGro" +
|
||||
"upId.attr(\'data-sourceurl\'),\r\n minLength: 2,\r\n " +
|
||||
" select: function (e, ui) {\r\n dialogGroupId.val(ui.it" +
|
||||
"em.Id);\r\n return false;\r\n }\r\n " +
|
||||
" }).data(\'ui-autocomplete\')._renderItem = function (ul, item) {\r\n " +
|
||||
" return $(\"<li>\")\r\n .data(\"item.autocomplete\", " +
|
||||
"item)\r\n .append(\"<a><strong>\" + item.Name + \"</strong><br" +
|
||||
">\" + item.Id + \" (\" + item.Type + \")</a>\")\r\n .appendTo(ul" +
|
||||
");\r\n };\r\n\r\n dialogTitle = $(\'#Config_LinkedGroup_T" +
|
||||
"itle\');\r\n }\r\n\r\n var dialogButtons = {};\r\n if (!" +
|
||||
"!groupId) {\r\n dialogButtons[\'Remove Link\'] = function () {\r\n " +
|
||||
" $(this).dialog(\'disable\');\r\n dialogGroupId.val" +
|
||||
"(\'\');\r\n dialogGroupId.closest(\'form\').attr(\'action\', updateUr" +
|
||||
"l).submit();\r\n }\r\n }\r\n dialogButtons[(!!gro" +
|
||||
"upId ? \'Save Changes\' : \'Link Group\')] = function () {\r\n if (!dia" +
|
||||
"logGroupId.val()) {\r\n alert(\'A Linked Group must be specified" +
|
||||
"\');\r\n return;\r\n }\r\n $(this).dia" +
|
||||
"log(\'disable\');\r\n dialogGroupId.closest(\'form\').attr(\'action\', up" +
|
||||
"dateUrl).submit();\r\n }\r\n dialogButtons[\'Cancel\'] = functio" +
|
||||
"n () {\r\n $(this).dialog(\'close\');\r\n };\r\n\r\n " +
|
||||
"dialogGroupId.val(groupId);\r\n\r\n if (!!filterDateOption) {\r\n " +
|
||||
" if (!!filterDateValue) {\r\n dialogFilterDate.datetimepic" +
|
||||
"ker(\'setDate\', moment(filterDateValue).toDate());\r\n } else {\r\n " +
|
||||
" dialogFilterDate.val(\'\');\r\n }\r\n d" +
|
||||
"ialogFilterDate.closest(\'tr\').show();\r\n } else {\r\n dia" +
|
||||
"logFilterDate.closest(\'tr\').hide();\r\n }\r\n\r\n dialogTitle.te" +
|
||||
"xt(title);\r\n dialog.dialog(\'option\', \'buttons\', dialogButtons);\r\n " +
|
||||
" dialog.dialog(\'option\', \'title\', \'Linked Group: \' + title);\r\n " +
|
||||
" dialog.dialog(\'open\');\r\n }\r\n\r\n $(document).on(\'click\', \'.Config_L" +
|
||||
"inkedGroup_LinkButton\', function () {\r\n $this = $(this);\r\n\r\n " +
|
||||
" var configuredGroupId = $this.attr(\'data-linkedgroupid\');\r\n var co" +
|
||||
"nfiguredFilterBeginDate = $this.attr(\'data-linkedgroupfilterdate\');\r\n " +
|
||||
" var filterDateOption = $this.attr(\'data-linkedgroupfilterdateoption\') == \'True\'" +
|
||||
";\r\n var description = $this.attr(\'data-linkedroupdescription\');\r\n " +
|
||||
" var updateUrl = $this.attr(\'data-linkedroupupdateurl\');\r\n\r\n s" +
|
||||
"howDialog(configuredGroupId, filterDateOption, configuredFilterBeginDate, update" +
|
||||
"Url, description);\r\n\r\n return false;\r\n });\r\n });\r\n</script>" +
|
||||
"and it will be automatically synchronized with related members.\r\n If " +
|
||||
"the \'Update Group Description\' option is selected, the group\'s description will " +
|
||||
"also be updated to reflect its managed status.\r\n </p>\r\n </div>\r\n</div>" +
|
||||
"\r\n<script>\r\n $(function () {\r\n let dialog;\r\n let dialogGroupId;" +
|
||||
"\r\n let dialogFilterDate;\r\n let dialogUpdateDescription;\r\n l" +
|
||||
"et dialogTitle;\r\n\r\n function showDialog(groupId, filterDateOption, filter" +
|
||||
"DateValue, updateUrl, title, updateDescription) {\r\n if (dialog == nul" +
|
||||
"l) {\r\n dialog = $(\'#Config_LinkedGroup_Dialog\').dialog({\r\n " +
|
||||
" width: 450,\r\n resizable: false,\r\n " +
|
||||
" modal: true,\r\n autoOpen: false\r\n });\r\n\r\n " +
|
||||
" dialogFilterDate = $(\'#Config_LinkedGroup_FilterDate\');\r\n " +
|
||||
" dialogFilterDate.datetimepicker({\r\n ampm: true,\r\n " +
|
||||
" changeYear: true,\r\n changeMonth: true,\r\n " +
|
||||
" dateFormat: \'yy/mm/dd\'\r\n });\r\n\r\n di" +
|
||||
"alogUpdateDescription = $(\'#Config_LinkedGroup_UpdateDescription\').on(\'change\', " +
|
||||
"function () {\r\n $(\'#Config_LinkedGroup_UpdateDescriptionOff\')" +
|
||||
".prop(\'disabled\', $(this).prop(\'checked\'));\r\n });\r\n\r\n " +
|
||||
" dialogGroupId = $(\'#Config_LinkedGroup_Id\');\r\n dialogGroupId." +
|
||||
"focus(function () { $(this).select(); });\r\n dialogGroupId.autocom" +
|
||||
"plete({\r\n source: dialogGroupId.attr(\'data-sourceurl\'),\r\n " +
|
||||
" minLength: 2,\r\n select: function (e, ui) {\r\n " +
|
||||
" dialogGroupId.val(ui.item.Id);\r\n r" +
|
||||
"eturn false;\r\n }\r\n }).data(\'ui-autocomplete\')." +
|
||||
"_renderItem = function (ul, item) {\r\n return $(\"<li>\")\r\n " +
|
||||
" .data(\"item.autocomplete\", item)\r\n .ap" +
|
||||
"pend(\"<a><strong>\" + item.Name + \"</strong><br>\" + item.Id + \" (\" + item.Type + " +
|
||||
"\")</a>\")\r\n .appendTo(ul);\r\n };\r\n\r\n " +
|
||||
" dialogTitle = $(\'#Config_LinkedGroup_Title\');\r\n }\r\n\r\n " +
|
||||
" var dialogButtons = {};\r\n if (!!groupId) {\r\n dialo" +
|
||||
"gButtons[\'Remove Link\'] = function () {\r\n $(this).dialog(\'dis" +
|
||||
"able\');\r\n dialogGroupId.val(\'\');\r\n dialogG" +
|
||||
"roupId.closest(\'form\').attr(\'action\', updateUrl).submit();\r\n }\r\n " +
|
||||
" }\r\n dialogButtons[(!!groupId ? \'Save Changes\' : \'Link Grou" +
|
||||
"p\')] = function () {\r\n if (!dialogGroupId.val()) {\r\n " +
|
||||
" alert(\'A Linked Group must be specified\');\r\n return;\r\n" +
|
||||
" }\r\n $(this).dialog(\'disable\');\r\n d" +
|
||||
"ialogGroupId.closest(\'form\').attr(\'action\', updateUrl).submit();\r\n }\r" +
|
||||
"\n dialogButtons[\'Cancel\'] = function () {\r\n $(this).di" +
|
||||
"alog(\'close\');\r\n };\r\n\r\n dialogGroupId.val(groupId);\r\n\r\n " +
|
||||
" if (!!filterDateOption) {\r\n if (!!filterDateValue) {\r\n " +
|
||||
" dialogFilterDate.datetimepicker(\'setDate\', moment(filterDateVa" +
|
||||
"lue).toDate());\r\n } else {\r\n dialogFilterDate." +
|
||||
"val(\'\');\r\n }\r\n dialogFilterDate.closest(\'tr\').show" +
|
||||
"();\r\n } else {\r\n dialogFilterDate.closest(\'tr\').hide()" +
|
||||
";\r\n }\r\n\r\n dialogUpdateDescription.prop(\'checked\', updateDe" +
|
||||
"scription);\r\n\r\n dialogTitle.text(title);\r\n dialog.dialog(\'" +
|
||||
"option\', \'buttons\', dialogButtons);\r\n dialog.dialog(\'option\', \'title\'" +
|
||||
", \'Linked Group: \' + title);\r\n dialog.dialog(\'open\');\r\n }\r\n\r\n " +
|
||||
" $(document).on(\'click\', \'.Config_LinkedGroup_LinkButton\', function () {\r\n" +
|
||||
" $this = $(this);\r\n\r\n var configuredGroupId = $this.attr(\'" +
|
||||
"data-linkedgroupid\');\r\n var configuredFilterBeginDate = $this.attr(\'d" +
|
||||
"ata-linkedgroupfilterdate\');\r\n var filterDateOption = $this.attr(\'dat" +
|
||||
"a-linkedgroupfilterdateoption\') === \'True\';\r\n var description = $this" +
|
||||
".attr(\'data-linkedgroupdescription\');\r\n var updateUrl = $this.attr(\'d" +
|
||||
"ata-linkedgroupupdateurl\');\r\n var updateDescription = $this.attr(\'dat" +
|
||||
"a-linkedgroupupdatedescription\') === \'True\';\r\n\r\n showDialog(configure" +
|
||||
"dGroupId, filterDateOption, configuredFilterBeginDate, updateUrl, description, u" +
|
||||
"pdateDescription);\r\n\r\n return false;\r\n });\r\n });\r\n</script>" +
|
||||
"\r\n");
|
||||
|
||||
}
|
||||
|
||||
@@ -11,45 +11,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<iframe name="callbackFrame" class="hidden">
|
||||
</iframe>
|
||||
|
||||
<form id="callbackSubmit" action="@Model.CallbackUrl" method="post" target="callbackFrame">
|
||||
<input type="hidden" name="callbackUrl" value="@(new Uri(Request.Url, Url.Action(MVC.API.Activation.TestCallback())))" />
|
||||
<input type="hidden" name="deploymentId" value="@Model.DeploymentId" />
|
||||
<form id="callbackSubmit" action="@Model.CallbackUrl" method="post" data-failedurl="@Url.Action(MVC.Config.SystemConfig.Index())">
|
||||
<input type="hidden" name="correlationId" value="@Model.CorrelationId" />
|
||||
<input type="hidden" name="deploymentId" value="@Model.DeploymentId" />
|
||||
<input type="hidden" name="timestamp" value="@Model.Timestamp" />
|
||||
<input type="hidden" name="proof" value="@Model.Proof" />
|
||||
<input type="hidden" name="userId" value="@Model.UserId" />
|
||||
<input type="hidden" name="callbackUrl" value="@(new Uri(Request.Url, Url.Action(MVC.API.Activation.Begin())))" />
|
||||
</form>
|
||||
|
||||
@using (Html.BeginForm(MVC.API.Activation.Begin(), FormMethod.Post, new { id = "activationBegin"}))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
}
|
||||
|
||||
<a id="callbackFailedUrl" href="@Url.Action(MVC.Config.SystemConfig.Index())" class="hidden"></a>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
const callbackForm = $('#callbackSubmit');
|
||||
const callbackFailedUrl = $('#callbackFailedUrl').attr('href');
|
||||
const callbackFailedUrl = callbackForm.attr('data-failedurl');
|
||||
const timeout = window.setTimeout(function () {
|
||||
alert('A timeout occurred while communicating with Online Services. Please try a different device/browser or try again later.');
|
||||
window.location.href = callbackFailedUrl;
|
||||
}, 1000 * 35);
|
||||
window.activateCallbackResponse = function (deploymentId, correlationId, userId) {
|
||||
window.clearTimeout(timeout);
|
||||
|
||||
const originalDeploymentId = callbackForm.find('input[name="deploymentId"]').val();
|
||||
const originalCorrelationId = callbackForm.find('input[name="correlationId"]').val();
|
||||
const originalUserId = callbackForm.find('input[name="userId"]').val();
|
||||
|
||||
if (deploymentId !== originalDeploymentId || correlationId !== originalCorrelationId || userId !== originalUserId) {
|
||||
alert('Invalid response when communicating with Online Services. Please try a different device/browser or try again later.');
|
||||
window.location.href = callbackFailedUrl;
|
||||
}
|
||||
|
||||
$('#activationBegin').trigger('submit');
|
||||
};
|
||||
}, 1000 * 18);
|
||||
callbackForm.trigger('submit');
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -68,44 +68,47 @@ WriteLiteral(">\r\n <h2><i");
|
||||
WriteLiteral(" class=\"fa fa-lg fa-cog fa-spin\"");
|
||||
|
||||
WriteLiteral("></i> Testing Connectivity to Disco ICT Online Services</h2>\r\n </div>\r\n</div>\r" +
|
||||
"\n\r\n<iframe");
|
||||
|
||||
WriteLiteral(" name=\"callbackFrame\"");
|
||||
|
||||
WriteLiteral(" class=\"hidden\"");
|
||||
|
||||
WriteLiteral(">\r\n</iframe>\r\n\r\n<form");
|
||||
"\n\r\n<form");
|
||||
|
||||
WriteLiteral(" id=\"callbackSubmit\"");
|
||||
|
||||
WriteAttribute("action", Tuple.Create(" action=\"", 563), Tuple.Create("\"", 590)
|
||||
WriteAttribute("action", Tuple.Create(" action=\"", 504), Tuple.Create("\"", 531)
|
||||
|
||||
#line 17 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 572), Tuple.Create<System.Object, System.Int32>(Model.CallbackUrl
|
||||
#line 14 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 513), Tuple.Create<System.Object, System.Int32>(Model.CallbackUrl
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 572), false)
|
||||
, 513), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" method=\"post\"");
|
||||
|
||||
WriteLiteral(" target=\"callbackFrame\"");
|
||||
WriteLiteral(" data-failedurl=\"");
|
||||
|
||||
|
||||
#line 14 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
Write(Url.Action(MVC.Config.SystemConfig.Index()));
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\"");
|
||||
|
||||
WriteLiteral(">\r\n <input");
|
||||
|
||||
WriteLiteral(" type=\"hidden\"");
|
||||
|
||||
WriteLiteral(" name=\"callbackUrl\"");
|
||||
WriteLiteral(" name=\"correlationId\"");
|
||||
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 674), Tuple.Create("\"", 752)
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 656), Tuple.Create("\"", 684)
|
||||
|
||||
#line 18 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 682), Tuple.Create<System.Object, System.Int32>(new Uri(Request.Url, Url.Action(MVC.API.Activation.TestCallback()))
|
||||
#line 15 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 664), Tuple.Create<System.Object, System.Int32>(Model.CorrelationId
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 682), false)
|
||||
, 664), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" />\r\n <input");
|
||||
@@ -114,30 +117,46 @@ WriteLiteral(" type=\"hidden\"");
|
||||
|
||||
WriteLiteral(" name=\"deploymentId\"");
|
||||
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 802), Tuple.Create("\"", 829)
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 734), Tuple.Create("\"", 761)
|
||||
|
||||
#line 19 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 810), Tuple.Create<System.Object, System.Int32>(Model.DeploymentId
|
||||
#line 16 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 742), Tuple.Create<System.Object, System.Int32>(Model.DeploymentId
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 810), false)
|
||||
, 742), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" />\r\n <input");
|
||||
|
||||
WriteLiteral(" type=\"hidden\"");
|
||||
|
||||
WriteLiteral(" name=\"correlationId\"");
|
||||
WriteLiteral(" name=\"timestamp\"");
|
||||
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 880), Tuple.Create("\"", 908)
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 808), Tuple.Create("\"", 832)
|
||||
|
||||
#line 20 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 888), Tuple.Create<System.Object, System.Int32>(Model.CorrelationId
|
||||
#line 17 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 816), Tuple.Create<System.Object, System.Int32>(Model.Timestamp
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 888), false)
|
||||
, 816), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" />\r\n <input");
|
||||
|
||||
WriteLiteral(" type=\"hidden\"");
|
||||
|
||||
WriteLiteral(" name=\"proof\"");
|
||||
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 875), Tuple.Create("\"", 895)
|
||||
|
||||
#line 18 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 883), Tuple.Create<System.Object, System.Int32>(Model.Proof
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 883), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" />\r\n <input");
|
||||
@@ -146,81 +165,43 @@ WriteLiteral(" type=\"hidden\"");
|
||||
|
||||
WriteLiteral(" name=\"userId\"");
|
||||
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 952), Tuple.Create("\"", 973)
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 939), Tuple.Create("\"", 960)
|
||||
|
||||
#line 21 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 960), Tuple.Create<System.Object, System.Int32>(Model.UserId
|
||||
#line 19 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 947), Tuple.Create<System.Object, System.Int32>(Model.UserId
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 960), false)
|
||||
, 947), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" />\r\n</form>\r\n\r\n");
|
||||
WriteLiteral(" />\r\n <input");
|
||||
|
||||
WriteLiteral(" type=\"hidden\"");
|
||||
|
||||
WriteLiteral(" name=\"callbackUrl\"");
|
||||
|
||||
WriteAttribute("value", Tuple.Create(" value=\"", 1009), Tuple.Create("\"", 1080)
|
||||
|
||||
#line 24 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
using (Html.BeginForm(MVC.API.Activation.Begin(), FormMethod.Post, new { id = "activationBegin"}))
|
||||
{
|
||||
|
||||
#line 20 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 1017), Tuple.Create<System.Object, System.Int32>(new Uri(Request.Url, Url.Action(MVC.API.Activation.Begin()))
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 26 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
Write(Html.AntiForgeryToken());
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 26 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("\r\n<a");
|
||||
|
||||
WriteLiteral(" id=\"callbackFailedUrl\"");
|
||||
|
||||
WriteAttribute("href", Tuple.Create(" href=\"", 1154), Tuple.Create("\"", 1205)
|
||||
|
||||
#line 29 "..\..\Areas\Config\Views\SystemConfig\Activate.cshtml"
|
||||
, Tuple.Create(Tuple.Create("", 1161), Tuple.Create<System.Object, System.Int32>(Url.Action(MVC.Config.SystemConfig.Index())
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
, 1161), false)
|
||||
, 1017), false)
|
||||
);
|
||||
|
||||
WriteLiteral(" class=\"hidden\"");
|
||||
|
||||
WriteLiteral(@"></a>
|
||||
WriteLiteral(@" />
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
const callbackForm = $('#callbackSubmit');
|
||||
const callbackFailedUrl = $('#callbackFailedUrl').attr('href');
|
||||
const callbackFailedUrl = callbackForm.attr('data-failedurl');
|
||||
const timeout = window.setTimeout(function () {
|
||||
alert('A timeout occurred while communicating with Online Services. Please try a different device/browser or try again later.');
|
||||
window.location.href = callbackFailedUrl;
|
||||
}, 1000 * 35);
|
||||
window.activateCallbackResponse = function (deploymentId, correlationId, userId) {
|
||||
window.clearTimeout(timeout);
|
||||
|
||||
const originalDeploymentId = callbackForm.find('input[name=""deploymentId""]').val();
|
||||
const originalCorrelationId = callbackForm.find('input[name=""correlationId""]').val();
|
||||
const originalUserId = callbackForm.find('input[name=""userId""]').val();
|
||||
|
||||
if (deploymentId !== originalDeploymentId || correlationId !== originalCorrelationId || userId !== originalUserId) {
|
||||
alert('Invalid response when communicating with Online Services. Please try a different device/browser or try again later.');
|
||||
window.location.href = callbackFailedUrl;
|
||||
}
|
||||
|
||||
$('#activationBegin').trigger('submit');
|
||||
};
|
||||
}, 1000 * 18);
|
||||
callbackForm.trigger('submit');
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Jobs.Noticeboards;
|
||||
using Disco.Services.Web;
|
||||
using Disco.Web.Areas.Public.Models.UserHeldDevices;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
|
||||
@@ -10,30 +10,53 @@ namespace Disco.Web.Areas.Public.Controllers
|
||||
{
|
||||
public partial class HeldDevicesController : DatabaseController
|
||||
{
|
||||
public virtual ActionResult Index(List<int?> DeviceProfileInclude, List<int?> DeviceProfileExclude, List<string> DeviceAddressInclude, List<string> DeviceAddressExclude)
|
||||
public virtual ActionResult Index(string DeviceProfileInclude, string DeviceProfileExclude, string DeviceAddressInclude, string DeviceAddressExclude, string JobQueueInclude, string JobQueueExclude)
|
||||
{
|
||||
IQueryable<Job> query = Database.Jobs;
|
||||
|
||||
if (DeviceProfileInclude != null)
|
||||
query = query.Where(j => DeviceProfileInclude.Contains(j.Device.DeviceProfileId));
|
||||
if (DeviceProfileExclude != null)
|
||||
query = query.Where(j => !DeviceProfileExclude.Contains(j.Device.DeviceProfileId));
|
||||
if (DeviceAddressInclude != null && DeviceAddressInclude.Count > 0)
|
||||
{
|
||||
var addressIds = Database.DiscoConfiguration.OrganisationAddresses.Addresses.Where(a => DeviceAddressInclude.Contains(a.ShortName)).Select(a => a.Id).ToList();
|
||||
query = query.Where(j => addressIds.Contains(j.Device.DeviceProfile.DefaultOrganisationAddress));
|
||||
}
|
||||
if (DeviceAddressExclude != null && DeviceAddressExclude.Count > 0)
|
||||
{
|
||||
var addressIds = Database.DiscoConfiguration.OrganisationAddresses.Addresses.Where(a => DeviceAddressExclude.Contains(a.ShortName)).Select(a => (int?)a.Id).ToList();
|
||||
query = query.Where(j => j.Device.DeviceProfile.DefaultOrganisationAddress == null || !addressIds.Contains(j.Device.DeviceProfile.DefaultOrganisationAddress));
|
||||
}
|
||||
var query = FilterJobs(Database.Jobs, Database, DeviceProfileInclude, DeviceProfileExclude, DeviceAddressInclude, DeviceAddressExclude, JobQueueInclude, JobQueueExclude);
|
||||
|
||||
var m = Disco.Services.Jobs.Noticeboards.HeldDevices.GetHeldDevices(query);
|
||||
|
||||
return View(m);
|
||||
}
|
||||
|
||||
internal static IQueryable<Job> FilterJobs(IQueryable<Job> query, DiscoDataContext database, string deviceProfileInclude, string deviceProfileExclude, string deviceAddressInclude, string deviceAddressExclude, string jobQueueInclude, string jobQueueExclude)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(deviceProfileInclude))
|
||||
{
|
||||
var include = deviceProfileInclude.Split(',').Select(int.Parse).ToList();
|
||||
query = query.Where(j => include.Contains(j.Device.DeviceProfileId));
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(deviceProfileExclude))
|
||||
{
|
||||
var exclude = deviceProfileExclude.Split(',').Select(int.Parse).ToList();
|
||||
query = query.Where(j => !exclude.Contains(j.Device.DeviceProfileId));
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(deviceAddressInclude))
|
||||
{
|
||||
var include = deviceAddressInclude.Split(',');
|
||||
var addressIds = database.DiscoConfiguration.OrganisationAddresses.Addresses.Where(a => include.Contains(a.ShortName)).Select(a => a.Id).ToList();
|
||||
query = query.Where(j => addressIds.Contains(j.Device.DeviceProfile.DefaultOrganisationAddress));
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(deviceAddressExclude))
|
||||
{
|
||||
var exclude = deviceAddressExclude.Split(',');
|
||||
var addressIds = database.DiscoConfiguration.OrganisationAddresses.Addresses.Where(a => exclude.Contains(a.ShortName)).Select(a => (int?)a.Id).ToList();
|
||||
query = query.Where(j => j.Device.DeviceProfile.DefaultOrganisationAddress == null || !addressIds.Contains(j.Device.DeviceProfile.DefaultOrganisationAddress));
|
||||
}
|
||||
if (jobQueueInclude != null)
|
||||
{
|
||||
var include = jobQueueInclude.Split(',').Select(int.Parse).ToList();
|
||||
query = query.Where(j => j.JobQueues.Any(q => q.RemovedDate == null && include.Contains(q.JobQueueId)));
|
||||
}
|
||||
if (jobQueueExclude != null)
|
||||
{
|
||||
var exclude = jobQueueExclude.Split(',').Select(int.Parse).ToList();
|
||||
query = query.Where(j => !j.JobQueues.Any(q => q.RemovedDate == null && exclude.Contains(q.JobQueueId)));
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
public virtual ActionResult ReadyForReturnXml()
|
||||
{
|
||||
var readyForReturn = Disco.Services.Jobs.Noticeboards.HeldDevices.GetHeldDevices(Database)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Jobs.Noticeboards;
|
||||
using Disco.Services.Jobs.Noticeboards;
|
||||
using Disco.Services.Web;
|
||||
using Disco.Web.Areas.Public.Models.UserHeldDevices;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
|
||||
@@ -10,24 +8,9 @@ namespace Disco.Web.Areas.Public.Controllers
|
||||
{
|
||||
public partial class UserHeldDevicesController : DatabaseController
|
||||
{
|
||||
public virtual ActionResult Index(List<int?> DeviceProfileInclude, List<int?> DeviceProfileExclude, List<string> DeviceAddressInclude, List<string> DeviceAddressExclude)
|
||||
public virtual ActionResult Index(string DeviceProfileInclude, string DeviceProfileExclude, string DeviceAddressInclude, string DeviceAddressExclude, string JobQueueInclude, string JobQueueExclude)
|
||||
{
|
||||
IQueryable<Job> query = Database.Jobs;
|
||||
|
||||
if (DeviceProfileInclude != null)
|
||||
query = query.Where(j => DeviceProfileInclude.Contains(j.Device.DeviceProfileId));
|
||||
if (DeviceProfileExclude != null)
|
||||
query = query.Where(j => !DeviceProfileExclude.Contains(j.Device.DeviceProfileId));
|
||||
if (DeviceAddressInclude != null && DeviceAddressInclude.Count > 0)
|
||||
{
|
||||
var addressIds = Database.DiscoConfiguration.OrganisationAddresses.Addresses.Where(a => DeviceAddressInclude.Contains(a.ShortName)).Select(a => a.Id).ToList();
|
||||
query = query.Where(j => addressIds.Contains(j.Device.DeviceProfile.DefaultOrganisationAddress));
|
||||
}
|
||||
if (DeviceAddressExclude != null && DeviceAddressExclude.Count > 0)
|
||||
{
|
||||
var addressIds = Database.DiscoConfiguration.OrganisationAddresses.Addresses.Where(a => DeviceAddressExclude.Contains(a.ShortName)).Select(a => (int?)a.Id).ToList();
|
||||
query = query.Where(j => j.Device.DeviceProfile.DefaultOrganisationAddress == null || !addressIds.Contains(j.Device.DeviceProfile.DefaultOrganisationAddress));
|
||||
}
|
||||
var query = HeldDevicesController.FilterJobs(Database.Jobs, Database, DeviceProfileInclude, DeviceProfileExclude, DeviceAddressInclude, DeviceAddressExclude, JobQueueInclude, JobQueueExclude);
|
||||
|
||||
var m = HeldDevicesForUsers.GetHeldDevicesForUsers(query);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
{
|
||||
<tr>
|
||||
<td class="id">
|
||||
@item.DeviceComputerNameFriendly
|
||||
@item.DeviceName
|
||||
</td>
|
||||
<td class="description">
|
||||
@if (item.UserId != null)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user