Base Documentation¶
SOFTINUX Base Documentation¶
Warning
The documentation is not complete and therefore undergoes frequent changes.
SOFTINUX Base is a free, open source and cross-platform based on ASP.NET Core and ExtCore framework. It runs on Windows, Mac and Linux. It is built using the best and the most modern tools and languages.
It is completely modular and extendable.
Using the features of the underlying ExtCore framework you can easily create your own extensions to extend its functionality.
Basic Concepts¶
Softinux Base is a framework that looks like a .NET Core web application, but is intended to host mini web applications called extensions. Every extension will plug its content (pages, menu items) as well as security and authentication related items (permissions, roles, links…).
Base manages the common stuff so that the developer can focus on its extension and business logic, just having to provide what we call metadata to know how to display and authorize access to content, and use our version of Authorize attribute.
Installation¶
Restore npm packages¶
After cloning Base repository, go to Barebone folder and run npm ci --save-dev
command so that dependencies packages are installed and settings updated.
Note
You must have Nodejs to restore web dependencies.
Restore nuGet packages¶
Restoring the nuGet packages is now an implicit command executed at application build so you don’t need to do it manually.
Update database with migration¶
dotnet ef database update
.services_.AddDbContext<...
).Build the application¶
Go to the root folder and run bp.bat
under Windows or bp.sh
under Linux/Macos. (use -h for help).
Note
You must have .NET Core SDK to compile and build the application.
Configure the application¶
appsettings.json
file.See configuration section for a full explanation.
Run the app¶
Warning
Remove the SeedDatabase.dll
to avoid any attempt to create a new administrator. See RestSeed configuration section.
dotnet run
.dotnet run --project WebApplication\WebApplication.csproj
).After that, the application is available on http://localhost:5000/
Note about Visual Studio 2017¶
Note about Rider 2017.3¶
./bp.sh copyexts
and ./bp.sh copydeps
after building the solution or project.Add the administrator user¶
- curl:
curl -i -X POST -H 'Content-Type: application/json' http://localhost:5000/dev/seed/create-user -d {}
- powershell:
Invoke-WebRequest -Uri http://localhost:5000/dev/seed/create-user -Method POST
This will create the administrator user with general permissions.
Note
Actually, we creating demo user. The first user is johndoe.
Login with demo user¶
Configuration¶
appsettings.json
file.- Extensions
- ConnectionStrings
- Corporate
- RestSeed
- SignIn
- LockoutUser
- ValidateUser
- PasswordStrategy
- ConfigureApplicationCookie
- Logging
- Serilog
You can add others sections, but it’s up to you to read them.
Extensions¶
WebApplication/Extensions
folder.- bp.bat for windows
- bp.sh for *nix system.
You have four variables :
netVersion
: folder name defined by .NET Core TargetFramework tag into cs proj file.ext_folder
: extensions folder path.dep_folder
: dependencies folder path.pub_folder
: publish folder path.
:: set .NET output folder name (use .NET Core version defined into csproj files)
set netVersion="netcoreapp2.2"
:: Extensions folder
set ext_folder=".\WebApplication\Extensions\"
:: Dependencies folder
set dep_folder=".\WebApplication\bin\Debug\%netVersion%\"
:: Publish folder
set pub_folder=".\WebApplication\bin\Debug\%netVersion%\publish"
ConnectionStrings¶
"ConnectionStrings": {
// Please use '/' for directory separator
"Default": "Data Source=basedb.sqlite"
// SqlServer
//"Default": "Data Source=localhost;Initial Catalog=Softinux;MultipleActiveResultSets=True;Persist Security Info=True;User ID=softinux;Password=?"
// PostgreSql
//"Default": "Host=localhost;Port=5432;Database=softinux;Pooling=true;User ID=softinux;Password=?;"
// localdb
//"Default": "Data Source=(localdb)\mssqllocaldb;Database=softinux;Trusted_Connection=True;MultipleActiveResultSets=true"
}
Corporate¶
Here you can set you Company name and logo.
"Corporate": {
"Name": "SOFTINUX",
"BrandLogo": "softinux_logo-bg-transparent.png"
}
The logo is to be place into : wwwroot\img
RestSeed¶
"RestSeed": {
"UserName": "",
"UserPassword": "",
"Id": "",
"Guid": ""
}
Warning
Is strongly recommended to remove the SeedDatabase.dll
to avoid any attempt to create a new administrator.
This can happen if you change the information in the configuration file and restart the application.
SignIn, LockoutUser, ValidateUser, PasswordStrategy, ConfigureApplicationCookie¶
These settings are used by ASP.NET Core Identity.
Logging¶
This is the standard .NET Core Logging configuration.
Here we’ll describe what to know about extensions and how to customize things.
Extension structure¶
ExtCore concepts¶
Read ExtCore documentation to learn about extensions and how they are structured into several projects.
Embedded resources¶
In your .csproj, you’ll find this:
<ItemGroup>
<EmbeddedResource Include="Styles\**;Scripts\**\*.min.js;Views\**" />
</ItemGroup>
So that your embedded styles, scripts and views are embedded.
You’ll also find complementary stuff like this, to be sure that any file used in your project but provided by another project is correctly built as an embedded resource only:
<ItemGroup>
<None Remove="Views\SomeView.cshtml" />
<None Remove="... path_to_some_file_of_other_project.js" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="... path_to_some_file_of_other_project.js" />
</ItemGroup>
Bundling¶
Bundling is a convenient way to save bandwith and processor time when dealing with .css files etc. This is not specific to our project but we share our preferred way of doing this, so you would do the same in your extension project:
We use a bundleconfig.json file in concerned projects and the .csproj contains something like this:
<DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.8.391" />
As a side note, embedded resources are bundled first.
Base’s common interface¶
In your extension main project, a class should implement the Infrastructure.IExtensionMetadata
interface,
so that the application knows what the extension provides in matter of display (menu items…).
We usually name it ExtensionMetadata
.
General useful properties¶
Base.Infrastructure.IExtensionMetadata
and ExtCore.Infrastructure.IExtensionMetadata
interfaces will require implementation of some properties.
We recommend using the following code, using assembly attributes.
/// <summary>
/// Gets the current assembly object.
/// </summary>
public Assembly CurrentAssembly => Assembly.GetExecutingAssembly();
/// <summary>
/// Gets the full path with assembly name.
/// </summary>
public string CurrentAssemblyPath => CurrentAssembly.Location;
/// <summary>
/// Gets the name of the extension.
/// </summary>
public string Name => CurrentAssembly.GetName().Name;
/// <summary>
/// Gets the URL of the extension.
/// </summary>
public string Url => Attribute.GetCustomAttribute(CurrentAssembly, typeof(AssemblyTitleAttribute)).ToString();
/// <summary>
/// Gets the version of the extension.
/// </summary>
public string Version => Attribute.GetCustomAttribute(CurrentAssembly, typeof(AssemblyVersionAttribute)).ToString();
/// <summary>
/// Gets the authors of the extension (separated by commas).
/// </summary>
public string Authors => Attribute.GetCustomAttribute(CurrentAssembly, typeof(AssemblyCompanyAttribute)).ToString();
/// <summary>
/// Gets the description of the extension (separated by commas).
/// </summary>
public string Description => Attribute.GetCustomAttribute(CurrentAssembly, typeof(AssemblyDescriptionAttribute)).ToString();
MVC structure¶
Controllers¶
Your controllers should inherit from Infrastructure.ControllerBase
so that you have access to storage layer (IStorage
) and optionally logging (ILoggerFactory
).
Additional configuration to web application¶
ExtCore.Infrastructure.Actions.IConfigureServicesAction
interface allows you to define your injections to the web application services container.Utilities¶
Logging¶
ILoggerFactory
from your controller and instantiate a private logger in your class with:ILogger _logger = _loggerFactory.CreateLogger(GetType().FullName);
Authentication¶
Introduction¶
Security.Common
extension manages authenticated access to the application by decorating controllers or controllers’ methods.Security
extensions allows to manage authentication data (administration).Permissions, Scopes and Claims¶
PermissionRequirementAttribute
or AnyPermissionRequirementAttribute
attribute from Security.Common.Attributes
.Security.Common.Enums.Permission enumeration
) and scope (extension assembly short name without the version and culture stuff).You will be able to use it to filter menu items too (work in progress, issue #9).
Create your extensions¶
Warning
You cannot place your web application’s Extensions folder to another drive. See #2981
You can use Visual Studio 2017, Visual Studio Code or JetBrains Rider to make your own extension. If you decide to use Visual Studio, be aware that projects are not compatible with Visual Studio 2015.
What there is to know¶
Warning
You cannot place your web application’s Extensions folder to another drive. See #2981
You can use Visual Studio 2017, Visual Studio Code or JetBrains Rider to make your own extension. If you decide to use Visual Studio, be aware that projects are not compatible with Visual Studio 2015.
In this section, we talking of SampleApi. This project is availiable on Github to :
Todo
add git repos for sample app
New Extension with Base source¶
Use Base solution and add your extension code into it.
Add a new project¶
Using command-line (easy and cross-platform):
$ dotnet new classlib -o <your_new_project> -f netcoreapp2.2
Assuming Base’s Infrastructure
framework version is 2.2. Check its .csproj file.
If you don’t specify framework version, it will default to netstandardxxx
, which is not what we expect.
Add project reference to the solution¶
Go to solution folder and type:
$ dotnet sln add <path_to_your_new_project_csproj>
Write your code¶
Infrastructure
and also Security.Common
.ExtensionMetadata
class that implements Infrastructure.IExtensionMetadata
.Have a look at write your extensions, feel free to open issues for questions.
Using Base as dependency¶
Use your own solution and Base as a dependency. This is an alternative to using Base’s solution.
Configure a new project with Visual Studio 2017/2019¶
Create new solution with a new ASP.NET Core project targeted on framework .NET Core 2.2.
Verification¶
Check if your new project is targeted on framework .NET Core 2.2.

Project properties, Application tab.
Configure pre-build scripts¶
Before building, you need to copy all Base dependencies to $(SolutionDir)$(OutDir) folder:

Configure post-build scripts¶
After building, you need to copy your extension into Base’s extensions folder:

Configure debug tab¶
Most important, configure debugging. Your extension is a partial app and is not directly executed. Here is how to configure your application to enable possibility of debugging.

Now, you can debug your extension into Visual Studio.
With commande line and Visual Studio Code¶
Create a new project¶
$ dotnet new classlib -o <your_new_project> -f netcoreapp2.2
Open your new .csproj file and adapt it with highlighted lines as in example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<ApplicationIcon />
<OutputType>Library</OutputType>
<StartupObject />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>$(BaseOutputPath)bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Styles\**;Scripts\**\*.min.js;Views\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ExtCore.Infrastructure" Version="4.1.0" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<Reference Include="SoftinuxBase.Infrastructure, Version=0.0.1.0, Culture=neutral, PublicKeyToken=null">
<HintPath>..\..\Base\SoftinuxBase.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="SoftinuxBase.Security.Common, Version=0.0.1.0, Culture=neutral, PublicKeyToken=null">
<HintPath>..\..\Base\SoftinuxBase.Security.Common.dll</HintPath>
</Reference>
</ItemGroup>
<PropertyGroup>
<SolutionDir Condition=" '$(SolutionDir)' == '' ">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), SampleApi.sln))</SolutionDir>
</PropertyGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="xcopy $(SolutionDir)..\..\Base\*.* $(SolutionDir)$(OutDir) /E /Y" />
</Target>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="mkdir $(SolutionDir)$(OutDir)Extensions
copy $(SolutionDir)$(OutDir)SampleApi.dll $(SolutionDir)$(OutDir)Extensions /Y
copy $(SolutionDir)$(OutDir)SampleApi.xml $(SolutionDir)$(OutDir)Extensions /Y" />
</Target>
</Project>
|
Note
Visual Studio Code Configuration¶
Tasks.json¶
WebApplication.dll
as entry point of application.Note
The order sequence makes build on every launch.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | {
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/src/SampleApi/SampleApi.csproj"
],
"problemMatcher": "$tsc"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/src/SampleApi/SampleApi.csproj"
],
"problemMatcher": "$tsc"
},
{
"label": "watch",
"dependsOrder": "sequence",
"dependsOn":[
"build"
],
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"${workspaceFolder}/src/SampleApi/bin/Debug/netcoreapp2.2/WebApplication.dll"
],
"problemMatcher": "$tsc",
"presentation": {
"reveal": "always",
"panel": "new"
}
}
]
}
|
Launch.json¶
WebApplication.dll
as the program to execute.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | {
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/src/SampleApi/bin/Debug/netcoreapp2.2/WebApplication.dll",
"args": [],
"cwd": "${workspaceFolder}/src/SampleApi/bin/Debug/netcoreapp2.2/",
"stopAtEntry": false,
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "^\\s*Now listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}
|
About Entity Framework¶
By definition, ExtCore uses Entity Framework but provides several projects to define:
- the entities in
YourExtension.Data.Entities
- the entities mapping in
YourExtension.Data.EntityFramework
(EntityRegistrar
class) - the EF provider to actually use, in
YourExtension.Data.EntityFramework.ProviderName
SecurityTest
test project in Testing/Unit
references the three aforementioned projects related to Security
extensionCommonTest.ApplicationStorageContext
class to indicate the DbContext structure.Internals¶
Implementations of IConfigureServicesAction
They register services implementations to web application container so that they become available for dependency injection (ExtCore feature).
Security project:
- priority 200: ConfigureAuthentication
- priority 201: AddAuthorizationPolicies
Implementations of IConfigureAction
They record web application’s request pipelines (ExtCore feature).
Security project:
- priority 100: ActivateAuthentication
Unit testing¶
Introduction¶
DatabaseFixture
class, that does several things:
- read configuration files, register services (same principle as web application’s Startup)
- expose ExtCore core components such as
IStorage
to test classes- expose Identity
RoleManager
andUserManager
to test classes
In addition, to perform an EF migration, an implementation of IDesignTimeDbContextFactory
has been provided,
as CommonTest isn’t a console but library project.
The test projects use an identical database to the one web application uses, but empty.
How to setup a test project¶
When you want to create a migration, be sure that your test project adds references to these projects:
- your extension’s entities project (
YourExtension.Data.Entities
) - your extension’s EF project where lives entities registrar and repositories implementations (
YourExtension.Data.EntityFramework
)
If you just want to use ExtCore’s repositories pattern to query DB, reference your extension’s repositories project
YourExtension.Data.EntityFramework
.
Running tests¶
- Perform any necessary migration (at least from Testing/Unit/CommonTest, with
dotnet ef database update
). - If testing with VS Code IDE, we use dotnet-test-explorer extension with some configuration in .vscode/settings.json (workspace configuration file).
How to log¶
We’ve integrated Serilog by associating it to the logger factory that ASP.NET Core creates at application startup.
Log level is defined in appsettings.json
of web application, sections “Logging” and “Serilog”.
Microsoft.Extensions.Logging.ILoggerFactory
into your class constructor.Microsoft.Extensions.Logging.ILogger myLogger = _loggerFactory.CreateLogger(GetType().FullName);
and log:
myLogger.LogInformation("Hello");
Configure Rider¶
Note
This page is for Rider 2018.2 and upper.
.sln
tag to build your application.Create an external tool¶
Click on edit configuration

If you have already one configuration, click on it


In new window click on plus sign:

Now, in external tool configuration window:
- enter a name for your new external tool configuration.
- in program field, enter same text as screen shot. Help yourself with macros.
- in arguments field enter bundles.
- working directory is auto completed.
- click on save.

Once you’ve configured this external tool, copy it and create the two other ones:

Change argument field to copyexts
for the second external tool and copydeps
for the third external tool.
Be sure you have the external tools and the project build tasks in this order:

Create an file watcher for javascript minification¶
In this example, we use Uglifyjs. You can install with nodejs by npm install uglify-js -g
.

In Edit Watcher window, click on three dot of Scope field.

In Scope window, select your javascript file and click to add.

Finish by clicking on Ok. Close all settings windows.
Faq for Linux¶
Permission denied for editing the folder '/usr/share/dotnet/sdk/NuGetFallbackFolder'.
dotnet restore
with root privilege because, the current user ave not right to write into /usr/share/dotnet/sdk/NuGetFallbackFolder
Permission denied for editing the folder '/usr/share/dotnet/sdk/NuGetFallbackFolder'.
DOTNET_SKIP_FIRST_TIME_EXPERIENCE
environment variable to 1 (or true)TODO¶
As mentioned before, all this is work-in-progress.
List of TODOs¶
The following list is automatically created by the
Sphinx TODO plugin.
If there is no list, either all TODOs are done (very unlikely), or they are
disabled with the option todo_include_todos = False
in the file
conf.py
.
Todo
add git repos for sample app
(The original entry is located in /home/docs/checkouts/readthedocs.org/user_builds/softinux-base/checkouts/latest/source/implement_your_extension/what_you_need_to_know.rst, line 13.)