Dracoon.psm1
$script:ModuleRoot = $PSScriptRoot $script:ModuleVersion = (Import-PowerShellDataFile -Path "$($script:ModuleRoot)\Dracoon.psd1").ModuleVersion # Detect whether at some level dotsourcing was enforced $script:doDotSource = Get-PSFConfigValue -FullName Dracoon.Import.DoDotSource -Fallback $false if ($Dracoon_dotsourcemodule) { $script:doDotSource = $true } <# Note on Resolve-Path: All paths are sent through Resolve-Path/Resolve-PSFPath in order to convert them to the correct path separator. This allows ignoring path separators throughout the import sequence, which could otherwise cause trouble depending on OS. Resolve-Path can only be used for paths that already exist, Resolve-PSFPath can accept that the last leaf my not exist. This is important when testing for paths. #> # Detect whether at some level loading individual module files, rather than the compiled module was enforced $importIndividualFiles = Get-PSFConfigValue -FullName Dracoon.Import.IndividualFiles -Fallback $false if ($Dracoon_importIndividualFiles) { $importIndividualFiles = $true } if (Test-Path (Resolve-PSFPath -Path "$($script:ModuleRoot)\..\.git" -SingleItem -NewChild)) { $importIndividualFiles = $true } if ("<was compiled>" -eq '<was not compiled>') { $importIndividualFiles = $true } function Import-ModuleFile { <# .SYNOPSIS Loads files into the module on module import. .DESCRIPTION This helper function is used during module initialization. It should always be dotsourced itself, in order to proper function. This provides a central location to react to files being imported, if later desired .PARAMETER Path The path to the file to load .EXAMPLE PS C:\> . Import-ModuleFile -File $function.FullName Imports the file stored in $function according to import policy #> [CmdletBinding()] Param ( [string] $Path ) $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($Path).ProviderPath if ($doDotSource) { . $resolvedPath } else { $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText($resolvedPath))), $null, $null) } } #region Load individual files if ($importIndividualFiles) { # Execute Preimport actions foreach ($path in (& "$ModuleRoot\internal\scripts\preimport.ps1")) { . Import-ModuleFile -Path $path } # Import all internal functions foreach ($function in (Get-ChildItem "$ModuleRoot\internal\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore)) { . Import-ModuleFile -Path $function.FullName } # Import all public functions foreach ($function in (Get-ChildItem "$ModuleRoot\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore)) { . Import-ModuleFile -Path $function.FullName } # Execute Postimport actions foreach ($path in (& "$ModuleRoot\internal\scripts\postimport.ps1")) { . Import-ModuleFile -Path $path } # End it here, do not load compiled code below return } #endregion Load individual files #region Load compiled code <# This file loads the strings documents from the respective language folders. This allows localizing messages and errors. Load psd1 language files for each language you wish to support. Partial translations are acceptable - when missing a current language message, it will fallback to English or another available language. #> Import-PSFLocalizedString -Path "$($script:ModuleRoot)\en-us\*.psd1" -Module 'Dracoon' -Language 'en-US' import-module PSFramework Class Dracoon { hidden [string]$webServiceRoot [string]$serverRoot hidden static [string]$dateFormat = 'yyyy-MM-dd' hidden static [int]$maxRecursionLevelSSP = 50 hidden [int]$homeRoomParentId hidden [int]$projectsRoomParentId hidden [int]$openIDConfigId = 4 hidden [int]$adminGroupId#=6 hidden [String] $authToken [String] $authenticatedUser [switch] $verbose $oauthToken_Expires_in $headers = @{ } Dracoon ([PSCredential]$credentials, [string]$baseUrl) { # [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 Write-PSFMessage "Verbinde Dracoon $baseUrl mittels Credentials-Objekt" $this.serverRoot = Get-DracoonServerRoot $baseUrl $this.webServiceRoot = "$($this.serverRoot)/api" $this.Login($credentials) } Dracoon ([String]$AccessToken, [string]$baseUrl) { # [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 Write-PSFMessage "Verbinde Dracoon $baseUrl mittels AccessToken $AccessToken" -functionName "[Dracoon]::new(AccessToken, baseUrl)" $this.serverRoot = Get-DracoonServerRoot $baseUrl $this.webServiceRoot = "$($this.serverRoot)/api" $this.Login($AccessToken) } hidden Login([PSCredential]$credentials) { # [void][System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions") # $this.jsonserial = New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer # $this.jsonserial.MaxJsonLength = 2147483647 $parameter = @{login = $credentials.UserName; password = $credentials.GetNetworkCredential().Password; language = "1"; authType = "sql" } $result = Invoke-DracoonAPI -connection $this -path "/v4/auth/login" -body $parameter -method Post -hideParameters $true Write-PSFMessage $result $this.authToken = $result.token $this.authenticatedUser = $credentials.UserName $this.headers.Add("X-Sds-Auth-Token", $this.authToken) Write-PSFMessage "This-Type: $($this.gettype())" } hidden Login([string]$AccessToken) { Write-PSFMessage "Login with AccessToken <$AccessToken>" -Level Debug if ($AccessToken){ $this.authToken = "Bearer $AccessToken" #$this.oauthToken_Expires_in = $tokenContent.expires_in $this.authenticatedUser = "OAuth" $this.headers.Add("Authorization", $this.authToken) }else{throw "AccessToken is null"} } # [PSCustomObject]GetGroups([string]$filter) { # $groups = $this.InvokeGet("/v4/groups?filter=$filter") # return $groups.items # } # [PSCustomObject]GetGroups() { # return $this.GetGroups("") # } # [PSCustomObject]GetGroupMembers([int]$groupId, [string]$filter) { # $groups = $this.InvokeGet("/v4/groups/$groupId/users?filter=$filter") # return $groups.items # } # [PSCustomObject]GetGroupMembers([int]$groupId) { # return $this.GetGroupMembers($groupId, "isMember:eq:true") # } # [PSCustomObject]AddGroupMembers([int]$groupId, [int[]]$userIdArray) { # $parameter = @{ids = $userIdArray } # $result = $this.InvokePost("/v4/groups/$groupId/users", $parameter) # return $result # } # [PSCustomObject]CreateGroup([string]$groupName) { # $parameter = @{name = "$groupName" } # $result = $this.InvokePost("/v4/groups", $parameter) # #if ($verbose) {Log-Message ("Authentication-Token: {0}" -f $result.token)} # Write-PSFMessage $result # return $result # } # [PSCustomObject]DeleteUser([int]$id) { # $result = $this.DeleteUser($id, $false) # return $result # } # [PSCustomObject]GetLastAdminRooms([int]$userId) { # $result = $this.InvokeGet("/v4/users/$userId/last_admin_rooms") # return $result.items # } # [PSCustomObject]GetUserDetails([int]$userId) { # $result = $this.InvokeGet("/v4/users/$userId") # return $result # } # [PSCustomObject]GetCurrentUserInfos() { # $result = $this.InvokeGet("/v4/user/account") # return $result # } # [PSCustomObject]UpdateUser([int]$userId, [Hashtable]$metaData) { # $result = $this.InvokePut("/v4/users/$userId", $metaData) # return $result # } # [PSCustomObject]SetUserAttributes([int]$userId, [Hashtable]$userAttributes) { # $result = $this.SetUserAttributes($userId, $userAttributes, $false) # return $result # } # [PSCustomObject]AddUserAttributes([int]$userId, [Hashtable]$userAttributes) { # $result = $this.SetUserAttributes($userId, $userAttributes, $true) # return $result # } # [Hashtable]GetUserAttributes([int]$userId) { # $attributes = @{ } # $userDetails = $this.GetUserDetails($userId) # foreach ($item in $userDetails.userAttributes.items) { # $attributes.add($item.key, $item.value) # } # return $attributes # } # [PSCustomObject]LockUser([int]$userId, [bool]$lock) { # return $this.UpdateUser($userId, @{isLocked = $lock }) # return $result # } # [PSCustomObject]CreateMailUser([string]$login, [string]$firstName, [string]$lastName, [string]$title, [string]$gender, [string]$eMail) { # return $this.CreateMailUser($login, $firstName, $lastName, $title, $gender, $eMail, $false) # } # [PSCustomObject]CreateMailUser([string]$login, [string]$password, [string]$firstName, [string]$lastName, [string]$title, [string]$gender, [string]$eMail, [bool]$notifyUser) { # return $this.CreateMailUser($login, $password, $firstName, $lastName, $title, $gender, $eMail, $notifyUser, $false) # } # [PSCustomObject]CreateMailUser([string]$login, [string]$password, [string]$firstName, [string]$lastName, [string]$title, [string]$gender, [string]$eMail, [bool]$notifyUser, [bool]$enableOpenID) { # $parameter = @{firstName = $firstName; lastName = $lastName; authMethods = @(@{authId = "sql"; isEnabled = "true" }); login = $login; title = $title; gender = $gender; expiration = @{enableExpiration = "false"; expireAt = "2018-01-01T00:00:00" }; receiverLanguage = "de-DE"; email = $eMail; notifyUser = "$notifyUser"; needsToChangePassword = "true"; password = $password } # if ($enableOpenID) { # $parameter["authMethods"] += (@{authId = "openid"; isEnabled = $true; options = @(@{key = "openid_config_id"; value = $this.openIDConfigId }; @{key = "username"; value = $eMail }) }) # } # $result = $this.InvokePost("/v4/users", $parameter) # return $result # } # [PSCustomObject]UploadFile([string]$fullFilePath, [int]$parentNodeId) { # return $this.UploadFile((get-item $fullFilePath), $parentNodeId) # } # [PSCustomObject]UploadFile([System.IO.FileSystemInfo]$fullFilePath, [int]$parentNodeId) { # $result = $null # Write-PSFMessage "Lade Datei hoch: $fullFilePath" # $parameter = @{"parentId" = $parentNodeId; "name" = $fullFilePath.Name; "classification" = 2; "size" = $fullFilePath.length; "expiration" = @{"enableExpiration" = $false; "expireAt" = "2018-01-01T00:00:00" }; "notes" = "" } # $initUpload = $this.InvokePost("/v4/nodes/files/uploads", $parameter) # try { # $result = Invoke-RestMethod $initUpload.uploadUrl -ContentType "application/octet-stream" -Method Post -Headers $this.headers -InFile $fullFilePath.FullName # Write-PSFMessage $result # $result = $this.Invoke(("/v4/uploads/{0}" -f $initUpload.token), $null, [Microsoft.Powershell.Commands.WebRequestMethod]::Put, $false) # } # catch { # Write-PSFMessage "Fehler $_" # $this.InvokeDelete(("/v4/uploads/{0}" -f $initUpload.token)) # throw $_ # } # return $result # } # # [PSCustomObject]GetEventLog([int]$limit, [int]$offset, [string]$filter) { # [PSCustomObject]GetEventLog([int]$limit, [int]$offset, [hashtable]$additionalParamater) { # $parameterTable = @{limit = $limit; offset = $offset } # $parameterTable += $additionalParamater # # $uri = "/v4/eventlog/events?type=6&limit=$limit&offset=$offset" # # if ($filter) { # # $uri += "&$filter" # # } # $events = $this.InvokeGet("/v4/eventlog/events", $parameterTable) # return $events # } # [PSCustomObject]GetNode([int]$nodeId) { # $node = $this.InvokeGet("/v4/nodes/$nodeId") # return $node # } # [PSCustomObject]GetNodeList([hashtable]$parameterTable) { # $nodes = $this.InvokeGet("/v4/nodes", $parameterTable) # return $nodes # } # [PSCustomObject]GetParents([int]$nodeId) { # $parents = $this.InvokeGet("/v4/nodes/$nodeId/parents") # return $parents # } # [PSCustomObject]SearchNode([int]$parentNode, [string]$searchString, [string]$filter, [string]$sort) { # $parameterTable = @{search_string = $searchString; filter = $filter; sort = $sort; parent_id = $parentNode } # $nodes = $this.InvokeGet("/v4/nodes/search", $parameterTable) # return $nodes # } # [PSCustomObject]MoveNodes([int]$targetNodeId, [int[]]$nodesToMove) { # Write-PSFMessage "Verschiebe nach $targetNodeId : $nodesToMove" # $parameter = @{resolutionStrategy = "overwrite"; keepShareLinks = $true; nodeIds = $nodesToMove } # $result = $this.InvokePost("/v4/nodes/$targetNodeId/move_to", $parameter) # return $result # } # [PSCustomObject]CopyNodes([int]$targetNodeId, [int[]]$nodesToMove) { # Write-PSFMessage "Kopiere nach $targetNodeId : $nodesToMove" # $parameter = @{resolutionStrategy = "overwrite"; keepShareLinks = $true; nodeIds = $nodesToMove } # $result = $this.InvokePost("/v4/nodes/$targetNodeId/copy_to", $parameter) # return $result # } # [PSCustomObject]CreateFolder([int]$parentNodeId, [string]$name, [string]$notes) { # Write-PSFMessage "Lege Ordner $name unter der Node $parentNodeId an, Comment=$notes" # $parameter = @{parentId = $parentNodeId; name = $name; notes = $notes } # $result = $this.InvokePost("/v4/nodes/folders", $parameter) # return $result # } } function Get-DracoonServerRoot { <# .SYNOPSIS Cleans a given URL to be used as a server-root. .DESCRIPTION Cleans a given URL to be used as a server-root. .PARAMETER Url A FQDN/URL or the server .EXAMPLE Get-DracoonServerRoot "my.server.de" Get-DracoonServerRoot "my.server.de/" Get-DracoonServerRoot "http://my.server.de" Get-DracoonServerRoot "http://my.server.de/" All versions return "https://my.server.de" .NOTES General notes #> param ( [parameter(mandatory = $true, Position=0)] [string]$Url ) Write-PSFMessage "Getting Dracoon Server-Root for $Url" # Strip leading / $serverRoot = $Url.Trim("/") # Strip Prefix protocoll $serverRoot = $serverRoot -replace "^.*:\/\/" $serverRoot="https://$serverRoot" Write-PSFMessage "Result: $serverRoot" $serverRoot } function Get-EncodedParameterString { <# .SYNOPSIS Converts a hashtable to GET Parameters .DESCRIPTION Converts a hashtable to GET Parameters. .PARAMETER parameter Hashtable with the GET Parameters .EXAMPLE Get-EncodedParameterString -parameter @{key1="value1";key2="value2"} Returns the string: key1=value1&key2=value2 .NOTES General notes #> param ( [parameter(Mandatory,Position=1)] [Hashtable]$parameter ) $getParameter = @() if ($parameter) { foreach ($key in $parameter.Keys) { $value = $parameter[$key] if ($value) { switch ($value.GetType()) { bool { $value = $value.ToString() } Default { } } $valueEncoded = [System.Web.HttpUtility]::UrlEncode($value) $getParameter += "$key=$valueEncoded" } } } return ($getParameter -join "&") } function New-DracoonRandomPassword { <# .SYNOPSIS Creates a new random password. .DESCRIPTION Creates a new random password with the following rules: -12 characters long -at least one lower case character -at least one upper case character -at least one number -at least one special character .EXAMPLE $newPassword=New-DracoonRandomPassword Creates a new password .NOTES General notes #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] param() process { [int]$PasswordLength = 12 # Specifies an array of strings containing charactergroups from which the password will be generated. # At least one char from each group (string) will be used. [String[]]$InputStrings = @('abcdefghijkmnpqrstuvwxyz', 'ABCEFGHJKLMNPQRSTUVWXYZ', '23456789', '!"#%') $Password = @{ } # Create char arrays containing groups of possible chars [char[][]]$CharGroups = $InputStrings # Create char array containing all chars $AllChars = $CharGroups | ForEach-Object { [Char[]]$_ } # Randomize one char from each group Foreach ($Group in $CharGroups) { if ($Password.Count -lt $PasswordLength) { $Index = Get-DracoonSeed While ($Password.ContainsKey($Index)) { $Index = Get-DracoonSeed } $Password.Add($Index, $Group[((Get-DracoonSeed) % $Group.Count)]) } } # Fill out with chars from $AllChars for ($i = $Password.Count; $i -lt $PasswordLength; $i++) { $Index = Get-DracoonSeed While ($Password.ContainsKey($Index)) { $Index = GetSeed } $Password.Add($Index, $AllChars[((Get-DracoonSeed) % $AllChars.Count)]) } return $( -join ($Password.GetEnumerator() | Sort-Object -Property Name | Select-Object -ExpandProperty Value)) } } function Get-DracoonSeed { # Generate a seed for randomization $RandomBytes = New-Object -TypeName 'System.Byte[]' 4 $Random = New-Object -TypeName 'System.Security.Cryptography.RNGCryptoServiceProvider' $Random.GetBytes($RandomBytes) return [BitConverter]::ToUInt32($RandomBytes, 0) } function Remove-NullFromHashtable { <# .SYNOPSIS Funktion zum Entfernen von $null Werten aus HashTables. .DESCRIPTION Funktion zum Entfernen von $null Werten aus HashTables. Dazu wird die HashTable in einen json String umgewandelt und anschließend per RegEx alle null-Werte entfernt. Falls InputHashmap -eq $null dann wird nichts zurückgegeben. .PARAMETER InputHashmap Die Eingabe-HashTable .PARAMETER Json Wird der Parameter gesetzt, so wird anstatt einer neuen HashTable der modifizierte json String zurück geliefert .EXAMPLE $hash=@{ Name="Max" Surname="Mustermann" MiddleName=$null kids=@( @{ Name="Maxi" MiddleName=$null Surname="Mustermann" }, @{ Name="Maxine" MiddleName="Christine" Surname="Mustermann" } ) } $newHash=$hash | Remove-NullFromHashtable .NOTES General notes #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] param ( [Parameter(ValueFromPipeline)] [hashtable]$InputHashmap, [switch]$Json ) process { if ($InputHashmap) { $tempString = $InputHashmap | ConvertTo-Json -Depth 15 Write-PSFMessage -Level Debug "Entferne null Values aus $tempString" $tempString = $tempString -replace '"\w+?"\s*:\s*null,?' Write-PSFMessage -Level Debug "Ergebnis: $tempString" if ($Json) { $tempString }else { Write-PSFMessage -Level Debug "Erzeuge HashTable" $newHashmap = $tempString | ConvertFrom-Json $newHashmap } } } } function Write-DracoonAPICallMessage { <# .SYNOPSIS Writes information about an API invocation to the PFS Logging. .DESCRIPTION Writes information about an API invocation to the PFS Logging. .PARAMETER APICallParameter HashTable with all parameters forwarded from Invoke-DracoonAPI to Invoke-RestMethod .EXAMPLE Write-DracoonAPICallMessage $restAPIParameter Logs the given parameter to the PSFLogging with Tag "APICALL" .EXAMPLE Get-PSFMessage -Tag "APICALL" |Select-Object -Last 1 -Property TargetObject,Message|Format-List Retrieves the last API call. .NOTES Logs are compressed by default. For troubleshooting set it to uncrompressed with Set-PSFConfig -Module 'Dracoon' -Name 'logging.compressRequests' -Value $false #> param ( [Parameter(Position = 0, Mandatory = $true)] $APICallParameter ) try { $compress=Get-PSFConfigValue -FullName 'Dracoon.logging.compressRequests' -Fallback $true $modifiedAPICallParameter = $APICallParameter.Clone() if ($modifiedAPICallParameter.Body -is [String]) { $modifiedAPICallParameter.Body = $modifiedAPICallParameter.Body | ConvertFrom-Json } $modifiedAPICallParameter.Method = "$($modifiedAPICallParameter.Method)".ToUpper() $callStack = (Get-PSCallStack | Select-Object -SkipLast 1 -ExpandProperty Command | Select-Object -Skip 1 ) [Array]::Reverse($callStack) $callStackString = $callStack -join ">" Write-PSFMessage "CallStack: $callStackString" $apiLogString = ($modifiedAPICallParameter | ConvertTo-Json -Depth 7 -Compress:$compress) # Remove confidental data $apiLogString = $apiLogString -replace '"Bearer (\w{5})\w*"', '"Bearer $1******************"' $apiLogString = $apiLogString -replace '("password":\s*").*"', '$1***********"' $apiLogString = $apiLogString -replace '("refresh_token":\s*").*"', '$1***********"' $apiLogString = $apiLogString -replace '("code":\s*").*"', '$1***********"' $apiLogString = $apiLogString -replace '("X-Sds-Auth-Token":\s*").*"', '$1***********"' $apiLogString = $apiLogString -replace '("Authorization":\s*"Basic ).*"', '$1[BASE64_ENCODED [CLIENT_ID]:[CLIENT_SECRET]]"' Write-PSFMessage -Level Verbose "$apiLogString" -Tag "APICALL" -Target "$callStackString" } catch { Write-PSFMessage -Level Critical "Could not log API Call $_" } } function Add-DracoonUrl { <# .SYNOPSIS This function allows the addition of new Server-URLs for TAB Completion. Each function which requires a -Url parameter will provide a TAB completer with suggested URLs. .DESCRIPTION This function allows the addition of new Server-URLs for TAB Completion. Each function which requires a -Url parameter will provide a TAB completer with suggested URLs, e.g. Connect-Dracoon Different from Set-DracoonUrl this command does not overwrite but append to existing settings. .PARAMETER NewUrl The new URLs to be added .PARAMETER whatIf If enabled it does not execute the backend API call. .PARAMETER confirm If enabled the backend API Call has to be confirmed .EXAMPLE Add-DracoonUrl 'https://dxi.mydomain' Add a single Server to the list of suggested URLs (get-adforest -ErrorAction Stop).domains | ForEach-Object { Add-DracoonUrl "https://dataexchange.$($_)" } If you have an on prem Dracoon server in each of your Windows Domains with the address "https://dracoon.<yourdomain>" it will get added to the list of suggested URLs. .NOTES The URLs get saved at the PSF-Config "Dracoon.tepp.urls" #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'low')] param ( [parameter(mandatory = $true, Position = 0)] [string[]]$NewUrl ) Write-PSFMessage "Adding new Urls for the URL TEPP: $NewUrl" $oldUrl=Get-PSFConfigValue "Dracoon.tepp.urls" Write-PSFMessage "Existing Urls for the URL TEPP: $oldUrl" $combinedUrls=@() $combinedUrls+=$NewUrl if ($oldUrl) { $combinedUrls+=$oldUrl } # Adjusting format of URLs $combinedUrls = $combinedUrls | ForEach-Object { Get-DracoonServerRoot $_ } # Sorting and filtering unique $combinedUrls=$combinedUrls | Sort-Object | Select-Object -Unique Write-PSFMessage "combined Urls for the URL TEPP: $combinedUrls" Invoke-PSFProtectedCommand -Action "Saving new Urls for the URL TEPP" -Target "$NewUrl" -ScriptBlock { Set-PSFConfig -Module 'Dracoon' -Name 'tepp.urls' -Value $combinedUrls -PassThru | Register-PSFConfig } -PSCmdlet $PSCmdlet } function Connect-Dracoon { <# .SYNOPSIS Creates a new Connection Object to a Dracoon Server instance. .DESCRIPTION Creates a new Connection Object to a Dracoon Server instance. For connecting you always need the -Url as the function needs to know where the Server is located. As a minimum additional information you have to provide an authorization, either as -Credential or as -AccessToken. The usage of a credential object as the only information is *deprecated* and should be replaces in favor of an OAuth workflow. For OAuth you need to configure an application within the Web-UI. For more information see about_Dracoon. .PARAMETER Credential Credential-Object for direct login. .PARAMETER Url The server root URL. .PARAMETER RefreshToken Neccessary for OAuth Login: Refresh-Token. Can be created with Request-OAuthRefreshToken. .PARAMETER AccessToken Neccessary for OAuth Login: Access-Token. Can be created with Request-OAuthRefreshToken. .PARAMETER AuthToken Neccessary for OAuth Login: Auth-Token. Can be created with Request-OAuthRefreshToken. .PARAMETER ClientID Neccessary for OAuth Login: The Id of the OAauth Client. .PARAMETER ClientSecret Neccessary for OAuth Login: The Secret of the OAauth Client. .PARAMETER EnableException Should Exceptions been thrown? .EXAMPLE $connection=Connect-Dracoon -Url $url -ClientID $clientId -ClientSecret $clientSecret -Credential $cred Connect directly with OAuth and a Credential-Object .EXAMPLE # Connect Via pre-generated OAuth access token ## Generate accesstoken $accessToken=Request-DracoonOAuthToken -ClientID $clientId -ClientSecret $clientSecret -Url $url -Credential $cred -TokenType access ## Login with created access token $connection=Connect-Dracoon -Url $url -AccessToken $accessToken .EXAMPLE # Connect Via pre-generated OAuth refresh token ## Create a refresh token $refreshToken=Request-DracoonOAuthToken -ClientID $clientId -ClientSecret $clientSecret -Credential $cred -url $url -TokenType refresh ## Connect directly with the refresh token $connection=Connect-Dracoon -ClientID $clientId -ClientSecret $clientSecret -url $url -RefreshToken $refreshToken .EXAMPLE ## Second option: Create an access token from the refreh token and login with the access token. $accessToken=Request-DracoonOAuthToken -ClientID $clientId -ClientSecret $clientSecret -Url $url -RefreshToken $refreshToken $connection=Connect-Dracoon -Url $url -AccessToken $accessToken .EXAMPLE # Direct auth with /auth/login (**Deprecated**) ## If you are running an older version it maybe possible to login directly. But this option is deprecated and [will be removed in every installation in the future](https://blog.dracoon.com/en/goodbye-x-sds-auth-token-hello-oauth-2.0) $connection=Connect-Dracoon -Url $url -Credential $cred .NOTES As you have to authenticate with OAuth2.0 it is neccessary to create a client application within the admin web-page. For this * Go to _System Settings_ / _Apps_ in the navigation bar * Click on the _Add app_ button * Enter an application name (e.g. "Powershell Scripting") * enable all checkboxes (authorization code:implicit:password) * Copy the _Client ID_ and the _Client Secret_. Both will be referenced as `$ClientID` and `$ClientSecret`. Now it's time to open the powershell. Prepare the basic variables: $cred=Get-Credential -Message "Dracoon" $clientId="YOU JUST CREATED IT ;-)" $clientSecret="THIS ALSO" $url="dracoon.mydomain.com" #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [CmdletBinding(DefaultParameterSetName = "AccessToken")] Param ( [parameter(mandatory = $true, ParameterSetName = "authorization_code")] [parameter(mandatory = $true, ParameterSetName = "password")] [parameter(mandatory = $true, ParameterSetName = "refresh_token")] [parameter(mandatory = $true, ParameterSetName = "deprecatedLogin")] [parameter(mandatory = $true, ParameterSetName = "AccessToken")] [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("Dracoon.url")] [string]$Url, [parameter(mandatory = $true, ParameterSetName = "authorization_code")] [parameter(mandatory = $true, ParameterSetName = "password")] [parameter(mandatory = $true, ParameterSetName = "refresh_token")] [string]$ClientID, [parameter(mandatory = $true, ParameterSetName = "authorization_code")] [parameter(mandatory = $true, ParameterSetName = "password")] [parameter(mandatory = $true, ParameterSetName = "refresh_token")] [string]$ClientSecret, [parameter(mandatory = $true, ParameterSetName = "password")] [parameter(mandatory = $true, ParameterSetName = "deprecatedLogin")] [pscredential]$Credential, [parameter(mandatory = $true, ParameterSetName = "authorization_code")] [string]$AuthToken, [parameter(mandatory = $true, ParameterSetName = "refresh_token")] [string]$RefreshToken, [parameter(mandatory = $true, ParameterSetName = "AccessToken")] [string]$AccessToken, [switch]$EnableException ) begin { Write-PSFMessage "Stelle Verbindung her zu $Url" -Target $Url if ($PSCmdlet.ParameterSetName -eq 'deprecatedLogin') { # $connection = [Dracoon]::new($Credential, $Url) Invoke-PSFProtectedCommand -ActionString "Connect-Dracoon.Connecting" -ActionStringValues $Url -Target $Url -ScriptBlock { # $connection = [Dracoon]::new($Credential.username, $Credential.GetNetworkCredential().password, $Url) $connection = [Dracoon]::new($Credential, $Url) } -PSCmdlet $PSCmdlet -EnableException $EnableException } else{ if ($PSCmdlet.ParameterSetName -ne 'AccessToken') { Write-PSFMessage "Aquiring AccessToken with splatting, ParameterSetName=$($PSCmdlet.ParameterSetName)" $AccessToken=Request-DracoonOAuthToken @PSBoundParameters } $connection = [Dracoon]::new($AccessToken,$Url) } } process { if (Test-PSFFunctionInterrupt) { return } Write-PSFMessage -string "Connect-Dracoon.Connected" $connection } } function Convert-DracoonGetSetRoomAcl { <# .SYNOPSIS Converts Information retrieved from Get-DracoonRoomAcl to be used for Set-DracoonRoomAcl. .DESCRIPTION Converts Information retrieved from Get-DracoonRoomAcl to be used for Set-DracoonRoomAcl. Get-DracoonRoomAcl returns an array in the following format: { "userInfo": { "id": 0, "userType": "internal", "avatarUuid": "string", "displayName": "string", "firstName": "string", "lastName": "string", "email": "string", "title": "string" }, "isGranted": true, "id": 0, "login": "string", "displayName": "string", "email": "john.doe@email.com", "permissions": { "manage": true, "read": true, "create": true, "change": true, "delete": true, "manageDownloadShare": true, "manageUploadShare": true, "readRecycleBin": true, "restoreRecycleBin": true, "deleteRecycleBin": true }, "publicKeyContainer": { "version": "A", "publicKey": "string" } } Set-DracoonRoomAcl needs only a sub-part as an array: { "id": 0, "permissions": { "manage": true, "read": true, "create": true, "change": true, "delete": true, "manageDownloadShare": true, "manageUploadShare": true, "readRecycleBin": true, "restoreRecycleBin": true, "deleteRecycleBin": true } } This function converts the different formats. .PARAMETER ExistingPermissions Array of the exisiting Permission Items. .EXAMPLE To be added in the Future .NOTES General notes #> param ( [parameter(Mandatory)] [array]$ExistingPermissions ) Write-PSFMessage "Converting Permissions " Write-PSFMessage "Current permissions: $($ExistingPermissions|convertto-json -depth 10)" $permissionList = @() foreach ($item in $ExistingPermissions) { $permissionList += @{id = $item.id; permissions = $item.permissions } } Write-PSFMessage "Neue Berechtigungen: $($permissionList|convertto-json -depth 10)" $permissionList } function Get-DracoonAPILog { <# .SYNOPSIS Retrieves the log entries from Write-DracoonAPICallMessage. .DESCRIPTION Retrieves the log entries from Write-DracoonAPICallMessage. .PARAMETER Raw If set then the raw PSFMessages are returned. .PARAMETER Last If set only the Last entries are returned. .EXAMPLE Get-DracoonAPILog -Last 5 Retrieves the last 5 logs. .NOTES General notes #> param ( [switch]$Raw, [int]$Last = 0 ) $selectParam = @{ } if ($Last -gt 0){ $selectParam.Last=$Last } if (!$Raw){ $selectParam.Property = @("TargetObject", "Message") } $messages = Get-PSFMessage -Tag "APICALL" | Select-Object @selectParam if ($Raw) { $messages }else { $messages | Format-List } } function Get-DracoonAuditDataroom { <# .SYNOPSIS Searches Datarooms by given filter. API-GET /v4/eventlog/audits/nodes .DESCRIPTION Retrieve a list of all nodes of type room, and the room assignment users with permissions. .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER Filter All filter fields are connected via logical conjunction (AND) Except for userName, userFirstName and userLastName - these are connected via logical disjunction (OR) Filter string syntax: FIELD_NAME:OPERATOR:VALUE[:VALUE...] userName:cn:searchString_1|userFirstName:cn:searchString_2|nodeId:eq:2 Possible combinations: 'nodeId:eq:[positive Integer]' 'nodeName:[cn/eq]:[search String]' 'nodeParentId:eq:[positive Integer]' 'Parent ID 0 is the root node.:[]:[]' 'userId:eq:[positive Integer]' 'userName:[cn/eq]:[search String]' 'userFirstName:[cn/eq]:[search String]' 'userLastName:[cn/eq]:[search String]' 'permissionsManage:eq:[true or false]' 'nodeIsEncrypted:eq:[true or false]' 'nodeHasActivitiesLog:eq:[true or false]' .PARAMETER Limit Range limit. Maximum 500. For more results please use paging (offset + limit). .PARAMETER Offset Range offset .PARAMETER Sort Sort string syntax: FIELD_NAME:ORDER ORDER can be asc or desc. Multiple sort fields are NOT supported. Nodes are sorted by type first, then by sent sort string. Example: name:desc .PARAMETER HideSpecialRooms Filters any room which has a GUID as roomName oder Parent-Path .EXAMPLE Get-DracoonAuditDataroom -Connection $connection Lists all available Datarooms .NOTES Right "read audit log" required. #> param ( [parameter(Mandatory)] [Dracoon]$Connection, [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("Dracoon.filter")] [string]$Filter, [int]$Limit, [int]$Offset, [string]$Sort, [bool]$HideSpecialRooms=$true ) $apiCallParameter = @{ Connection = $Connection method = "Get" Path = "/v4/eventlog/audits/nodes" EnablePaging = $true UrlParameter = @{ filter = $Filter limit = $Limit sort = $Sort offset = $offset } } $datarooms = Invoke-DracoonAPI @apiCallParameter if($HideSpecialRooms){ Write-PSFMessage "Entferne Datenräume, deren Parent einer GUID entsprechen" $regex = '[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?' return $datarooms | Where-Object {( $_.nodeParentPath -notmatch $regex) -and ( $_.nodeName -notmatch $regex)} }else{ return $datarooms } } function Get-DracoonAuthConfigAD{ <# .SYNOPSIS Retrieve a list of configured Active Directories. API-GET /v4/system/config/auth/ads .DESCRIPTION Retrieve a list of configured Active Directories. API-GET /v4/system/config/auth/ads Right “read global config” required. Role Config Manager of the Provider Customer. .PARAMETER Connection Parameter description .PARAMETER Alias Returns only the configuration whose "alias" attribute matches the parameter value .EXAMPLE To be added in the Future #> param ( [parameter(Mandatory)] [Dracoon]$Connection, [string]$Alias ) $apiCallParameter = @{ Connection = $Connection method = "Get" Path ="/v4/system/config/auth/ads" } $result=Invoke-DracoonAPI @apiCallParameter if ($Alias){ $result.items|where-object {$_.Alias -eq $Alias} }else{ $result.items } } function Get-DracoonCurrentAccount { <# .SYNOPSIS Retrieves all information regarding the current user’s account. API-GET /v4/user/account .DESCRIPTION Retrieves all information regarding the current user’s account. API-GET /v4/user/account .PARAMETER Connection Parameter description .EXAMPLE To be added in the Future .NOTES General notes #> param ( [parameter(Mandatory)] [Dracoon]$Connection ) $apiCallParameter = @{ Connection = $Connection method = "Get" Path ="/v4/user/account" } Invoke-DracoonAPI @apiCallParameter } function Get-DracoonGroup { <# .SYNOPSIS Returns a list of user groups. API-GET /v4/groups .DESCRIPTION Returns a list of user groups. .PARAMETER Connection Parameter description .PARAMETER Filter All filter fields are connected via logical conjunction (AND) Filter string syntax: FIELD_NAME:OPERATOR:VALUE Possible combinations: 'name:cn:[search String]' .PARAMETER Limit Range limit. Maximum 500. For more results please use paging (offset + limit). .PARAMETER Offset Range offset .PARAMETER Sort Sort string syntax: FIELD_NAME:ORDER ORDER can be asc or desc. Multiple sort fields are supported. Example: name:asc|expireAt:desc Sort by name ascending AND by expireAt descending. Possible fields: name Group name createdAt Creation date expireAt Expiration date cntUsers Amount of users .PARAMETER EnablePaging Wenn die API mit Paging arbeitet, kann über diesn Parameter ein automatisches Handling aktivieren. Dann werden alle Pages abgehandelt und nur die items zurückgeliefert. .EXAMPLE To be added in the Future .NOTES General notes #> param ( [parameter(Mandatory)] [Dracoon]$Connection, [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("Dracoon.filter")] [string]$Filter, [int]$Limit=500, [int]$Offset=0, [string]$Sort, [bool]$EnablePaging=$true ) $apiCallParameter = @{ Connection = $Connection method = "Get" Path ="/v4/groups" EnablePaging = $EnablePaging UrlParameter = @{ filter=$Filter limit=$Limit sort=$Sort offset=$offset } } Invoke-DracoonAPI @apiCallParameter } # [PSCustomObject]GetLastAdminRooms([int]$userId) { # $result = $this.InvokeGet("/v4/users/$userId/last_admin_rooms") # return $result.items # } function Get-DracoonLastAdminRoom { <# .SYNOPSIS Get rooms where the user is last admin. API-GET /v4/users/$id/last_admin_rooms .DESCRIPTION Retrieve a list of all rooms where the user is last admin (except homeroom and its subordinary rooms). .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL. .PARAMETER Id ID of the user .EXAMPLE To be added in the Future .NOTES General notes #> param ( [parameter(Mandatory)] [Dracoon]$Connection, [parameter(Mandatory)] [int]$Id ) $apiCallParameter = @{ Connection = $Connection method = "Get" Path = "/v4/users/$Id/last_admin_rooms" } $result=Invoke-DracoonAPI @apiCallParameter $result.items } function Get-DracoonNode { <# .SYNOPSIS Provides a hierarchical list of file system nodes (rooms, folders or files) of a given parent that are *accessible* by the current user. .DESCRIPTION Provides a hierarchical list of file system nodes (rooms, folders or files) of a given parent that are accessible by the current user. GET /v4/nodes .PARAMETER connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER Filter All filter fields are connected via logical conjunction (AND) Filter string syntax: FIELD_NAME:OPERATOR:VALUE[:VALUE...] Possible combinations: 'type:eq:[room/folder/file]' 'perm:eq:[manage/read/change/create/delete/manageDownloadShare/manageUploadShare/canReadRecycleBin/canRestoreRecycleBin/canDeleteRecycleBin]' 'childPerm:eq:[cf perm]' 'name:[cn/eq]:[Node name]' 'encrypted:eq:[true/false]' 'branchVersion:[ge/le]:[Branch version]' .PARAMETER Limit Range limit. Maximum 500. For more results please use paging (offset + limit). .PARAMETER Offset Range offset .PARAMETER Sort Sort string syntax: FIELD_NAME:ORDER ORDER can be asc or desc. Multiple sort fields are NOT supported. Nodes are sorted by type first, then by sent sort string. Example: name:desc .PARAMETER ParentID Parent node ID. Only rooms and folders can be parents. Parent ID 0 or empty is the root node. .PARAMETER RoomManager boolean. Show all rooms for management perspective. Only possible for Rooms Managers. For all other users, it will be ignored. .EXAMPLE To be added in the Future .NOTES General notes #> param ( [parameter(Mandatory)] [Dracoon]$connection, [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("Dracoon.filter")] [string]$Filter, [int]$Limit=500, [int]$Offset=0, [string]$Sort, [int]$ParentID=0, $RoomManager ) $apiCallParameter = @{ Connection = $Connection method = "Get" Path ="/v4/nodes" EnablePaging = $true UrlParameter = @{ filter=$Filter limit=$Limit sort=$Sort offset=$offset parent_id=$ParentID room_manager=$RoomManager } # EnablePaging=$true } $result = Invoke-DracoonAPI @apiCallParameter $result } function Get-DracoonNodeAsZip { <# .SYNOPSIS Creates a ZIP archive from given NodeIDs and downloads it. API-POST /v4/nodes/zip .DESCRIPTION Creates a ZIP archive from given NodeIDs and downloads it. .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER Id Array of NodeIDs which should be included into the ZIP file. .PARAMETER FileName Name of the downloaded ZIP file. .PARAMETER EnableException If set to true, inner exceptions will be rethrown. Otherwise the an empty result will be returned. .EXAMPLE Get-DracoonNode -connection $connection -ParentID $roomId | Get-DracoonNodeAsZip -Connection $connection -FileName "myArchive.zip" Creates a ZIP archive containing all files of the given room. .NOTES General notes #> param ( [parameter(Mandatory)] [Dracoon]$Connection, [parameter(Mandatory, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [int[]]$Id, [parameter(Mandatory)] $FileName, $EnableException = $false ) begin { $idArray = @() } process { $idArray += $id } end { Write-PSFMessage "Download ID: $($idArray -join ",")" $apiCallParameter = @{ Connection = $Connection method = "Post" Path = "/v4/nodes/zip" Body = @{ nodeIds = $idArray } } $result = Invoke-DracoonAPI @apiCallParameter if ($result) { Invoke-PSFProtectedCommand -Action "Downloading" -Target $FileName -ScriptBlock { Invoke-WebRequest -Uri $result.downloadUrl -OutFile $FileName -ErrorAction Stop } -PSCmdlet $PSCmdlet -EnableException $EnableException -RetryCount 4 -RetryWait 5 } } } # [PSCustomObject]GetRoomAcl([int]$roomId) { # $result = $this.InvokeGet("/v4/nodes/rooms/$roomId/users") # return $result # } function Get-DracoonRoomAcl { <# .SYNOPSIS Retrieve a list of users that are and / or can be granted to the room. API-GET /v4/nodes/rooms/$NodeId/users .DESCRIPTION Retrieve a list of users that are and / or can be granted to the room. .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER NodeId ID of the room .PARAMETER Filter All filter fields are connected via logical conjunction (AND) Filter string syntax: FIELD_NAME:OPERATOR:VALUE Example: permissionsManage:eq:true|user:cn:searchString Get all users that have manage permissions to this room AND whose (firstname OR lastname OR email) is like searchString. Possible combinations: 'user:cn:[search String]', 'userId:eq:[positive Integer]' 'isGranted:eq:[true/false/any]' 'permissionsManage:eq:[true/false]' 'effectivePerm:eq:[true/false]' .PARAMETER Limit Range limit. Maximum 500. For more results please use paging (offset + limit). .PARAMETER Offset Range offset .PARAMETER Sort Sort string syntax: FIELD_NAME:ORDER ORDER can be asc or desc. Multiple sort fields are NOT supported. Nodes are sorted by type first, then by sent sort string. Example: name:desc .PARAMETER EnablePaging Wenn die API mit Paging arbeitet, kann über diesn Parameter ein automatisches Handling aktivieren. Dann werden alle Pages abgehandelt und nur die items zurückgeliefert. .EXAMPLE To be added in the Future .NOTES General notes #> param ( [parameter(Mandatory)] [Dracoon]$Connection, [parameter(Mandatory)] [int]$NodeId, [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("Dracoon.filter")] [string]$Filter, [int]$Limit = 500, [int]$Offset = 0, [bool]$EnablePaging = $true ) $apiCallParameter = @{ Connection = $Connection method = "Get" Path = "/v4/nodes/rooms/$NodeId/users" EnablePaging = $EnablePaging UrlParameter = @{ filter = $Filter limit = $Limit offset = $offset } } Invoke-DracoonAPI @apiCallParameter } function Get-DracoonUser { <# .SYNOPSIS Query of all Users. API-GET /v4/users oder /v4/users/$Id .DESCRIPTION Function has two modes: Single or Multi-Users. If using in Multi-User Mode (without Id parameter) it returns an array of all users. .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL. .PARAMETER Filter All filter fields are connected via logical conjunction (AND) Except for login, firstName and lastName - these are connected via logical disjunction (OR) Filter string syntax: FIELD_NAME:OPERATOR:VALUE[:VALUE...] Example: login:cn:searchString_1|firstName:cn:searchString_2|lockStatus:eq:2 Filter users by login contains searchString_1 OR firstName contains searchString_2 AND those who are NOT locked. effectiveRoles Filter users with DIRECT or DIRECT AND EFFECTIVE roles false: DIRECT roles true: DIRECT AND EFFECTIVE roles DIRECT means: e.g. user gets role directly granted from someone with grant permission right. EFFECTIVE means: e.g. user gets role through group membership. Possible combinations: 'login:cn:[search String]' 'firstName:cn:[search String]' 'lastName:cn:[search String]' 'isLocked:eq:[true/false]' 'effectiveRoles:eq:[true/false]' .PARAMETER IncludeAttributes Parameter description .PARAMETER Limit Range limit. Maximum 500. For more results please use paging (offset + limit). .PARAMETER Offset Range offset .PARAMETER Sort Sort string syntax: FIELD_NAME:ORDER ORDER can be asc or desc. Multiple sort fields are NOT supported. Nodes are sorted by type first, then by sent sort string. Example: name:desc .PARAMETER EnablePaging Wenn die API mit Paging arbeitet, kann über diesn Parameter ein automatisches Handling aktivieren. Dann werden alle Pages abgehandelt und nur die items zurückgeliefert. .PARAMETER Id Id of a specific user. .PARAMETER EffectiveRoles Filter users with DIRECT or DIRECT AND EFFECTIVE roles. false: DIRECT roles true: DIRECT AND EFFECTIVE roles DIRECT means: e.g. user gets role directly granted from someone with grant permission right. EFFECTIVE means: e.g. user gets role through group membership. .EXAMPLE To be added in the Future .NOTES General notes #> [CmdletBinding(DefaultParameterSetName = "SingleUser")] Param ( [parameter(Mandatory)] [Dracoon]$Connection, [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("Dracoon.filter")] [parameter(Mandatory = $false, ParameterSetName = "MultipleUsers")] [string]$Filter, [parameter(Mandatory = $false, ParameterSetName = "MultipleUsers")] [int]$Limit=500, [parameter(Mandatory = $false, ParameterSetName = "MultipleUsers")] [int]$Offset=0, [parameter(Mandatory = $false, ParameterSetName = "MultipleUsers")] [switch]$IncludeAttributes, [parameter(Mandatory = $false, ParameterSetName = "MultipleUsers")] [string]$Sort, [parameter(Mandatory = $false, ParameterSetName = "MultipleUsers")] [bool]$EnablePaging=$true, [parameter(Mandatory = $false, ParameterSetName = "SingleUser")] [int]$Id, [parameter(Mandatory = $false, ParameterSetName = "SingleUser")] [bool]$EffectiveRoles=$true ) if ($id -eq 0){ Write-PSFMessage "Ermittle mehrere User" $apiCallParameter = @{ Connection = $Connection method = "Get" Path ="/v4/users" EnablePaging = $EnablePaging UrlParameter = @{ filter=$Filter include_attributes=$IncludeAttributes limit=$Limit sort=$Sort offset=$offset } } }else{ Write-PSFMessage "Requesting detailed user info for #$Id" $apiCallParameter = @{ Connection = $Connection method = "Get" Path ="/v4/users/$Id" UrlParameter = @{ effective_roles = $EffectiveRoles } } } Invoke-DracoonAPI @apiCallParameter } function Get-DracoonUserAttribute { <# .SYNOPSIS Retrieve a list of user attributes. API-GET /v4/users/{user_id}/userAttributes .DESCRIPTION Retrieve a list of user attributes. .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER Id ID of the User. .PARAMETER ReturnHashTable If set to true (default), results are returned as a HashTable. Otherwise an array of PSCustomObjects is returned .PARAMETER Filter All filter fields are connected via logical conjunction (AND) Filter string syntax: FIELD_NAME:OPERATOR:VALUE[:VALUE...] Example: key:cn:searchString_1|value:cn:searchString_2 Filter by attribute key contains searchString_1 AND attribute value contains searchString_2. Possible combinations: 'key:[cn/eq/sw]:[Attribute key]' 'value:[cn/eq/sw]:[Attribute value]' .PARAMETER Limit Range limit. Maximum 500. For more results please use paging (offset + limit). .PARAMETER Offset Range offset .PARAMETER Sort Sort string syntax: FIELD_NAME:ORDER ORDER can be asc or desc. Multiple sort fields are NOT supported. Nodes are sorted by type first, then by sent sort string. Example: name:desc .PARAMETER EnablePaging Wenn die API mit Paging arbeitet, kann über diesn Parameter ein automatisches Handling aktivieren. Dann werden alle Pages abgehandelt und nur die items zurückgeliefert. .EXAMPLE To be added in the Future .NOTES General notes #> param ( [parameter(Mandatory)] [Dracoon]$connection, [parameter(Mandatory)] [int]$Id, [bool]$ReturnHashTable = $true, [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("Dracoon.filter")] [string]$Filter, [int]$Limit = 500, [int]$Offset = 0, [string]$Sort, [bool]$EnablePaging = $true ) $apiCallParameter = @{ Connection = $Connection method = "Get" Path = "/v4/users/$Id/userAttributes" EnablePaging = $EnablePaging UrlParameter = @{ filter = $Filter limit = $Limit sort = $Sort offset = $offset } } Write-PSFMessage "Ermittele UserAttribute zu User $Id" $results = Invoke-DracoonAPI @apiCallParameter if ($EnablePaging) { $items = $results }else { $items = $results.items} if ($ReturnHashTable) { $attributes = @{} foreach ($item in $items ) { $attributes.add($item.key, $item.value) } $attributes } else { $items } } function Invoke-DracoonAPI { <# .SYNOPSIS Generic API Call to the Dracoon API. .DESCRIPTION Generic API Call to the Dracoon API. This function is a wrapper for the usage of Invoke-WebRequest. It handles some annoying repetitive tasks which occur in most use cases. This includes (list may be uncompleted) - Connecting to a server with authentication - Parsing API parameter - Handling $null parameter - Paging for API endpoints which do only provide limited amounts of datasets .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL. Can be obtained with Connect-Dracoon. .PARAMETER Path API Path to the REST function, starting *after* /api. Example: "/v4/users" .PARAMETER Body Parameter for the API call; The hashtable is Converted to the POST body by using ConvertTo-Json .PARAMETER URLParameter Parameter for the API call; Converted to the GET URL parameter set. Example: { id=4 name=Jon Doe } will result in "?id=4&name=Jon%20Doe" being added to the URL Path .PARAMETER Method HTTP Method, Get/Post/Delete/Put/... .PARAMETER ContentType HTTP-ContentType, defaults to "application/json;charset=UTF-8" See Publish-DracoonFile for usage. .PARAMETER InFile File which should be transferred during the Request. See Publish-DracoonFile for usage. .PARAMETER HideParameters If set to $true the password is hidden from logging .PARAMETER EnablePaging If the API makes use of paging (therefor of limit/offset URLParameter) setting EnablePaging to $true will not return the raw data but a combination of all data sets. .PARAMETER EnableException If set to true, inner exceptions will be rethrown. Otherwise the an empty result will be returned. .EXAMPLE $result = Invoke-DracoonAPI -connection $this -path "/v4/auth/login" -method POST -body @{login = $credentials.UserName; password = $credentials.GetNetworkCredential().Password; language = "1"; authType = "sql" } -hideparameters $true Login to the service .NOTES General notes #> param ( [parameter(Mandatory)] $Connection, [parameter(Mandatory)] [string]$Path, [Hashtable] $Body, [Hashtable] $URLParameter, [parameter(Mandatory)] [Microsoft.Powershell.Commands.WebRequestMethod]$Method, [bool] $HideParameters = $false, [string]$ContentType = "application/json;charset=UTF-8", [string]$InFile, [bool]$EnableException=$true, [switch]$EnablePaging ) $uri = $connection.webServiceRoot + $path if ($URLParameter) { Write-PSFMessage "Converting UrlParameter to a Request-String and add it to the path" Write-PSFMessage "$($UrlParameter|ConvertTo-Json)" $parameterString = (Get-EncodedParameterString($URLParameter)) $uri = $uri + '?' + $parameterString.trim("?") } $restAPIParameter = @{ Uri = $Uri method = $Method body = ($Body | Remove-NullFromHashtable) Headers = $connection.headers ContentType = $ContentType } If ($Body) { $restAPIParameter.body = ($Body | Remove-NullFromHashtable -Json) } If ($InFile) { $restAPIParameter.InFile = $InFile } try { Write-DracoonAPICallMessage $restAPIParameter $result = Invoke-RestMethod @restAPIParameter Write-PSFMessage -Level Debug "result= $($result|ConvertTo-Json -Depth 5)" if ($EnablePaging -and ($result -is [array])) { Write-PSFMessage "Paging enabled, aber keine Range zurückgeliefert" -Level Warning }elseif ($EnablePaging) { Write-PSFMessage "Paging enabled, starte Schleife, result.range=$($result.range)" $allItems = ($result.items) write-psfmessage "Anzahl ermittelter Items: $($allItems.count)" $URLParameter.limit = $result.range.limit $URLParameter.offset = $result.range.offset while ($result.range.total -gt $allItems.count) { Write-PSFMessage "result.range.total=$($result.range.total) -gt allItems.count=$($allItems.count)" $URLParameter.offset = $allItems.count $nextParameter = @{ Connection = $Connection Path = $Path Body = $Body URLParameter = $URLParameter Method = $Method HideParameters = $HideParameters } Write-DracoonAPICallMessage $nextParameter $result = Invoke-DracoonAPI @nextParameter $allItems += ($result.items) } return $allItems } } catch { $result = $_.errordetails Write-PSFMessage "$result" -Level Critical If ($EnableException){ throw $_#$result.Message }else{ return } } return $result } function New-DracoonDataroom { <# .SYNOPSIS Creates a new room at the provided parent node. Creation of top level rooms provided. .DESCRIPTION API-POST /v4/nodes/rooms .PARAMETER connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER RoomName Name of the new room .PARAMETER ParentNodeId Node-ID of the parent-node, 0 for Creation of top level rooms .PARAMETER AdminUserId Array of user-ids of room admins .PARAMETER AdminGroupId Array of user-ids of room admin groups .PARAMETER RecycleBinRetentionPeriod How many days should files be kept in the recycle bin. .PARAMETER InheritPermissions Room inherits the permissions of the parent room .PARAMETER Notes Description notes for the room .PARAMETER NewGroupMemberAcceptance Do new admin group members have to be released? Default is "autoallow" .PARAMETER Quota Quota for the new room in bytes. 0 for no quota. .PARAMETER HasActivitiesLog Is the activity log enabled for the room? .PARAMETER Classification Nummerical classification. .PARAMETER whatIf If enabled it does not execute the backend API call. .PARAMETER confirm If enabled the backend API Call has to be confirmed .EXAMPLE To be added in the Future .NOTES Precondition: User has “manage” permissions in the parent room. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param ( [parameter(Mandatory)] [Dracoon]$Connection, [parameter(Mandatory)] [string]$RoomName, [int]$ParentNodeId, [parameter(Mandatory)] [array]$AdminUserId, [array]$AdminGroupId, [int]$RecycleBinRetentionPeriod = 14, [bool]$InheritPermissions = $false, [string]$Notes = "", [ValidateSet("autoallow", "pending")] [String]$NewGroupMemberAcceptance = "autoallow", [int]$Quota = 0, [bool]$HasActivitiesLog = $true, $Classification=2 ) Write-PSFMessage "Erzeuge Datenraum $RoomName" $apiCallParameter = @{ Connection = $Connection method = "Post" Path = "/v4/nodes/rooms" Body = @{ name = "$RoomName" recycleBinRetentionPeriod = $RecycleBinRetentionPeriod quota = $Quota inheritPermissions = $InheritPermissions adminIds = $AdminUserId adminGroupIds = $adminGroupId newGroupMemberAcceptance = $NewGroupMemberAcceptance notes = $Notes hasActivitiesLog = $HasActivitiesLog classification = $Classification hasRecycleBin = $true } # EnablePaging=$true } if ($parentNodeId -gt 0) { $apiCallParameter.body.parentId = $parentNodeId } Invoke-PSFProtectedCommand -Action "Creating new dataroom" -Target "$RoomName" -ScriptBlock { $result = Invoke-DracoonAPI @apiCallParameter Write-PSFMessage "User erfolgreich angelegt" $result } -PSCmdlet $PSCmdlet } function New-DracoonDownloadShare { <# .SYNOPSIS Creates a Download Share for an existing file node .DESCRIPTION API-POST /v4/shares/downloads .PARAMETER connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER NodeId Node-ID of the file .PARAMETER Notes Description notes for the share .PARAMETER InternalNotes Internal Description notes for the share .PARAMETER ShareName Descriptive Name of the download share. .PARAMETER MaxDownloads How often should the file be downloadable? 0 for infinity. .PARAMETER ShowCreatorName Should the Share-Creator-Name be displayed? Defaults to true .PARAMETER ShowCreatorUsername Should the Share-Creator-Username be displayed? Defaults to false .PARAMETER NotifyCreator Should the creator be informed about any download? Defaults to true .PARAMETER Password Password for accessing the shared file. See .PARAMETER RandomPassword .PARAMETER RandomPassword If used the password is generated randomly. .PARAMETER TextMessageRecipients Optional Array of text message recipients .PARAMETER whatIf If enabled it does not execute the backend API call. .PARAMETER confirm If enabled the backend API Call has to be confirmed .PARAMETER ExpirationDate Sets a date when the user will expire .PARAMETER EnableException If set to true, inner exceptions will be rethrown. Otherwise the an empty result will be returned. .EXAMPLE New-DracoonDownloadShare -Connection $connection -NodeId $NodeId -MaxDownloads 2 Creates a download share which lasts for 2 downloads. .EXAMPLE New-DracoonDownloadShare -Connection $connection -NodeId $NodeId -Password "fsdjfdsfhj8934234****" Creates a download share with set password .EXAMPLE New-DracoonDownloadShare -Connection $connection -NodeId $NodeId -RandomPassword Creates a download share with a random access password. .EXAMPLE New-DracoonDownloadShare -Connection $connection -NodeId $NodeId -RandomPassword -TextMessageRecipients "0123456789" Creates a download share with a random access password, sends the password to the given mobile number .NOTES #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUsernameAndPasswordParams', '')] [CmdletBinding(DefaultParameterSetName = "Default")] param ( [parameter(Mandatory = $true)] [Dracoon]$Connection, [parameter(Mandatory = $true)] [int]$NodeId, [string]$Notes = "", [string]$InternalNotes = "", [string]$ShareName = "Download-Share", [bool]$ShowCreatorName = $true, [bool]$ShowCreatorUsername = $false, [bool]$NotifyCreator = $true, [string[]]$TextMessageRecipients, [int]$MaxDownloads=0, [string]$Password, [switch]$RandomPassword, [datetime]$ExpirationDate, [bool]$EnableException = $false ) Write-PSFMessage "Creating Download Share for Node $NodeId" if ($RandomPassword){ $Password=(New-DracoonRandomPassword) } $apiCallParameter = @{ Connection = $Connection method = "Post" Path = "/v4/shares/downloads" Body = @{ "nodeId" = $NodeId "name" = $ShareName # "password" = $Password "expiration" = @{ "enableExpiration" = $false "expireAt" = "2018-01-01T00:00:00" } "internalNotes" = $InternalNotes "notes" = $notes "showCreatorName" = $ShowCreatorName "showCreatorUsername" = $ShowCreatorUsername "notifyCreator" = $NotifyCreator # "maxDownloads" = 0 "creatorLanguage" = "de-DE" "receiverLanguage" = "de-DE" # "textMessageRecipients" = @( # "string" # ) "sendMail" = $false # "sendSms" = $true # "smsRecipients" = "string" } # EnablePaging=$true } if ($ExpirationDate) { $apiCallParameter.Body.expiration.enableExpiration = 'true' $apiCallParameter.Body.expiration.expireAt = $ExpirationDate.ToString('yyyy-MM-ddT00:00:00') } if ($TextMessageRecipients){ $apiCallParameter.Body.textMessageRecipients = @($TextMessageRecipients) } if ($Password){ $apiCallParameter.Body.password = $Password } if ($MaxDownloads -gt 0){ $apiCallParameter.Body.maxDownloads = $MaxDownloads } Invoke-PSFProtectedCommand -Action "Creating new download-share" -Target "$NodeId" -ScriptBlock { $result = Invoke-DracoonAPI @apiCallParameter # Write-PSFMessage "User erfolgreich angelegt" $result } -PSCmdlet $PSCmdlet -EnableException $EnableException } function New-DracoonFolder { <# .SYNOPSIS Creates a new folder at the provided parent node. .DESCRIPTION API-POST /v4/nodes/folders .PARAMETER connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER Name Name of the new room .PARAMETER ParentNodeId Node-ID of the parent-node. .PARAMETER Notes Description notes for the folder .PARAMETER whatIf If enabled it does not execute the backend API call. .PARAMETER confirm If enabled the backend API Call has to be confirmed .PARAMETER EnableException If set to true, inner exceptions will be rethrown. Otherwise the an empty result will be returned. .EXAMPLE New-DracoonDataroom -Connection $connection -Name "MyFolder" -ParentRoomId $room.id Creates a folder within the defined room .NOTES Precondition: User has “create” permissions in current room. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'low')] param ( [parameter(Mandatory)] [Dracoon]$Connection, [parameter(Mandatory)] [string]$Name, [int]$ParentNodeId, [string]$Notes = "", [bool]$EnableException = $false ) Write-PSFMessage "Create folder $Name in room $ParentNodeID" $apiCallParameter = @{ Connection = $Connection method = "Post" Path = "/v4/nodes/folders" Body = @{ name = "$Name" parentId = $ParentNodeID notes = $Notes } } Invoke-PSFProtectedCommand -Action "Creating new folder" -Target "$RoomName" -ScriptBlock { $result = Invoke-DracoonAPI @apiCallParameter Write-PSFMessage "Folder successful created" $result } -PSCmdlet $PSCmdlet -EnableException $EnableException } function New-DracoonUser { <# .SYNOPSIS Create a new user. .DESCRIPTION Create a new user. Two option sets are possible: Mail-User (internal authentication) or Active Directory based authentification. .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER Login Unique login for the user, UPN/MAIL format expected .PARAMETER FirstName First name of the user. .PARAMETER LastName Last name of the user. .PARAMETER Title Title of the user. .PARAMETER Gender Gender of the user. .PARAMETER Mail Mail address of the user. .PARAMETER Domain Only needed for Domain based Authentication. .PARAMETER SamAccountName Login Name Only needed for Domain based Authentication. .PARAMETER ExpirationDate Sets a date when the user will expire .PARAMETER NotifyUser If set to true the user is notified by mail. .PARAMETER NeedsToChangePassword If set to true the user has to change the password on first login. .PARAMETER whatIf If enabled it does not execute the backend API call. .PARAMETER confirm If enabled the backend API Call has to be confirmed .EXAMPLE To be added in the Future .NOTES General notes #> [CmdletBinding(DefaultParameterSetName = "Mailuser", SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param ( [parameter(mandatory = $true, ParameterSetName = "Mailuser")] [parameter(mandatory = $true, ParameterSetName = "ADuser")] [Dracoon]$Connection, [parameter(mandatory = $true, ParameterSetName = "Mailuser")] [parameter(mandatory = $true, ParameterSetName = "ADuser")] [PSFValidateScript( { $_ -as [mailaddress] }, ErrorMessage = "{0} - is not a valid mail address")] [string]$Login, [parameter(mandatory = $true, ParameterSetName = "Mailuser")] [parameter(mandatory = $true, ParameterSetName = "ADuser")] [string]$FirstName, [parameter(mandatory = $true, ParameterSetName = "Mailuser")] [parameter(mandatory = $true, ParameterSetName = "ADuser")] [string]$LastName, [string]$Title = "", [ValidateSet('m', 'f', 'n')] [string]$Gender = 'n', [string]$Mail, [parameter(mandatory = $true, ParameterSetName = "ADuser")] [string]$Domain, [parameter(mandatory = $true, ParameterSetName = "ADuser")] [string]$SamAccountName, [parameter(mandatory = $false, ParameterSetName = "Mailuser")] [parameter(mandatory = $false, ParameterSetName = "ADuser")] [datetime]$ExpirationDate, [parameter(mandatory = $false, ParameterSetName = "Mailuser")] [bool]$NotifyUser = $false, [parameter(mandatory = $false, ParameterSetName = "Mailuser")] [bool]$NeedsToChangePassword = $false ) $apiCallParameter = @{ Connection = $Connection method = "Post" Path = "/v4/users" Body = @{ firstName = $FirstName lastName = $LastName authMethods = @() login = $Login title = $Title gender = $Gender expiration = @{ enableExpiration = "false" expireAt = "2018-01-01T00:00:00" } receiverLanguage = "de-DE" email = $Mail notifyUser = ("$NotifyUser").ToLower() needsToChangePassword = ("$NeedsToChangePassword").ToLower() password = New-DracoonRandomPassword } } if ($ExpirationDate) { $apiCallParameter.Body.expiration.enableExpiration = 'true' $apiCallParameter.Body.expiration.expireAt = $ExpirationDate.ToString('yyyy-MM-ddT00:00:00') } if ($Domain) { $adId = (Get-DracoonAuthConfigAD -Connection $Connection -Alias $Domain).id if (-not ($adId)) { throw "Unbekannter AD-Alias $domain" } Write-PSFMessage "Lege einen AD-User an ($Domain/$adId)" $apicallparameter.Body.authMethods += @{ authId = "active_directory" isEnabled = "true" options = @( @{ key = "ad_config_id" value = $adId } @{ key = "username" value = $samAccountName }) } } else { Write-PSFMessage "Lege einen SQL-User an ($Domain/$adId)" $apicallparameter.Body.authMethods += @{ authId = "sql" isEnabled = "true" } } write-psfmessage "($apiCallParameter|convertfrom-json -depth 10)" Invoke-PSFProtectedCommand -Action "Lege User an" -Target $Login -ScriptBlock { $result = Invoke-DracoonAPI @apiCallParameter Write-PSFMessage "User erfolgreich angelegt" $result } -PSCmdlet $PSCmdlet -Confirm:$false -Verbose if (Test-PSFFunctionInterrupt) { return } } function Publish-DracoonFile { <# .SYNOPSIS Uploads a file to an already existing dataroom. .DESCRIPTION Uploads a file to an already existing dataroom. .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER FilePath Filepath of the file which should get uploaded. .PARAMETER ParentNodeId ID of the target room. .PARAMETER ExpirationDate Optional expiration date for the file. .PARAMETER Classification Classification of the file. .PARAMETER Notes Notes for the file .PARAMETER ResolutionStrategy If the file already exists: Should it be overwritten (overwrite) ord should it be uploaded with an automatic name (autorename) .PARAMETER EnableException If set to $true errors throw an exception .PARAMETER whatIf If enabled it does not execute the backend API call. .PARAMETER confirm If enabled the backend API Call has to be confirmed .EXAMPLE Publish-DracoonFile -Connection $connection -FilePath $fileName -ParentNodeId $roomId Performs an upload of $fileName .NOTES Uploads consist of three steps: -Initialization - Announces the upload and creates a placeholder -Upload - Binary transfer of the file -Closing the upload - Tell Dracoon that the data has completely transfered #> [CmdletBinding(DefaultParameterSetName = "Upload", SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param ( [parameter(mandatory = $true)] [Dracoon]$Connection, [parameter(mandatory = $true)] [string]$FilePath, [parameter(mandatory = $true)] [int]$ParentNodeId, [datetime]$ExpirationDate, [int]$Classification = 2, [string]$Notes = "", [ValidateSet("overwrite", "autorename")] [string]$ResolutionStrategy = "autorename", [bool]$EnableException = $false ) $fullFilePath = Get-Item $FilePath -ErrorAction SilentlyContinue Write-PSFMessage "Upload of $FilePath ($fullFilePath), ResolutionStrategy=$ResolutionStrategy" if ($fullFilePath) { $apiCallParameter = @{ Connection = $Connection method = "Post" Path = "/v4/nodes/files/uploads" Body = @{"parentId" = $parentNodeId "name" = $fullFilePath.Name "classification" = $Classification "size" = $fullFilePath.length "expiration" = @{ "enableExpiration" = $false "expireAt" = "2018-01-01T00:00:00" } "notes" = $Notes } } if ($ExpirationDate) { $apiCallParameter.Body.expiration.enableExpiration = 'true' $apiCallParameter.Body.expiration.expireAt = $ExpirationDate.ToString('yyyy-MM-ddT00:00:00') } Write-PSFMessage "Init: $($apiCallParameter|convertTo-json -depth 10)" -Level Debug Invoke-PSFProtectedCommand -Action "Initialize Upload, Open Upload Channel" -Target $fullFilePath.Name -ScriptBlock { $initUpload = Invoke-DracoonAPI @apiCallParameter Write-PSFMessage "initUpload=$($initUpload|ConvertTo-Json -Depth 5)" -Level Debug Invoke-PSFProtectedCommand -Action "Upload File-Data" -Target $initUpload.token -ScriptBlock { $apiCallParameter = @{ Connection = $Connection method = "Post" Path = "/v4/uploads/$($initUpload.token)" ContentType = "application/octet-stream" InFile = $fullFilePath.FullName } $result = Invoke-DracoonAPI @apiCallParameter # $result = Invoke-RestMethod $initUpload.uploadUrl -ContentType "application/octet-stream" -Method Post -Headers $connection.headers -InFile $fullFilePath.FullName Write-PSFMessage $result Invoke-PSFProtectedCommand -Action "Close Upload Channel" -Target $initUpload.token -ScriptBlock { $apiCallParameter = @{ Connection = $Connection method = "Put" Path = "/v4/uploads/$($initUpload.token)" Body=@{ resolutionStrategy = $ResolutionStrategy } } $result = Invoke-DracoonAPI @apiCallParameter # $result = $this.Invoke(("/v4/uploads/{0}" -f $initUpload.token), $null, [Microsoft.Powershell.Commands.WebRequestMethod]::Put, $false) Write-PSFMessage "Upload successfull closed" return $result } -PSCmdlet $PSCmdlet -Verbose -EnableException $EnableException } -PSCmdlet $PSCmdlet -Verbose -EnableException $EnableException if (Test-PSFFunctionInterrupt) { Write-PSFMessage "Error uploading the file" Invoke-PSFProtectedCommand -Action 'Cleanup $initUpload.token' -Target $initUpload.token -ScriptBlock { $apiCallParameter = @{ Connection = $Connection method = "Delete" Path = "/v4/uploads/$($initUpload.token)" } Invoke-DracoonAPI @apiCallParameter } -PSCmdlet $PSCmdlet -Verbose -EnableException $EnableException } } -PSCmdlet $PSCmdlet -Verbose -EnableException $EnableException if (Test-PSFFunctionInterrupt) { return } } elseif ($EnableException) { Write-PSFMessage "File not found: $FilePath" throw "File not found: $FilePath" } } function Remove-DracoonNode { <# .SYNOPSIS Delete node (room, folder or file). API-DELETE /v4/nodes/{node_id} .DESCRIPTION Delete node (room, folder or file). Precondition: Authenticated user with “delete” permissions on: supplied nodes (for folders or files) superordinated node (for rooms) Effects: Node gets deleted. .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER NodeId ID of the node which should be deleted. .PARAMETER whatIf If enabled it does not execute the backend API call. .PARAMETER confirm If enabled the backend API Call has to be confirmed .EXAMPLE $rooms = Get-DracoonAuditDataroom -connection $connection -filter "nodeName:cn:DEMO" $hubbaRooms| Remove-DracoonNode -connection $connection Queries all rooms with "DEMO" within the nodeName and deletes them. Remove-DracoonNode -connection $connection -NodeId 15 Deletes the node with ID 15. .NOTES General notes #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param ( [parameter(Mandatory)] [Dracoon]$connection, [parameter(Mandatory, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias("ID")] [int]$NodeId ) process { $apiCallParameter = @{ Connection = $Connection method = "Delete" Path = "/v4/nodes/$NodeId" } Invoke-PSFProtectedCommand -Action "Removing Node" -Target "$Id" -ScriptBlock { $result = Invoke-DracoonAPI @apiCallParameter Write-PSFMessage "Node removed" $result } -PSCmdlet $PSCmdlet } } # [PSCustomObject]DeleteUser([int]$id, [bool]$deleteLastAdminRooms) { # if ($deleteLastAdminRooms) { # $LastAdminRooms = $this.getLastAdminRooms($id) # if ($LastAdminRooms) { # Write-PSFMessage ("Lösche {0} LastAdminRooms" -f $LastAdminRooms.count) # $LastAdminRooms | Format-Table | Out-String | Write-PSFMessage # foreach ($room in $LastAdminRooms) { # $this.DeleteRoom($room.id) # } # } # } # $result = $this.InvokeDelete("/v4/users/$id") # return $result # } function Remove-DracoonUser { <# .SYNOPSIS Delete a user. API-DELETE /v4/users/{user_id} .DESCRIPTION Delete a user. API-DELETE /v4/users/{user_id} .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER Id ID of the User which should be deleted. .PARAMETER DeleteLastAdminRooms If true, the function will check if the user is the last admin of any data room. If yes, the rooms will be removed first. .PARAMETER whatIf If enabled it does not execute the backend API call. .PARAMETER confirm If enabled the backend API Call has to be confirmed .EXAMPLE To be added in the Future .NOTES General notes #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param ( [parameter(Mandatory)] [Dracoon]$connection, [parameter(Mandatory, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [int]$Id, [bool]$DeleteLastAdminRooms = $false ) process { $apiCallParameter = @{ Connection = $Connection method = "Delete" Path = "/v4/users/$Id" } Write-PSFMessage "Lösche User $Id" if ($DeleteLastAdminRooms) { Write-PSFMessage "Check if the user is last admin of some rooms" $lastAdminRooms = Get-DracoonLastAdminRoom -Connection $connection -id $id if ($lastAdminRooms) { Write-PSFMessage "Removing $($lastAdminRooms.count) rooms" $lastAdminRooms | Remove-DracoonNode -Connection $connection } } Invoke-PSFProtectedCommand -Action "Removing User" -Target "$Id" -ScriptBlock { $result = Invoke-DracoonAPI @apiCallParameter Write-PSFMessage "User removed" $result } -PSCmdlet $PSCmdlet } } function Request-DracoonOAuthToken { <# .SYNOPSIS Helper-Function for creation of OAuth Tokens. .DESCRIPTION The function uses OAuth for creating an refresh token which can be used for login to a dracoon instance. For connecting to Dracoon via OAuth you always need the -Url as the function needs to know where the Server is located. Furthermore you need an AccessToken which can be requested with this function. Besides the ClientId/ClientSecret you need to provide one of the following: -Credential -RefreshToken -AuthToken The RefreshToken and AuthToken can be generated with this function, too. For more information about OAuth see about_Dracoon. .PARAMETER Url Base-URL of the Dracoon Server .PARAMETER Credential Credential object used for login. .PARAMETER RefreshToken As an alternative a refresh token can be used instead of a credential Object .PARAMETER AuthToken Authorization Token/Code from Three Legged OAuth Workflow .PARAMETER ClientID OAuth client ID .PARAMETER ClientSecret OAuth client secret .PARAMETER TokenType Defines the type of token to be returned, default "access" .EXAMPLE $accessToken=Request-DracoonOAuthToken -ClientID $clientId -ClientSecret $clientSecret -Url $url -Credential $cred -TokenType access #Creates an AccessToken which can be used for simple connection- $connection=Connect-Dracoon -Url $url -AccessToken $accessToken .EXAMPLE $refreshToken=Request-DracoonOAuthToken -ClientID $clientId -ClientSecret $clientSecret -Credential $cred -url $url -TokenType refresh # Creates a refresh token which can be exchanged for an accessToken. $accessToken=Request-DracoonOAuthToken -ClientID $clientId -ClientSecret $clientSecret -Url $url -RefreshToken $refreshToken .EXAMPLE Request-DracoonOAuthToken -url $url -ClientID $ClientID Opens the default browser for aquiring an authorization code. .EXAMPLE #Read authorization code and generate an access code from it. $tempCred = Get-Credential -Message "Please perform browser login" -UserName "Enter AuthorizationCode as PW" $authToken = $tempCred.GetNetworkCredential().Password $accessToken = Request-DracoonOAuthToken -url $url -ClientID $ClientID -clientSecret $clientSecret -AuthToken $authToken .NOTES General notes #> param ( [parameter(mandatory = $true, ParameterSetName = "ThreeLeggedOAuth")] [parameter(mandatory = $true, ParameterSetName = "authorization_code")] [parameter(mandatory = $true, ParameterSetName = "password")] [parameter(mandatory = $true, ParameterSetName = "refresh_token")] [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("Dracoon.url")] [string]$Url, [parameter(mandatory = $true, ParameterSetName = "ThreeLeggedOAuth")] [parameter(mandatory = $true, ParameterSetName = "authorization_code")] [parameter(mandatory = $true, ParameterSetName = "password")] [parameter(mandatory = $true, ParameterSetName = "refresh_token")] [string]$ClientID, [parameter(mandatory = $true, ParameterSetName = "authorization_code")] [parameter(mandatory = $true, ParameterSetName = "password")] [parameter(mandatory = $true, ParameterSetName = "refresh_token")] [string]$ClientSecret, [parameter(mandatory = $true, ParameterSetName = "password")] [pscredential]$Credential, [parameter(mandatory = $true, ParameterSetName = "authorization_code")] [string]$AuthToken, [parameter(mandatory = $true, ParameterSetName = "refresh_token")] [string]$RefreshToken, [ValidateSet('refresh', 'access')] [System.String]$TokenType = 'access' ) $serverRoot = Get-DracoonServerRoot $Url $callbackUrl = "$serverRoot/oauth/callback" Write-PSFMessage "ParameterSet: $($PSCmdlet.ParameterSetName)" if ($PSCmdlet.ParameterSetName -eq 'ThreeLeggedOAuth') { # Open Browser to request the accesstoken $callbackUrl = [System.Web.HttpUtility]::UrlEncode($callbackUrl) $openUrl = "$serverRoot/oauth/authorize?response_type=code&client_id=$ClientId&state=xyz&redirect_uri=$callbackUrl" Start-Process $openUrl Write-PSFMessage -Level Host "Starting Default Browser for Access Token Generation" } else { $Base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $ClientID, $ClientSecret))) switch ($PSCmdlet.ParameterSetName) { "password" { $parameter = @{ "grant_type" = "password"; "username" = $Credential.UserName; "password" = $Credential.GetNetworkCredential().password } } "refresh_token" { $parameter = @{ "grant_type" = "refresh_token"; "refresh_token" = "$RefreshToken" } } "authorization_code" { $parameter = @{ grant_type = "authorization_code"; code = $AuthToken ; redirect_uri = $callbackUrl } # $parameter="foo=bar&grant_type=authorization_code&code=$AuthToken&redirect_uri=$callbackUrl&bar=foo" # Write-PSFMessage "parameter=$($parameter|convertto-json)" } Default { Write-PSFMessage -Level Critical "Unknown ParameterSetName $($PSCmdlet.ParameterSetName)" } } # Write-PSFMessage "parameter=$($parameter|convertto-json)" $tokenParameter = @{ URI = "$serverRoot/oauth/token" Method = "Post" ContentType = "application/x-www-form-urlencoded" Body = $parameter #($parameter | convertto-json) Headers = @{ Authorization = ("Basic {0}" -f $Base64AuthInfo) } } # Write-PSFMessage "tokenParameter=$($tokenParameter|convertto-json)" try { Write-DracoonAPICallMessage $tokenParameter $tokenResponse = Invoke-WebRequest @tokenParameter # Write-PSFMessage "tokenResponse=$tokenResponse" if (($TokenType -eq 'access') -or $RefreshToken) { $token = (ConvertFrom-Json $tokenResponse.Content).access_token } else { $token = (ConvertFrom-Json $tokenResponse.Content).refresh_token } return $token } catch { Write-PSFMessage "Exception: $_" $streamReader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream()) $errResp = $streamReader.ReadToEnd() | ConvertFrom-Json $streamReader.Close() Write-PSFMessage "Error-Response=$ErrResp" } } } function Search-DracoonNode { <# .SYNOPSIS Provides a flat list of file system nodes (rooms, folders or files) of a given parent that are accessible by the current user. API-GET /v4/nodes/search .DESCRIPTION Provides a flat list of file system nodes (rooms, folders or files) of a given parent that are accessible by the current user. .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER Filter All filter fields are connected via logical conjunction (AND) Filter string syntax: FIELD_NAME:OPERATOR:VALUE[:VALUE...] Example: type:eq:file|createdAt:ge:2015-01-01 Get nodes where type equals file AND file creation date is >= 2015-01-01. Possible combinations: 'type:[eq]:[room/folder/file]' 'fileType:[cn/eq]:[search String]' 'classification:[eq]:[1 - public/2 - internal/3 - confidential/4 - strictly confidential]' 'createdBy:[cn/eq]:[search String]' 'createdAt:[ge/le]:[Date (yyyy-MM-dd)]' 'updatedBy:[cn/eq]:[search String]' 'updatedAt:[ge/le]:[Date (yyyy-MM-dd)]' 'expireAt:[ge/le]:[Date (yyyy-MM-dd)]' 'size:[ge/le]:[size in bytes]' 'isFavorite:[eq]:[true or false]' 'branchVersion:[ge/le]:[version number]' 'parentPath:[cn/eq]:[search String]' .PARAMETER Limit Range limit. Maximum 500. For more results please use paging (offset + limit). .PARAMETER Offset Range offset .PARAMETER Sort Sort string syntax: FIELD_NAME:ORDER ORDER can be asc or desc. Multiple sort fields are NOT supported. Nodes are sorted by type first, then by sent sort string. Example: name:desc .PARAMETER ParentID Parent node ID. Only rooms and folders can be parents. Parent ID 0 or empty is the root node. .PARAMETER DepthLevel 0 - top level nodes only (default) -1 - full tree n (any positive number) - include n levels starting from the current node .PARAMETER SearchString String to be searched in the NodeName .EXAMPLE To be added in the Future .NOTES General notes #> param ( [parameter(Mandatory)] [Dracoon]$Connection, [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("Dracoon.filter")] [string]$Filter, [int]$Limit=500, [int]$Offset=0, [string]$Sort, [int]$ParentID = 0, [int]$DepthLevel = 0, [parameter(Mandatory)] [string]$SearchString ) $apiCallParameter = @{ Connection = $Connection method = "Get" Path ="/v4/nodes/search" EnablePaging = $true UrlParameter = @{ filter=$Filter limit=$Limit sort=$Sort offset=$offset depth_level=$DepthLevel parent_id=$ParentID room_manager=$RoomManager search_string = $SearchString } # EnablePaging=$true } $result = Invoke-DracoonAPI @apiCallParameter $result } function Send-DracoonDownloadShareMail { <# .SYNOPSIS Sends an E-Mail for an existing Download Share. .DESCRIPTION API-POST /v4/shares/downloads/{share_id}/email .PARAMETER connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER Id ID of the existing Download Share .PARAMETER MailBody Body for the generated Mail .PARAMETER Recipients Optional Array of text message recipients .PARAMETER ReceiverLanguage Language of the receiver .PARAMETER whatIf If enabled it does not execute the backend API call. .PARAMETER confirm If enabled the backend API Call has to be confirmed .PARAMETER EnableException If set to true, inner exceptions will be rethrown. Otherwise the an empty result will be returned. .EXAMPLE $newShare=New-DracoonDownloadShare -Connection $connection -NodeId $NodeId -MaxDownloads 2 $currentUser=Get-DracoonCurrentAccount -Connection $connection Send-DracoonDownloadShareMail -Connection $connection -Id $newShare.id -Recipient $currentUser.email -MailBody "This is the body" Sends an E-Mail to the current user mail address. .NOTES #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param ( [parameter(Mandatory = $true)] [Dracoon]$Connection, [parameter(Mandatory = $true)] [int]$Id, [parameter(Mandatory = $true)] [string]$MailBody, [parameter(Mandatory = $true)] [string[]]$Recipients, [string]$ReceiverLanguage = "de-DE", [bool]$EnableException = $false ) Write-PSFMessage "Creating E-Mail for Download Share $Id" $apiCallParameter = @{ Connection = $Connection method = "Post" Path = "/v4/shares/downloads/$Id/email" Body = @{ "recipients" = @($Recipients) "body" = $MailBody "receiverLanguage" = $ReceiverLanguage } } Invoke-PSFProtectedCommand -Action "Creating E-Mail for Download Share" -Target "$Id" -ScriptBlock { $result = Invoke-DracoonAPI @apiCallParameter -EnableException $EnableException $result } -PSCmdlet $PSCmdlet -EnableException $EnableException } function Set-DracoonRoomAcl { <# .SYNOPSIS Add or change room granted user(s). API-PUT /v4/nodes/rooms/$NodeId/users .DESCRIPTION Batch function. All existing user permissions will be overwritten. .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER NodeId ID of the room .PARAMETER NewPermission Array of the new Permission Items. [ { "id": 0, "permissions": { "manage": true, "read": true, "create": true, "change": true, "delete": true, "manageDownloadShare": true, "manageUploadShare": true, "readRecycleBin": true, "restoreRecycleBin": true, "deleteRecycleBin": true } } ] .PARAMETER whatIf If enabled it does not execute the backend API call. .PARAMETER confirm If enabled the backend API Call has to be confirmed .EXAMPLE To be added in the Future .NOTES General notes #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param ( [parameter(Mandatory)] [Dracoon]$Connection, [parameter(Mandatory)] [int]$NodeId, [array]$NewPermission ) $apiCallParameter = @{ Connection = $Connection method = "Put" Path = "/v4/nodes/rooms/$NodeId/users" Body=@{ items=@() } } $apiCallParameter.Body.items += $NewPermission Invoke-PSFProtectedCommand -Action "Setting permissions on node" -Target "$NodeId" -ScriptBlock { $result = Invoke-DracoonAPI @apiCallParameter Write-PSFMessage "Permissions set" $result } -PSCmdlet $PSCmdlet } function Set-DracoonUrl { <# .SYNOPSIS This function allows to set new Server-URLs for TAB Completion. Each function which requires a -Url parameter will provide a TAB completer with suggested URLs. .DESCRIPTION This function allows to set new Server-URLs for TAB Completion. Each function which requires a -Url parameter will provide a TAB completer with suggested URLs, e.g. Connect-Dracoon Different from Add-DracoonUrl this command overwrites existing settings. .PARAMETER NewUrl The new URLs to be added .PARAMETER whatIf If enabled it does not execute the backend API call. .PARAMETER confirm If enabled the backend API Call has to be confirmed .EXAMPLE Add-DracoonUrl 'https://dxi.mydomain' Add a single Server to the list of suggested URLs (get-adforest -ErrorAction Stop).domains | ForEach-Object { Add-DracoonUrl "https://dataexchange.$($_)" } If you have an on prem Dracoon server in each of your Windows Domains with the address "https://dracoon.<yourdomain>" it will get added to the list of suggested URLs. .NOTES The URLs get saved at the PSF-Config "Dracoon.tepp.urls" #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'medium')] param ( [parameter(mandatory = $true, Position = 0)] [string[]]$NewUrl ) Write-PSFMessage "Saving new Urls for the URL TEPP: $NewUrl" # Adjusting format of URLs $NewUrl = $NewUrl | ForEach-Object { Get-DracoonServerRoot $_} Invoke-PSFProtectedCommand -Action "Saving new Urls for the URL TEPP" -Target "$NewUrl" -ScriptBlock { Set-PSFConfig -Module 'Dracoon' -Name 'tepp.urls' -Value $NewUrl -AllowDelete -PassThru | Register-PSFConfig } -PSCmdlet $PSCmdlet } # [PSCustomObject]SetUserAttributes([int]$userId, [Hashtable]$userAttributes, [bool]$keepExisting) { # $items = @() # foreach ($key in $userAttributes.Keys) { # $items += @{ key = $key ; value = $userAttributes[$key] } # } # $parameter = @{items = $items } # if ($keepExisting) { # $result = $this.InvokePut("/v4/users/$userId/userAttributes", $parameter) # } # else { # $result = $this.InvokePost("/v4/users/$userId/userAttributes", $parameter) # } # return $result # } function Set-DracoonUserAttribute { <# .SYNOPSIS Set custom user attributes. API-(POST/PUT) /v4/users/{user_id}/userAttributes .DESCRIPTION Set custom user attributes. Uses POST for overwriting the userAttributes or PUT for updating the userAttributes. .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .PARAMETER id ID of the user to be changed. .PARAMETER UserAttributes HashTable wit the UserAttributes. .PARAMETER Overwrite Shall all exisiting attributes be overwritten? Default False. .PARAMETER whatIf If enabled it does not execute the backend API call. .PARAMETER confirm If enabled the backend API Call has to be confirmed .EXAMPLE To be added in the Future .NOTES If the operation fails the function throws the exception #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param ( [parameter(mandatory = $true)] [Dracoon]$Connection, [parameter(Mandatory)] [int]$Id, [Hashtable]$UserAttributes, [bool]$Overwrite = $false ) $itemArray = @() Write-PSFMessage "Setze User-Attribute für User $id auf $UserAttributes" foreach ($key in $UserAttributes.Keys) { $itemArray += @{ key = $key ; value = $userAttributes[$key] } } if ($Overwrite) { $method = "Post" }else { $method = "Put" } $apiCallParameter = @{ Connection = $Connection method = $method Path = "/v4/users/$Id/userAttributes" Body = @{items = $itemArray } } Invoke-PSFProtectedCommand -Action "Setting attributes on user" -Target "$Id" -ScriptBlock { $result = Invoke-DracoonAPI @apiCallParameter Write-PSFMessage "Attribute set" $result } -PSCmdlet $PSCmdlet -EnableException $true } function Test-DracoonConnection { <# .SYNOPSIS Test connection to DRACOON Server. API-GET /v4/auth/ping .DESCRIPTION Test connection to DRACOON Server. API-GET /v4/auth/ping .PARAMETER Connection Object of Class [Dracoon], stores the authentication Token and the API Base-URL .EXAMPLE Test-DracoonConnection $connection Throws a [System.Net.NetworkInformation.PingException] if connection does not succeed, otherwise it returns $true .NOTES General notes #> param ( [parameter(Mandatory)] [Dracoon]$Connection ) $apiCallParameter = @{ Connection = $Connection method = "Get" Path ="/v4/auth/ping" } try { $result=Invoke-DracoonAPI @apiCallParameter Write-PSFMessage "Ping result: $result" if ($result -notmatch '^OK'){ throw [System.Net.NetworkInformation.PingException]::new("API not pingable, $($connection.serverRoot)/v4/auth/ping") } } catch { throw [System.Net.NetworkInformation.PingException]::new("API not pingable, $($connection.serverRoot)/v4/auth/ping") } $true } <# This is an example configuration file By default, it is enough to have a single one of them, however if you have enough configuration settings to justify having multiple copies of it, feel totally free to split them into multiple files. #> <# # Example Configuration Set-PSFConfig -Module 'Dracoon' -Name 'Example.Setting' -Value 10 -Initialize -Validation 'integer' -Handler { } -Description "Example configuration setting. Your module can then use the setting using 'Get-PSFConfigValue'" #> Set-PSFConfig -Module 'Dracoon' -Name 'Import.DoDotSource' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be dotsourced on import. By default, the files of this module are read as string value and invoked, which is faster but worse on debugging." Set-PSFConfig -Module 'Dracoon' -Name 'Import.IndividualFiles' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be imported individually. During the module build, all module code is compiled into few files, which are imported instead by default. Loading the compiled versions is faster, using the individual files is easier for debugging and testing out adjustments." Set-PSFConfig -Module 'Dracoon' -Name 'tepp.urls' -Value "https://dracoon.team" -Initialize -Description "List of URLs for TabCompletion." Set-PSFConfig -Module 'Dracoon' -Name 'logging.compressRequests' -Value $true -Initialize -Validation 'bool' -Description "Every API call is logged with the tag APICALL. If this setting is $true, the corresponding JSON String will be compressed. Set to $false for better readability." <# Stored scriptblocks are available in [PsfValidateScript()] attributes. This makes it easier to centrally provide the same scriptblock multiple times, without having to maintain it in separate locations. It also prevents lengthy validation scriptblocks from making your parameter block hard to read. Set-PSFScriptblock -Name 'Dracoon.ScriptBlockName' -Scriptblock { } #> Register-PSFTeppScriptblock -Name "Dracoon.filter" -ScriptBlock { try { # Get inline help of configured command $help = get-help $commandName # Get help for the parameter -Filter $parameterHelp = $Help.parameters.parameter | Where-Object Name -EQ 'Filter' $filterDescription = $parameterHelp.Description.Text # Extract filter examples, format: # 'attribute:operator:[possible Values]' $pattern = "'(\w*?):\[?([\w, \/]*?)\]?:\[?([\w, \/]*?)\]?',?" $results = $filterDescription | Select-String $pattern -AllMatches foreach ($match in $results.Matches) { $attribute = $match.Groups[1] # Inline help may provide multiple operators, divided by '/' $operators = $match.Groups[2] -split '/' # Inline help may provide multiple value examples, divided by '/' $valueExamples = $match.Groups[3] -split '/' foreach ($operator in $operators) { foreach ($value in $valueExamples) { "'$($attribute):$($operator):$($value)'" } } } } catch { Write-PSFMessage -Level Debug "Could not load filter information from Get-Help $commandName" } } Register-PSFTeppScriptblock -Name "Dracoon.url" -ScriptBlock { $staticList=@('https://dracoon.team') try { $urlList=Get-PSFConfigValue "Dracoon.tepp.urls" -Fallback $staticList return $urlList } catch { return $staticList } } <# # Example: Register-PSFTeppScriptblock -Name "Dracoon.alcohol" -ScriptBlock { 'Beer','Mead','Whiskey','Wine','Vodka','Rum (3y)', 'Rum (5y)', 'Rum (7y)' } #> <# # Example: Register-PSFTeppArgumentCompleter -Command Get-Alcohol -Parameter Type -Name Dracoon.alcohol #> New-PSFLicense -Product 'Dracoon' -Manufacturer 'Sascha Spiekermann' -ProductVersion $script:ModuleVersion -ProductType Module -Name MIT -Version "1.0.0.0" -Date (Get-Date "2020-09-21") -Text @" Copyright (c) 2020 Sascha Spiekermann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. "@ Export-PSFModuleClass -ClassType ([Dracoon]) [System.Net.ServicePointManager]::SecurityProtocol = "Tls12" #endregion Load compiled code |