Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions FileShares/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Network File Sharing Sample App

ASP.NET Core sample app showing how to use Steeltoe to manage credentialed connections with Windows file shares.

## General pre-requisites

1. Windows machine with the .NET 8 SDK installed
1. Pre-existing Windows file share or local adminstrator rights
1. Optional: [Tanzu Platform for Cloud Foundry](https://techdocs.broadcom.com/us/en/vmware-tanzu/platform/tanzu-platform-for-cloud-foundry/10-0/tpcf/concepts-overview.html)
with [Windows support](https://techdocs.broadcom.com/us/en/vmware-tanzu/platform/tanzu-platform-for-cloud-foundry/10-0/tpcf/toc-tasw-install-index.html)

## Running locally

Before running the app, you need to create the fileshare or update `appsettings.Development.json` with the values for a pre-existing fileshare.

### Create a file share

> [!CAUTION]
> The script [add-user-and-share.ps1](../../scripts/add-user-and-share.ps1) must be run as administrator, in Windows.
> As with any script found on the internet, review the contents before running it.

1. Open a PowerShell window as an administrator
1. `cd` to the `scripts` directory
1. Run [add-user-and-share.ps1](../../scripts/add-user-and-share.ps1), optionally using parameters to override the default values:
* `-ShareName steeltoe_network_share` - the name of the share
* `-SharePath c:\steeltoe_network_share` - the path to the share
* `-UserName shareWriteUser` - the name of the user
* `-Password thisIs1Pass!` - the password for the user

### Using an existing file share

1. Open the `appsettings.Development.json` file
1. Update the `location`, `username`, and `password` values with the appropriate values for your file share
1. Save the file

### Run the app

1. Open a terminal window
1. `cd` to the `src\FileSharesWeb` directory
1. Run the app with the following command:
```shell
dotnet run --launch-profile https
```

Once the app is running, you should be able to [upload files](https://localhost:7032/files/upload) and [list files](https://localhost:7032/files/list) in the file share.
Multiple files can be uploaded at once using the form provided, but you should be aware that files are renamed when they are saved in order to prevent issues with improper characters.
You can also delete files by clicking the "Delete file" button in the same row as the file name on the list files page.

> [!TIP]
> The sample uses credentials different from those of your Windows user account. If you've opened the file share in Windows Explorer before running the sample, it fails because a file share can't be accessed by one user using multiple credentials. To recover, run `klist purge` to make Windows forget the connection from Windows Explorer.


### Removing the local user account and file share

> [!CAUTION]
> The script [remove-user-and-share.ps1](../../scripts/remove-user-and-share.ps1) must be run as administrator, in Windows.
> As with any script found on the internet, review the contents before running it.

When you are done working with the sample, you can remove the user account, the file share, and its target directory, with the following steps:

1. Open a PowerShell window as an administrator
1. `cd` to the `scripts` directory
1. Run [remove-user-and-share.ps1](../../scripts/remove-user-and-share.ps1), optionally using parameters to override the default values:
* `-ShareName steeltoe_network_share` - the name of the share
* `-SharePath c:\steeltoe_network_share` - the path to the share
* `-UserName shareWriteUser` - the name of the user

## Running on Tanzu Platform for Cloud Foundry

Before deploying the app, you must create an entry in CredHub to contain the credentials.

### Store credentials in CredHub

1. Run [cf-create-service.ps1](../../scripts/cf-create-service.ps1) to create a service instance in CredHub, using parameters to set the required values:
* `-NetworkAddress \\\\<hostname>\\<sharename>` - escaped UNC path of the fileshare
* `-UserName <username>` - the username for accessing the fileshare
* `-Password <password>` - the password for accessing the fileshare

### Deploy the app

1. This sample will only run on Windows, so binaries must be built locally before push. Use the following commands to publish and push the app:
```shell
dotnet publish -r win-x64 --self-contained
cf push -f manifest-windows.yml -p bin/Release/net8.0/win-x64/publish
```
1. Copy the value of `routes` in the output and open in your browser

---

See the Official [Steeltoe Windows Network File Shares Documentation](https://docs.steeltoe.io/api/v4/fileshares/) for more detailed information.
74 changes: 74 additions & 0 deletions FileShares/scripts/add-user-and-share.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#Requires -RunAsAdministrator
#Requires -Modules Microsoft.PowerShell.LocalAccounts, SmbShare

Param(
[string]$ShareName = "steeltoe_network_share",
[string]$SharePath = "c:\steeltoe_network_share",
[string]$UserName = "shareWriteUser",
[string]$Password = "thisIs1Pass!"
)
$ErrorActionPreference = "Stop"
if ($PSVersionTable.PSVersion.Major -lt 6)
{
Write-Output "Running in Windows PowerShell (version < 6)"
}
else
{
Write-Output "Running in PowerShell (Pwsh) 7+"
Add-Type -AssemblyName System.Management.Automation
Import-Module Microsoft.PowerShell.LocalAccounts -SkipEditionCheck
}
$SecurePassword = ConvertTo-SecureString -String $Password -AsPlainText -Force

if (Get-LocalUser -Name $UserName -ErrorAction SilentlyContinue)
{
Write-Host "User $UserName already exists."
}
else
{
Write-Host "Creating local user $UserName..."
New-LocalUser $UserName `
-Password $SecurePassword `
-FullName "SMB ReadWrite" `
-Description "For write access to $ShareName" | Out-Null
Write-Host "Done creating user."
}

if (Get-LocalGroupMember -Group "Users" -Member $UserName -ErrorAction SilentlyContinue)
{
Write-Host "$UserName is already a member of the 'Users' group."
}
else
{
Write-Host "Adding $UserName to 'Users' group..."
Add-LocalGroupMember -Group "Users" -Member $UserName
Write-Host "Done adding user to group."
}

if (Get-Item -Path $SharePath -ErrorAction SilentlyContinue)
{
Write-Host "Directory $SharePath already exists."
}
else
{
Write-Host "Creating directory $SharePath..."
New-Item -ItemType directory -Path $SharePath | Out-Null
Write-Host "Done creating directory."
}

if (Get-SmbShare $ShareName -ErrorAction SilentlyContinue)
{
Write-Host "SMB share $ShareName already exists."
}
else
{
# Share the directory:
# - allow all "Users" to read
# - grant full control to current user and $UserName
Write-Host "Creating SMB share '$ShareName'..."
New-SmbShare -Name $ShareName `
-Path $SharePath `
-ReadAccess "Everyone" `
-FullAccess $UserName, $env:UserName | Out-Null
Write-Host "Done creating share, now available at this path: \\$Env:COMPUTERNAME\$ShareName"
}
18 changes: 9 additions & 9 deletions FileShares/scripts/cf-create-service.ps1
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
Param(
[string]$networkAddress = "\\\\localhost\\steeltoe_network_share",
[string]$username = "shareWriteUser",
[string]$password = "thisIs1Pass!"
[Parameter(Mandatory = $true, HelpMessage = "Escaped UNC path. For example, if the path is '\\localhost\steeltoe_network_share', use '\\\\localhost\\steeltoe_network_share'.")][string]$NetworkAddress,
[Parameter(Mandatory=$true)][string]$UserName,
[Parameter(Mandatory=$true)][string]$Password,
[string]$ServiceName = "credhub",
[string]$ServicePlan = "default",
[string]$ServiceInstanceName = "sampleNetworkShare"
)
$ErrorActionPreference = "Stop"

$serviceName = "credhub"
$servicePlan = "default"
$serviceInstanceName = "steeltoe-network-share"
$ParamJSON = [string]::Format('{{\"location\":\"{0}\",\"username\":\"{1}\",\"password\":\"{2}\"}}', $NetworkAddress, $UserName, $Password)

$paramJSON = [string]::Format('{{\"location\":\"{0}\",\"username\":\"{1}\",\"password\":\"{2}\"}}', $networkAddress, $username, $password)
Write-Host "cf create-service $ServiceName $ServicePlan $ServiceInstanceName -c $ParamJSON -t $ServiceInstanceName"

#Create the service instance
cf create-service $serviceName $servicePlan $serviceInstanceName -c $paramJSON -t $serviceInstanceName
cf create-service $ServiceName $ServicePlan $ServiceInstanceName -c $ParamJSON -t $ServiceInstanceName
33 changes: 0 additions & 33 deletions FileShares/scripts/create-user-and-share.ps1

This file was deleted.

60 changes: 60 additions & 0 deletions FileShares/scripts/remove-user-and-share.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#Requires -RunAsAdministrator
#Requires -Modules Microsoft.PowerShell.LocalAccounts, SmbShare

Param(
[string]$ShareName = "steeltoe_network_share",
[string]$SharePath = "c:\steeltoe_network_share",
[string]$UserName = "shareWriteUser"
)
$ErrorActionPreference = "Stop"
if ($PSVersionTable.PSVersion.Major -lt 6)
{
Write-Output "Running in Windows PowerShell (version < 6)"
}
else
{
Write-Output "Running in PowerShell (Pwsh) 7+"
Add-Type -AssemblyName System.Management.Automation
Import-Module Microsoft.PowerShell.LocalAccounts -SkipEditionCheck
}
if (Get-SmbShare $ShareName -ErrorAction SilentlyContinue)
{
Remove-SmbShare -Name $ShareName
Write-Host "SMB share $ShareName removed."
}
else
{
Write-Host "SMB share $ShareName was not found."
}

if (Get-LocalUser -Name $UserName -ErrorAction SilentlyContinue)
{
if (Get-LocalGroupMember -Group "Users" -Member $UserName -ErrorAction SilentlyContinue)
{
Write-Host "Removing $UserName from local 'Users' group..."
Remove-LocalGroupMember -Group "Users" -Member $UserName
Write-Host "User removed from group."
}
else
{
Write-Host "User $UserName was not found in 'Users' group."
}
Write-Host "Removing local user $UserName..."
Remove-LocalUser -Name $UserName
Write-Host "User completely removed."
}
else
{
Write-Host "User $UserName was not found."
}

if (Get-Item -Path $SharePath -ErrorAction SilentlyContinue)
{
Write-Host "Removing $SharePath from disk..."
Remove-Item -Path $SharePath -Recurse
Write-Host "Directory completely removed."
}
else
{
Write-Host "$SharePath was not found."
}
55 changes: 55 additions & 0 deletions FileShares/src/FileSharesWeb/Controllers/FilesController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System.Web;
using Microsoft.AspNetCore.Mvc;
using Steeltoe.Samples.FileSharesWeb.Models;
using SystemFile = System.IO.File;

namespace Steeltoe.Samples.FileSharesWeb.Controllers;

public sealed class FilesController(FileShareConfiguration fileShareConfiguration, TimeProvider timeProvider) : Controller
{
[HttpGet]
public IActionResult Upload()
{
return View();
}

[HttpPost]
public async Task<IActionResult> Upload(List<IFormFile>? files)
{
UploadViewModel model = new();

if (files is null)
{
model.Error = "No files were uploaded, this could happen if the file is too large.";
}
else
{
model.Files = [];

foreach (IFormFile file in files)
{
string sanitizedFileName = string.Join('_', file.FileName.Split(Path.GetInvalidFileNameChars()));
string saveFileAs = $"UPLOADED_{timeProvider.GetUtcNow():yyyyMMdd-hhmmss}_{sanitizedFileName}";
await using var stream = new FileStream(Path.Combine(fileShareConfiguration.Location, saveFileAs), FileMode.Create);
await file.CopyToAsync(stream, HttpContext.RequestAborted);
model.Files.Add(file.FileName, saveFileAs);
}
}

return View(model);
}

[HttpGet]
public ActionResult List()
{
return View(Directory.EnumerateFiles(fileShareConfiguration.Location));
}

[HttpDelete]
public JsonResult Delete(string fileToDelete)
{
string actualFileName = HttpUtility.UrlDecode(fileToDelete);
SystemFile.Delete(actualFileName);
return Json($"Successfully deleted {actualFileName}");
}
}
27 changes: 27 additions & 0 deletions FileShares/src/FileSharesWeb/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Steeltoe.Samples.FileSharesWeb.Models;

namespace Steeltoe.Samples.FileSharesWeb.Controllers;

public sealed class HomeController : Controller
{
public IActionResult Index()
{
return View();
}

public IActionResult Privacy()
{
return View();
}

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier
});
}
}
8 changes: 8 additions & 0 deletions FileShares/src/FileSharesWeb/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project>
<PropertyGroup>
<SteeltoeVersion>4.0.*-*</SteeltoeVersion>
</PropertyGroup>
<PropertyGroup>
<AspNetCoreVersion>8.0.*</AspNetCoreVersion>
</PropertyGroup>
</Project>
Loading
Loading