/// <summary>
        /// Implements the Aegis 'create' verb.
        /// </summary>
        /// <param name="options">The verb options.</param>
        /// <returns>Whether or not the operation was handled.</returns>
        private bool CreateVerb(CreateVerbOptions options)
        {
            if (string.IsNullOrWhiteSpace(options.AegisArchivePath))
            {
                throw new AegisUserErrorException("The archive path parameter is required for this operation.");
            }

            // Add the canonical "ags" extension to the new archive, if it's not already part of the path.
            var newArchivePath = options.AegisArchivePath.EndsWith(
                $".{AegisConstants.CanonicalSecureArchiveFileExtension}",
                StringComparison.OrdinalIgnoreCase)
                    ? options.AegisArchivePath
                    : $"{options.AegisArchivePath}.{AegisConstants.CanonicalSecureArchiveFileExtension}";

            var archiveFileSettings = new SecureArchiveFileSettings(newArchivePath, this.TempDirectory);

            try
            {
                if (File.Exists(archiveFileSettings.Path))
                {
                    if (options.Force)
                    {
                        // Get rid of the pre-existing archive file first.
                        File.Delete(archiveFileSettings.Path);
                    }
                    else
                    {
                        throw new AegisUserErrorException(
                                  "A file exists at the specified location. Use --force flag to overwrite.");
                    }
                }

                using var firstUserKeyAuthorization = this.ArchiveUnlocker.GetNewUserKeyAuthorizationParams();
                using var createParameters          = new SecureArchiveCreationParameters(firstUserKeyAuthorization);

                using var archive = AegisArchive.CreateNew(archiveFileSettings, createParameters);

                var newArchiveFileInfo = new FileInfo(archive.FullFilePath);
                this.IO.Out.WriteLine($"Created new secure archive file '{newArchiveFileInfo.FullName}'.");
            }
            catch (IOException e)
            {
                throw new AegisUserErrorException($"Unable to write to {archiveFileSettings.Path}.", innerException: e);
            }

            return(true);
        }
Example #2
0
        /// <summary>
        /// Helper that opens an <see cref="AegisArchive"/> from disk.
        /// </summary>
        /// <param name="archivePath">The path to the <see cref="AegisArchive"/> on disk.</param>
        /// <param name="tempDirectory">The temp directory that the archive can use for operations.</param>
        /// <param name="archiveUnlocker">The archive unlock helper. If null, archive will be opened but left unlocked.</param>
        /// <returns>The opened <see cref="AegisArchive"/>.</returns>
        private static AegisArchive OpenArchive(string archivePath, string tempDirectory, ArchiveUnlocker archiveUnlocker)
        {
            if (string.IsNullOrWhiteSpace(archivePath))
            {
                throw new AegisUserErrorException(
                          "Specify the path to the Aegis archive to open. Use the '-a' option or check the 'open' command help for details.");
            }

            AegisArchive archive     = null;
            var          openSuccess = false;

            try
            {
                var archiveFileSettings = new SecureArchiveFileSettings(archivePath, tempDirectory);
                archive = AegisArchive.Load(archiveFileSettings);

                if (archiveUnlocker != null)
                {
                    UnlockArchive(archive, archiveUnlocker);
                }

                openSuccess = true;
            }
            catch (IOException e)
            {
                throw new AegisUserErrorException(
                          $"Unable to read file at {archivePath}.",
                          innerException: e);
            }
            catch (ArchiveCorruptedException e)
            {
                throw new AegisUserErrorException(
                          $"The archive file at {archivePath} is corrupted: {e.Message}",
                          innerException: e);
            }
            finally
            {
                if (!openSuccess)
                {
                    // Make sure to release any holds if we couldn't successfully open the archive.
                    archive?.Dispose();
                }
            }

            return(archive);
        }
Example #3
0
 /// <summary>
 /// Helper that unlocks the <see cref="AegisArchive"/> using the given <see cref="ArchiveUnlocker"/>.
 /// </summary>
 /// <param name="archive">The archive to unlock.</param>
 /// <param name="unlocker">The unlocking helper.</param>
 private static void UnlockArchive(AegisArchive archive, ArchiveUnlocker unlocker)
 {
     try
     {
         unlocker.Unlock(archive);
     }
     catch (ArchiveCorruptedException e)
     {
         throw new AegisUserErrorException(
                   $"The archive file at {archive.FullFilePath} is corrupted: {e.Message}",
                   innerException: e);
     }
     catch (UnauthorizedException e)
     {
         throw new AegisUserErrorException(
                   $"The key was not able to unlock the archive.",
                   innerException: e);
     }
 }
Example #4
0
        /// <summary>
        /// Prompts the user for their user key and uses it to unlock the given archive.
        /// </summary>
        /// <param name="archive">The archive to unlock.</param>
        public void Unlock(AegisArchive archive)
        {
            ArgCheck.NotNull(archive, nameof(archive));

            if (!archive.IsLocked)
            {
                throw new InvalidOperationException("The given archive is already unlocked.");
            }

            // First, filter down the user key authorizations to only keep those for which we have a registered interface.
            var availableUserKeys = archive.GetUserKeyAuthorizations()
                                    .Where(key =>
                                           this.SecretEntryInterfaces.ContainsKey(key.SecretMetadata.SecretKind) &&
                                           this.SecretEntryInterfaces[key.SecretMetadata.SecretKind].CanProvideSecret(key.SecretMetadata));

            if (!availableUserKeys.Any())
            {
                throw new NoKeyAvailableException(
                          "None of the authorized user keys are currently available because this interface does not support them.");
            }

            // Of those, ask the user to choose which type of secret they'd like to use to unlock the archive.
            var availableSecretKinds = availableUserKeys.Select(k => k.SecretMetadata.SecretKind).Distinct().ToImmutableArray();
            var selectedSecretKind   = availableSecretKinds.Length > 1
                ? this.SecretSelector.PromptSelectSecretKind(availableSecretKinds)
                : availableSecretKinds[0];

            // Split out the user keys that are available, and of the selected type.
            var selectedUserKeys = availableUserKeys
                                   .Where(k => k.SecretMetadata.SecretKind == selectedSecretKind)
                                   .Select(k => k.SecretMetadata)
                                   .ToImmutableArray();

            // Finally, prompt to get the user secret and use it to unlock the archive.
            using var userSecret = this.SecretEntryInterfaces[selectedSecretKind].GetUserSecret(selectedUserKeys);
            archive.Unlock(userSecret);
        }