Compare commits
	
		
			93 Commits
		
	
	
		
			release/v2
			...
			version_up
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					1977ecdfb7 | ||
| 
						 | 
					1d703e1737 | ||
| 
						 | 
					22ddb40d4c | ||
| 
						 | 
					791b72aec4 | ||
| 
						 | 
					46b9524903 | ||
| 
						 | 
					bb09f919e6 | ||
| 
						 | 
					8d04cbc059 | ||
| 
						 | 
					073856d385 | ||
| 
						 | 
					c782981ca7 | ||
| 
						 | 
					ca3691e665 | ||
| 
						 | 
					be2ffc86ec | ||
| 
						 | 
					75ebc0771c | ||
| 
						 | 
					d050635a99 | ||
| 
						 | 
					e3592b5fe6 | ||
| 
						 | 
					9eec54effb | ||
| 
						 | 
					6a7ae342dc | ||
| 
						 | 
					5b3205823e | ||
| 
						 | 
					db45a01bce | ||
| 
						 | 
					d2f70ec82d | ||
| 
						 | 
					2b01453970 | ||
| 
						 | 
					68ccc4da93 | ||
| 
						 | 
					1b4a58c95c | ||
| 
						 | 
					bd20abacdf | ||
| 
						 | 
					f0ca087d48 | ||
| 
						 | 
					21bf1ff148 | ||
| 
						 | 
					66c010dd28 | ||
| 
						 | 
					0fb18e8a32 | ||
| 
						 | 
					da21df49ce | ||
| 
						 | 
					54d6565411 | ||
| 
						 | 
					5052a818ff | ||
| 
						 | 
					5e1f3e0e31 | ||
| 
						 | 
					c0740a9760 | ||
| 
						 | 
					88ebeead8c | ||
| 
						 | 
					41c155c332 | ||
| 
						 | 
					aaf6c933b5 | ||
| 
						 | 
					5e58f7ef37 | ||
| 
						 | 
					9fdd0019c1 | ||
| 
						 | 
					f94e4b3aed | ||
| 
						 | 
					b2b183f95c | ||
| 
						 | 
					2cfa5040dc | ||
| 
						 | 
					3112e2ad36 | ||
| 
						 | 
					0e45f3700b | ||
| 
						 | 
					78d9e508fc | ||
| 
						 | 
					81ff75f1cc | ||
| 
						 | 
					ab276bb474 | ||
| 
						 | 
					2a8f09cb46 | ||
| 
						 | 
					239dcffc98 | ||
| 
						 | 
					5fb32934fa | ||
| 
						 | 
					38010d4628 | ||
| 
						 | 
					669f7f3433 | ||
| 
						 | 
					3576d5516f | ||
| 
						 | 
					0781f15bae | ||
| 
						 | 
					e87f4d6476 | ||
| 
						 | 
					dd7d5e8ab6 | ||
| 
						 | 
					776ecabf81 | ||
| 
						 | 
					5c6814852e | ||
| 
						 | 
					90c700702e | ||
| 
						 | 
					7c3ae1b5b9 | ||
| 
						 | 
					15c2f6a4fc | ||
| 
						 | 
					9d5855bc6e | ||
| 
						 | 
					b7d72474da | ||
| 
						 | 
					33650f5cea | ||
| 
						 | 
					e8955454f7 | ||
| 
						 | 
					6e0cf66008 | ||
| 
						 | 
					6d305636a0 | ||
| 
						 | 
					958b3337a8 | ||
| 
						 | 
					3c7fa2ce9e | ||
| 
						 | 
					b6a941197a | ||
| 
						 | 
					338ac586de | ||
| 
						 | 
					69262ee213 | ||
| 
						 | 
					a592534621 | ||
| 
						 | 
					c7e41c6671 | ||
| 
						 | 
					17cecb3a3a | ||
| 
						 | 
					64432c2fcd | ||
| 
						 | 
					b8e98abfbd | ||
| 
						 | 
					da507cb55c | ||
| 
						 | 
					bdf8f642f9 | ||
| 
						 | 
					d6a7ff14e4 | ||
| 
						 | 
					3f3c48b17d | ||
| 
						 | 
					878de17cd6 | ||
| 
						 | 
					c1babcff00 | ||
| 
						 | 
					41ec3b3495 | ||
| 
						 | 
					daa264c984 | ||
| 
						 | 
					17f95a64ad | ||
| 
						 | 
					8fff2ced69 | ||
| 
						 | 
					4bbc4154eb | ||
| 
						 | 
					ffb7dd890e | ||
| 
						 | 
					ce1818c93c | ||
| 
						 | 
					acdb617d35 | ||
| 
						 | 
					b0f1ecbbe4 | ||
| 
						 | 
					040c782f3b | ||
| 
						 | 
					2f39ead739 | ||
| 
						 | 
					6312c7b1d8 | 
							
								
								
									
										178
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,178 @@
 | 
			
		||||
---
 | 
			
		||||
Language:        Cpp
 | 
			
		||||
# BasedOnStyle:  LLVM
 | 
			
		||||
AccessModifierOffset: -2
 | 
			
		||||
AlignAfterOpenBracket: Align
 | 
			
		||||
AlignArrayOfStructures: None
 | 
			
		||||
AlignConsecutiveMacros: None
 | 
			
		||||
AlignConsecutiveAssignments: None
 | 
			
		||||
AlignConsecutiveBitFields: None
 | 
			
		||||
AlignConsecutiveDeclarations: None
 | 
			
		||||
AlignEscapedNewlines: Right
 | 
			
		||||
AlignOperands:   Align
 | 
			
		||||
AlignTrailingComments: true
 | 
			
		||||
AllowAllArgumentsOnNextLine: true
 | 
			
		||||
AllowAllConstructorInitializersOnNextLine: true
 | 
			
		||||
AllowAllParametersOfDeclarationOnNextLine: true
 | 
			
		||||
AllowShortEnumsOnASingleLine: true
 | 
			
		||||
AllowShortBlocksOnASingleLine: Never
 | 
			
		||||
AllowShortCaseLabelsOnASingleLine: false
 | 
			
		||||
AllowShortFunctionsOnASingleLine: All
 | 
			
		||||
AllowShortLambdasOnASingleLine: All
 | 
			
		||||
AllowShortIfStatementsOnASingleLine: Never
 | 
			
		||||
AllowShortLoopsOnASingleLine: false
 | 
			
		||||
AlwaysBreakAfterDefinitionReturnType: None
 | 
			
		||||
AlwaysBreakAfterReturnType: None
 | 
			
		||||
AlwaysBreakBeforeMultilineStrings: false
 | 
			
		||||
AlwaysBreakTemplateDeclarations: MultiLine
 | 
			
		||||
AttributeMacros:
 | 
			
		||||
  - __capability
 | 
			
		||||
BinPackArguments: true
 | 
			
		||||
BinPackParameters: true
 | 
			
		||||
BraceWrapping:
 | 
			
		||||
  AfterCaseLabel:  false
 | 
			
		||||
  AfterClass:      false
 | 
			
		||||
  AfterControlStatement: Never
 | 
			
		||||
  AfterEnum:       false
 | 
			
		||||
  AfterFunction:   false
 | 
			
		||||
  AfterNamespace:  false
 | 
			
		||||
  AfterObjCDeclaration: false
 | 
			
		||||
  AfterStruct:     false
 | 
			
		||||
  AfterUnion:      false
 | 
			
		||||
  AfterExternBlock: false
 | 
			
		||||
  BeforeCatch:     false
 | 
			
		||||
  BeforeElse:      false
 | 
			
		||||
  BeforeLambdaBody: false
 | 
			
		||||
  BeforeWhile:     false
 | 
			
		||||
  IndentBraces:    false
 | 
			
		||||
  SplitEmptyFunction: true
 | 
			
		||||
  SplitEmptyRecord: true
 | 
			
		||||
  SplitEmptyNamespace: true
 | 
			
		||||
BreakBeforeBinaryOperators: None
 | 
			
		||||
BreakBeforeConceptDeclarations: true
 | 
			
		||||
BreakBeforeBraces: Attach
 | 
			
		||||
BreakBeforeInheritanceComma: false
 | 
			
		||||
BreakInheritanceList: BeforeColon
 | 
			
		||||
BreakBeforeTernaryOperators: true
 | 
			
		||||
BreakConstructorInitializersBeforeComma: false
 | 
			
		||||
BreakConstructorInitializers: BeforeColon
 | 
			
		||||
BreakAfterJavaFieldAnnotations: false
 | 
			
		||||
BreakStringLiterals: true
 | 
			
		||||
ColumnLimit:     100
 | 
			
		||||
CommentPragmas:  '^ IWYU pragma:'
 | 
			
		||||
CompactNamespaces: false
 | 
			
		||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
 | 
			
		||||
ConstructorInitializerIndentWidth: 4
 | 
			
		||||
ContinuationIndentWidth: 4
 | 
			
		||||
Cpp11BracedListStyle: true
 | 
			
		||||
DeriveLineEnding: true
 | 
			
		||||
DerivePointerAlignment: false
 | 
			
		||||
DisableFormat:   false
 | 
			
		||||
EmptyLineAfterAccessModifier: Never
 | 
			
		||||
EmptyLineBeforeAccessModifier: LogicalBlock
 | 
			
		||||
ExperimentalAutoDetectBinPacking: false
 | 
			
		||||
FixNamespaceComments: true
 | 
			
		||||
ForEachMacros:
 | 
			
		||||
  - foreach
 | 
			
		||||
  - Q_FOREACH
 | 
			
		||||
  - BOOST_FOREACH
 | 
			
		||||
IfMacros:
 | 
			
		||||
  - KJ_IF_MAYBE
 | 
			
		||||
IncludeBlocks:   Preserve
 | 
			
		||||
IncludeCategories:
 | 
			
		||||
  - Regex:           '^"(llvm|llvm-c|clang|clang-c)/'
 | 
			
		||||
    Priority:        2
 | 
			
		||||
    SortPriority:    0
 | 
			
		||||
    CaseSensitive:   false
 | 
			
		||||
  - Regex:           '^(<|"(gtest|gmock|isl|json)/)'
 | 
			
		||||
    Priority:        3
 | 
			
		||||
    SortPriority:    0
 | 
			
		||||
    CaseSensitive:   false
 | 
			
		||||
  - Regex:           '.*'
 | 
			
		||||
    Priority:        1
 | 
			
		||||
    SortPriority:    0
 | 
			
		||||
    CaseSensitive:   false
 | 
			
		||||
IncludeIsMainRegex: '(Test)?$'
 | 
			
		||||
IncludeIsMainSourceRegex: ''
 | 
			
		||||
IndentAccessModifiers: false
 | 
			
		||||
IndentCaseLabels: false
 | 
			
		||||
IndentCaseBlocks: false
 | 
			
		||||
IndentGotoLabels: true
 | 
			
		||||
IndentPPDirectives: None
 | 
			
		||||
IndentExternBlock: AfterExternBlock
 | 
			
		||||
IndentRequires:  false
 | 
			
		||||
IndentWidth:     4
 | 
			
		||||
IndentWrappedFunctionNames: false
 | 
			
		||||
InsertTrailingCommas: None
 | 
			
		||||
JavaScriptQuotes: Leave
 | 
			
		||||
JavaScriptWrapImports: true
 | 
			
		||||
KeepEmptyLinesAtTheStartOfBlocks: true
 | 
			
		||||
LambdaBodyIndentation: Signature
 | 
			
		||||
MacroBlockBegin: ''
 | 
			
		||||
MacroBlockEnd:   ''
 | 
			
		||||
MaxEmptyLinesToKeep: 1
 | 
			
		||||
NamespaceIndentation: All
 | 
			
		||||
ObjCBinPackProtocolList: Auto
 | 
			
		||||
ObjCBlockIndentWidth: 2
 | 
			
		||||
ObjCBreakBeforeNestedBlockParam: true
 | 
			
		||||
ObjCSpaceAfterProperty: false
 | 
			
		||||
ObjCSpaceBeforeProtocolList: true
 | 
			
		||||
PenaltyBreakAssignment: 2
 | 
			
		||||
PenaltyBreakBeforeFirstCallParameter: 19
 | 
			
		||||
PenaltyBreakComment: 300
 | 
			
		||||
PenaltyBreakFirstLessLess: 120
 | 
			
		||||
PenaltyBreakString: 1000
 | 
			
		||||
PenaltyBreakTemplateDeclaration: 10
 | 
			
		||||
PenaltyExcessCharacter: 1000000
 | 
			
		||||
PenaltyReturnTypeOnItsOwnLine: 60
 | 
			
		||||
PenaltyIndentedWhitespace: 0
 | 
			
		||||
PointerAlignment: Right
 | 
			
		||||
PPIndentWidth:   -1
 | 
			
		||||
ReferenceAlignment: Pointer
 | 
			
		||||
ReflowComments:  true
 | 
			
		||||
ShortNamespaceLines: 1
 | 
			
		||||
SortIncludes:    CaseSensitive
 | 
			
		||||
SortJavaStaticImport: Before
 | 
			
		||||
SortUsingDeclarations: true
 | 
			
		||||
SpaceAfterCStyleCast: false
 | 
			
		||||
SpaceAfterLogicalNot: false
 | 
			
		||||
SpaceAfterTemplateKeyword: true
 | 
			
		||||
SpaceBeforeAssignmentOperators: true
 | 
			
		||||
SpaceBeforeCaseColon: false
 | 
			
		||||
SpaceBeforeCpp11BracedList: false
 | 
			
		||||
SpaceBeforeCtorInitializerColon: true
 | 
			
		||||
SpaceBeforeInheritanceColon: true
 | 
			
		||||
SpaceBeforeParens: ControlStatements
 | 
			
		||||
SpaceAroundPointerQualifiers: Default
 | 
			
		||||
SpaceBeforeRangeBasedForLoopColon: true
 | 
			
		||||
SpaceInEmptyBlock: false
 | 
			
		||||
SpaceInEmptyParentheses: false
 | 
			
		||||
SpacesBeforeTrailingComments: 1
 | 
			
		||||
SpacesInAngles:  Never
 | 
			
		||||
SpacesInConditionalStatement: false
 | 
			
		||||
SpacesInContainerLiterals: true
 | 
			
		||||
SpacesInCStyleCastParentheses: false
 | 
			
		||||
SpacesInLineCommentPrefix:
 | 
			
		||||
  Minimum:         1
 | 
			
		||||
  Maximum:         -1
 | 
			
		||||
SpacesInParentheses: false
 | 
			
		||||
SpacesInSquareBrackets: false
 | 
			
		||||
SpaceBeforeSquareBrackets: false
 | 
			
		||||
BitFieldColonSpacing: Both
 | 
			
		||||
Standard:        Latest
 | 
			
		||||
StatementAttributeLikeMacros:
 | 
			
		||||
  - Q_EMIT
 | 
			
		||||
StatementMacros:
 | 
			
		||||
  - Q_UNUSED
 | 
			
		||||
  - QT_REQUIRE_VERSION
 | 
			
		||||
TabWidth:        4
 | 
			
		||||
UseCRLF:         false
 | 
			
		||||
UseTab:          Always
 | 
			
		||||
WhitespaceSensitiveMacros:
 | 
			
		||||
  - STRINGIZE
 | 
			
		||||
  - PP_STRINGIZE
 | 
			
		||||
  - BOOST_PP_STRINGIZE
 | 
			
		||||
  - NS_SWIFT_NAME
 | 
			
		||||
  - CF_SWIFT_NAME
 | 
			
		||||
...
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										197
									
								
								BUILDING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,197 @@
 | 
			
		||||
# Building from source
 | 
			
		||||
 | 
			
		||||
In order to build OWSEC, you will need to install its dependencies, which includes the following:
 | 
			
		||||
- cmake
 | 
			
		||||
- boost
 | 
			
		||||
- POCO 1.10.1 or later
 | 
			
		||||
- a C++17 compiler
 | 
			
		||||
- openssl
 | 
			
		||||
- libpq-dev (PortgreSQL development libraries)
 | 
			
		||||
- mysql-client (MySQL client)
 | 
			
		||||
- librdkafka
 | 
			
		||||
- cppkafka
 | 
			
		||||
 | 
			
		||||
The build is done in 2 parts. The first part is to build a local copy of the framework tailored to your environment. This
 | 
			
		||||
framework is called [Poco](https://github.com/pocoproject/poco). The version used in this project has a couple of fixes
 | 
			
		||||
from the master copy needed for cmake. Please use the version of this [Poco fix](https://github.com/Telecominfraproject/wlan-cloud-lib-poco). Building
 | 
			
		||||
Poco may take several minutes depending on the platform you are building on.
 | 
			
		||||
 | 
			
		||||
## Ubuntu
 | 
			
		||||
These instructions have proven to work on Ubuntu 20.4.
 | 
			
		||||
```bash
 | 
			
		||||
sudo apt install git cmake g++ libssl-dev libmariadb-dev \
 | 
			
		||||
    libpq-dev libaprutil1-dev apache2-dev libboost-all-dev \
 | 
			
		||||
    librdkafka-dev default-libmysqlclient-dev \
 | 
			
		||||
    nlohmann-json-dev
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch poco-tip-v1 poco
 | 
			
		||||
cd poco
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
cd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
cmake --build . --config Release
 | 
			
		||||
sudo cmake --build . --target install
 | 
			
		||||
cd ../..
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka --branch tip-v1 cppkafka
 | 
			
		||||
cd cppkafka
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
cd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
cmake --build . --config Release
 | 
			
		||||
sudo cmake --build . --target install
 | 
			
		||||
cd ../..
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch tip-v1 valijson
 | 
			
		||||
cd valijson
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
cd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
cmake --build . --config Release
 | 
			
		||||
sudo cmake --build . --target install
 | 
			
		||||
cd ../..
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/fmtlib/fmt --branch 9.0.0 /fmtlib
 | 
			
		||||
cd fmtlib
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
cd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
make
 | 
			
		||||
make install
 | 
			
		||||
cd ../..
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralsec
 | 
			
		||||
cd wlan-cloud-ucentralsec
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
cd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
make -j 8
 | 
			
		||||
cd ../..
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Fedora
 | 
			
		||||
The following instructions have proven to work on Fedora 33
 | 
			
		||||
```bash
 | 
			
		||||
sudo yum install cmake g++ openssl-devel mysql-devel mysql apr-util-devel boost boost-devel \
 | 
			
		||||
    yaml-cpp-devel lua-devel
 | 
			
		||||
sudo dnf install postgresql.x86_64 librdkafka-devel
 | 
			
		||||
sudo dnf install postgresql-devel json-devel
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch poco-tip-v1 poco
 | 
			
		||||
cd poco
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
cd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
cmake --build . --config Release
 | 
			
		||||
sudo cmake --build . --target install
 | 
			
		||||
cd ../..
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka --branch tip-v1 cppkafka
 | 
			
		||||
cd cppkafka
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
cd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
cmake --build . --config Release
 | 
			
		||||
sudo cmake --build . --target install
 | 
			
		||||
cd ../..
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch tip-v1 valijson
 | 
			
		||||
cd valijson
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
cd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
cmake --build . --config Release
 | 
			
		||||
sudo cmake --build . --target install
 | 
			
		||||
cd ../..
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralsec
 | 
			
		||||
cd wlan-cloud-ucentralsec
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
cd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
make
 | 
			
		||||
cd ../..
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## macOS Build
 | 
			
		||||
The following instructions have proven to work on macOS Big Sur. You need to install [Homebrew](https://brew.sh/). You must also have installed [XCode for OS X](https://www.freecodecamp.org/news/how-to-download-and-install-xcode/).
 | 
			
		||||
```bash
 | 
			
		||||
brew install openssl \
 | 
			
		||||
	cmake \
 | 
			
		||||
	libpq \
 | 
			
		||||
	mysql-client \
 | 
			
		||||
	apr \
 | 
			
		||||
	apr-util \
 | 
			
		||||
	boost \
 | 
			
		||||
	yaml-cpp \
 | 
			
		||||
	postgresql \
 | 
			
		||||
	librdkafka \
 | 
			
		||||
	nlohmann-json \
 | 
			
		||||
	fmt
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch poco-tip-v1 poco
 | 
			
		||||
pushd poco
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
push cmake-build
 | 
			
		||||
cmake -DOPENSSL_ROOT_DIR=</path/to/openssl> -DENABLE_NETSSL=1 -DENABLE_JWT=1 -DENABLE_CRYPTO=1 ..
 | 
			
		||||
cmake --build . --config Release
 | 
			
		||||
sudo cmake --build . --target install
 | 
			
		||||
popd
 | 
			
		||||
popd
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka --branch tip-v1 cppkafka
 | 
			
		||||
pushd cppkafka
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
pushd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
cmake --build . --config Release
 | 
			
		||||
sudo cmake --build . --target install
 | 
			
		||||
popd
 | 
			
		||||
popd
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch tip-v1 valijson
 | 
			
		||||
pushd valijson
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
pushd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
cmake --build . --config Release
 | 
			
		||||
sudo cmake --build . --target install
 | 
			
		||||
popd
 | 
			
		||||
popd
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralsec
 | 
			
		||||
pushd wlan-cloud-ucentralsec
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
pushd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
make -j
 | 
			
		||||
popd
 | 
			
		||||
popd
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Raspberry
 | 
			
		||||
The build on a rPI takes a while. You can shorten that build time and requirements by disabling all the larger database
 | 
			
		||||
support. You can build with only SQLite support by not installing the packages for PostgreSQL, and MySQL by
 | 
			
		||||
adding -DSMALL_BUILD=1 on the cmake build line.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
sudo apt install git cmake g++ libssl-dev libaprutil1-dev apache2-dev \
 | 
			
		||||
    libboost-all-dev libyaml-cpp-dev
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch poco-tip-v1 poco
 | 
			
		||||
cd poco
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
cd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
cmake --build . --config Release
 | 
			
		||||
sudo cmake --build . --target install
 | 
			
		||||
cd ../..
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralsec
 | 
			
		||||
cd wlan-cloud-ucentralsec
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
cd cmake-build
 | 
			
		||||
cmake -DSMALL_BUILD=1 ..
 | 
			
		||||
make
 | 
			
		||||
cd ../..
 | 
			
		||||
```
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.13)
 | 
			
		||||
project(owsec VERSION 2.8.0)
 | 
			
		||||
project(owsec VERSION 3.2.0)
 | 
			
		||||
 | 
			
		||||
set(CMAKE_CXX_STANDARD 17)
 | 
			
		||||
 | 
			
		||||
@@ -42,7 +42,7 @@ if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
 | 
			
		||||
    string(REGEX REPLACE "\n$" "" GIT_HASH "${GIT_HASH}")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
add_definitions(-DAWS_CUSTOM_MEMORY_MANAGEMENT)
 | 
			
		||||
add_definitions(-DAWS_CUSTOM_MEMORY_MANAGEMENT -DBOOST_NO_CXX98_FUNCTION_BASE=1)
 | 
			
		||||
 | 
			
		||||
set(BUILD_SHARED_LIBS 1)
 | 
			
		||||
 | 
			
		||||
@@ -118,7 +118,7 @@ add_executable( owsec
 | 
			
		||||
        src/framework/RESTAPI_PartHandler.h
 | 
			
		||||
        src/framework/MicroService.cpp
 | 
			
		||||
        src/framework/MicroServiceExtra.h
 | 
			
		||||
 | 
			
		||||
        src/framework/default_device_types.h
 | 
			
		||||
        src/RESTObjects/RESTAPI_SecurityObjects.h src/RESTObjects/RESTAPI_SecurityObjects.cpp
 | 
			
		||||
        src/RESTObjects/RESTAPI_GWobjects.h src/RESTObjects/RESTAPI_GWobjects.cpp
 | 
			
		||||
        src/RESTObjects/RESTAPI_FMSObjects.h src/RESTObjects/RESTAPI_FMSObjects.cpp
 | 
			
		||||
@@ -175,7 +175,7 @@ add_executable( owsec
 | 
			
		||||
        src/TotpCache.h
 | 
			
		||||
        src/RESTAPI/RESTAPI_subtotp_handler.cpp src/RESTAPI/RESTAPI_subtotp_handler.h
 | 
			
		||||
        src/RESTAPI/RESTAPI_signup_handler.cpp src/RESTAPI/RESTAPI_signup_handler.h
 | 
			
		||||
        src/MessagingTemplates.cpp src/MessagingTemplates.h src/RESTAPI/RESTAPI_apiKey_handler.cpp src/RESTAPI/RESTAPI_apiKey_handler.h src/storage/orm_apikeys.cpp src/storage/orm_apikeys.h src/RESTAPI/RESTAPI_validate_apikey.cpp src/RESTAPI/RESTAPI_validate_apikey.h)
 | 
			
		||||
        src/MessagingTemplates.h src/RESTAPI/RESTAPI_apiKey_handler.cpp src/RESTAPI/RESTAPI_apiKey_handler.h src/storage/orm_apikeys.cpp src/storage/orm_apikeys.h src/RESTAPI/RESTAPI_validate_apikey.cpp src/RESTAPI/RESTAPI_validate_apikey.h src/RESTAPI/RESTAPI_systemSecret_handler.cpp src/RESTAPI/RESTAPI_systemSecret_handler.h src/SecretStore.cpp src/SecretStore.h)
 | 
			
		||||
 | 
			
		||||
if(NOT SMALL_BUILD)
 | 
			
		||||
    target_link_libraries(owsec PUBLIC
 | 
			
		||||
@@ -185,6 +185,7 @@ if(NOT SMALL_BUILD)
 | 
			
		||||
            CppKafka::cppkafka
 | 
			
		||||
            ${AWSSDK_LINK_LIBRARIES}
 | 
			
		||||
            fmt::fmt
 | 
			
		||||
            resolv
 | 
			
		||||
            )
 | 
			
		||||
    if(UNIX AND NOT APPLE)
 | 
			
		||||
        target_link_libraries(owsec PUBLIC PocoJSON)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										270
									
								
								CONFIGURATION.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,270 @@
 | 
			
		||||
# OWSEC Configuration
 | 
			
		||||
Here is the list of parameters you can configure in the `owsec.properties` file.
 | 
			
		||||
 | 
			
		||||
## OWSEC Specific Parameters
 | 
			
		||||
### OWSEC Login
 | 
			
		||||
```properties
 | 
			
		||||
authentication.default.password: 13268b7daa751240369d125e79c873bd8dd3bef7981bdfd38ea03dbb1fbe7dcf
 | 
			
		||||
authentication.default.username: tip@ucentral.com
 | 
			
		||||
authentication.enabled: true
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```properties
 | 
			
		||||
authentication.token.ageing = 30 * 24 * 60 * 60
 | 
			
		||||
authentication.oldpasswords = 5
 | 
			
		||||
openwifi.document.policy.access = /wwwassets/access_policy.html
 | 
			
		||||
openwifi.document.policy.password = /wwwassets/password_policy.html
 | 
			
		||||
authentication.validation.expression =
 | 
			
		||||
subscriber.validation.expression =
 | 
			
		||||
subscriber.policy.access = /wwwassets/access_policy.html
 | 
			
		||||
subscriber.policy.password = /wwwassets/password_policy.html
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Mail template variables
 | 
			
		||||
```properties
 | 
			
		||||
helper.user.email = charles.bourque@arilia.com
 | 
			
		||||
helper.sub.email = charles.bourque@arilia.com
 | 
			
		||||
helper.user.global.email = info@arilia.com
 | 
			
		||||
helper.sub.global.email = info@arilia.com
 | 
			
		||||
helper.user.site = https://ucentral.dpaas.arilia.com
 | 
			
		||||
helper.sub.site = https://ucentral.dpaas.arilia.com
 | 
			
		||||
helper.user.login = https://ucentral.dpaas.arilia.com
 | 
			
		||||
helper.sub.login = https://ucentral.dpaas.arilia.com
 | 
			
		||||
helper.user.signature = Arilia Wireless Inc.
 | 
			
		||||
helper.sub.signature = Arilia Wireless Inc.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Google authenticator
 | 
			
		||||
```properties
 | 
			
		||||
totp.issuer: Arilia
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Mailer 
 | 
			
		||||
```properties
 | 
			
		||||
mailer.enabled: true
 | 
			
		||||
mailer.hostname: email-smtp.us-west-2.amazonaws.com
 | 
			
		||||
mailer.loginmethod: login
 | 
			
		||||
mailer.password: ***********************************************
 | 
			
		||||
mailer.port: 587
 | 
			
		||||
mailer.sender: no-reply@arilia.com
 | 
			
		||||
mailer.templates: $OWSEC_ROOT/templates
 | 
			
		||||
mailer.username: AKIATXEXGKF3QZN543VS
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Built-in web server 
 | 
			
		||||
```properties
 | 
			
		||||
openwifi.avatar.maxsize: 2000000
 | 
			
		||||
openwifi.document.policy.access: /wwwassets/access_policy.html
 | 
			
		||||
openwifi.document.policy.password: /wwwassets/password_policy.html
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### SMS Sender
 | 
			
		||||
```properties
 | 
			
		||||
smssender.aws.accesskey: ***********************
 | 
			
		||||
smssender.aws.region: us-west-2
 | 
			
		||||
smssender.aws.secretkey: ******************************************+X
 | 
			
		||||
smssender.enabled: true
 | 
			
		||||
smssender.provider: aws
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```properties
 | 
			
		||||
smssender.provider = twilio
 | 
			
		||||
smssender.twilio.sid = ***********************
 | 
			
		||||
smssender.twilio.token = **********************
 | 
			
		||||
smssender.twilio.phonenumber = +18888888888
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Generic OpenWiFi SDK parameters
 | 
			
		||||
### REST API External parameters
 | 
			
		||||
These are the parameters required for the configuration of the external facing REST API server
 | 
			
		||||
```properties
 | 
			
		||||
openwifi.restapi.host.0.backlog = 100
 | 
			
		||||
openwifi.restapi.host.0.security = relaxed
 | 
			
		||||
openwifi.restapi.host.0.rootca = $OWSEC_ROOT/certs/restapi-ca.pem
 | 
			
		||||
openwifi.restapi.host.0.address = *
 | 
			
		||||
openwifi.restapi.host.0.port = 16001
 | 
			
		||||
openwifi.restapi.host.0.cert = $OWSEC_ROOT/certs/restapi-cert.pem
 | 
			
		||||
openwifi.restapi.host.0.key = $OWSEC_ROOT/certs/restapi-key.pem
 | 
			
		||||
openwifi.restapi.host.0.key.password = mypassword
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### openwifi.restapi.host.0.backlog
 | 
			
		||||
This is the number of concurrent REST API calls that maybe be kept in the backlog for processing. That's a good rule of thumb. Never go above 500.
 | 
			
		||||
#### openwifi.restapi.host.0.rootca
 | 
			
		||||
This is the root file of your own certificate CA in `pem` format.
 | 
			
		||||
#### openwifi.restapi.host.0.cert
 | 
			
		||||
This is your own server certificate in `pem` format..
 | 
			
		||||
#### openwifi.restapi.host.0.key
 | 
			
		||||
This is the private key associated with your own certificate in `pem` format.
 | 
			
		||||
#### openwifi.restapi.host.0.address
 | 
			
		||||
Leve this a `*` in the case you want to bind to all interfaces on your gateway host or select the address of a single interface.
 | 
			
		||||
#### openwifi.restapi.host.0.port
 | 
			
		||||
The port on which the REST API server is listening. By default, this is 16002.
 | 
			
		||||
#### openwifi.restapi.host.0.security
 | 
			
		||||
Leave this as `relaxed` for now for devices.
 | 
			
		||||
#### openwifi.restapi.host.0.key.password
 | 
			
		||||
If you key file uses a password, please enter it here.
 | 
			
		||||
 | 
			
		||||
### REST API Intra microservice parameters
 | 
			
		||||
The following parameters describe the configuration for the inter-microservice HTTP server. You may use the same certificate/key
 | 
			
		||||
you are using for your extenral server or another certificate.
 | 
			
		||||
```properties
 | 
			
		||||
openwifi.internal.restapi.host.0.backlog = 100
 | 
			
		||||
openwifi.internal.restapi.host.0.security = relaxed
 | 
			
		||||
openwifi.internal.restapi.host.0.rootca = $OWSEC_ROOT/certs/restapi-ca.pem
 | 
			
		||||
openwifi.internal.restapi.host.0.address = *
 | 
			
		||||
openwifi.internal.restapi.host.0.port = 17001
 | 
			
		||||
openwifi.internal.restapi.host.0.cert = $OWSEC_ROOT/certs/restapi-cert.pem
 | 
			
		||||
openwifi.internal.restapi.host.0.key = $OWSEC_ROOT/certs/restapi-key.pem
 | 
			
		||||
openwifi.internal.restapi.host.0.key.password = mypassword
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### openwifi.internal.host.0.backlog
 | 
			
		||||
This is the number of concurrent REST API calls that maybe be kept in the backlog for processing. That's a good rule of thumb. Never go above 500.
 | 
			
		||||
#### openwifi.internal.host.0.rootca
 | 
			
		||||
This is the root file of your own certificate CA in `pem` format.
 | 
			
		||||
#### openwifi.internal.host.0.cert
 | 
			
		||||
This is your own server certificate in `pem` format..
 | 
			
		||||
#### openwifi.internal.host.0.key
 | 
			
		||||
This is the private key associated with your own certificate in `pem` format.
 | 
			
		||||
#### openwifi.internal.host.0.address
 | 
			
		||||
Leve this a `*` in the case you want to bind to all interfaces on your gateway host or select the address of a single interface.
 | 
			
		||||
#### openwifi.internal.host.0.port
 | 
			
		||||
The port on which the REST API server is listening. By default, this is 17002.
 | 
			
		||||
#### openwifi.internal.host.0.security
 | 
			
		||||
Leave this as `relaxed` for now for devices.
 | 
			
		||||
#### openwifi.internal.host.0.key.password
 | 
			
		||||
If you key file uses a password, please enter it here.
 | 
			
		||||
 | 
			
		||||
### Microservice information
 | 
			
		||||
These are different Microservie parameters. Following is a brief explanation.
 | 
			
		||||
```properties
 | 
			
		||||
openwifi.service.key = $OWSEC_ROOT/certs/restapi-key.pem
 | 
			
		||||
openwifi.service.key.password = mypassword
 | 
			
		||||
openwifi.system.data = $OWSEC_ROOT/data
 | 
			
		||||
openwifi.system.uri.private = https://localhost:17004
 | 
			
		||||
openwifi.system.uri.public = https://ucentral.dpaas.arilia.com:16002
 | 
			
		||||
openwifi.system.uri.ui = https://ucentral-ui.arilia.com
 | 
			
		||||
openwifi.security.restapi.disable = false
 | 
			
		||||
openwifi.system.commandchannel = /tmp/app.ucentralfms
 | 
			
		||||
openwifi.autoprovisioning = true
 | 
			
		||||
```
 | 
			
		||||
#### openwifi.service.key
 | 
			
		||||
From time to time, the microservice must encrypt information. This is the key it should use. You may use the
 | 
			
		||||
same keey as you RESTAPI or your server.
 | 
			
		||||
#### openwifi.service.key.password
 | 
			
		||||
The password for the `openwifi.service.key`
 | 
			
		||||
#### openwifi.system.data
 | 
			
		||||
The location of system data. This path must exist.
 | 
			
		||||
#### openwifi.system.uri.private
 | 
			
		||||
The URI to reach the controller on the internal port.
 | 
			
		||||
#### openwifi.system.uri.public
 | 
			
		||||
The URI to reach the controller from the outside world.
 | 
			
		||||
#### openwifi.system.uri.ui
 | 
			
		||||
The URI of the UI to manage this service
 | 
			
		||||
#### openwifi.security.restapi.disable
 | 
			
		||||
This allows to disable security for internal and external API calls. This should only be used if the controller
 | 
			
		||||
sits behind an application load balancer that will actually do TLS. Setting this to `true` disables security.
 | 
			
		||||
#### openwifi.system.commandchannel
 | 
			
		||||
The UNIX socket command channel used by this service.
 | 
			
		||||
#### openwifi.autoprovisioning
 | 
			
		||||
Allow unknown devices to be provisioned by the system.
 | 
			
		||||
 | 
			
		||||
### ALB Support
 | 
			
		||||
In order to support an application load balancer health check verification, your need to provide the following parameters.
 | 
			
		||||
```properties
 | 
			
		||||
alb.enable = true
 | 
			
		||||
alb.port = 16101
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Kafka
 | 
			
		||||
The controller use Kafka, like all the other microservices. You must configure the kafka section in order for the
 | 
			
		||||
system to work.
 | 
			
		||||
```properties
 | 
			
		||||
openwifi.kafka.group.id = security
 | 
			
		||||
openwifi.kafka.client.id = security1
 | 
			
		||||
openwifi.kafka.enable = true
 | 
			
		||||
openwifi.kafka.brokerlist = my_Kafka.example.com:9092
 | 
			
		||||
openwifi.kafka.auto.commit = false
 | 
			
		||||
openwifi.kafka.queue.buffering.max.ms = 50
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### openwifi.kafka.group.id
 | 
			
		||||
The group ID is a single word that should identify the type of service tuning. In the case `security`
 | 
			
		||||
### openwifi.kafka.client.id
 | 
			
		||||
The client ID is a single service within that group ID. Each participant must have a unique client ID.
 | 
			
		||||
### openwifi.kafka.enable
 | 
			
		||||
Kafka should always be enabled.
 | 
			
		||||
### openwifi.kafka.brokerlist
 | 
			
		||||
The list of servers where your Kafka server is running. Comma separated.
 | 
			
		||||
### openwifi.kafka.auto.commit
 | 
			
		||||
Auto commit flag in Kafka. Leave as `false`.
 | 
			
		||||
### openwifi.kafka.queue.buffering.max.ms
 | 
			
		||||
Kafka buffering. Leave as `50`.
 | 
			
		||||
### Kafka security
 | 
			
		||||
If you intend to use SSL, you should look into Kafka Connect and specify the certificates below.
 | 
			
		||||
```properties
 | 
			
		||||
penwifi.kafka.ssl.ca.location =
 | 
			
		||||
openwifi.kafka.ssl.certificate.location =
 | 
			
		||||
openwifi.kafka.ssl.key.location =
 | 
			
		||||
openwifi.kafka.ssl.key.password =
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### DB Type
 | 
			
		||||
The controller supports 3 types of Database. SQLite should only be used for sites with less than 100 APs or for testing in the lab.
 | 
			
		||||
In order to select which database to use, you must set the `storage.type` value to sqlite, postgresql, or mysql.
 | 
			
		||||
 | 
			
		||||
```properties
 | 
			
		||||
storage.type = sqlite
 | 
			
		||||
#storage.type = postgresql
 | 
			
		||||
#storage.type = mysql
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Storage SQLite parameters
 | 
			
		||||
Additional parameters to set for SQLite. The only important one is `storage.type.sqlite.db` which is the database name on disk.
 | 
			
		||||
```properties
 | 
			
		||||
storage.type.sqlite.db = security.db
 | 
			
		||||
storage.type.sqlite.idletime = 120
 | 
			
		||||
storage.type.sqlite.maxsessions = 128
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Storage Postgres
 | 
			
		||||
Additional parameters to set if you select Postgres for your database. You must specify `host`, `username`, `password`,
 | 
			
		||||
`database`, and `port`.
 | 
			
		||||
```properties
 | 
			
		||||
storage.type.postgresql.maxsessions = 64
 | 
			
		||||
storage.type.postgresql.idletime = 60
 | 
			
		||||
storage.type.postgresql.host = localhost
 | 
			
		||||
storage.type.postgresql.username = security
 | 
			
		||||
storage.type.postgresql.password = security
 | 
			
		||||
storage.type.postgresql.database = security
 | 
			
		||||
storage.type.postgresql.port = 5432
 | 
			
		||||
storage.type.postgresql.connectiontimeout = 60
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Storage MySQL/MariaDB
 | 
			
		||||
Additional parameters to set if you select mysql for your database. You must specify `host`, `username`, `password`,
 | 
			
		||||
`database`, and `port`.
 | 
			
		||||
```properties
 | 
			
		||||
storage.type.mysql.maxsessions = 64
 | 
			
		||||
storage.type.mysql.idletime = 60
 | 
			
		||||
storage.type.mysql.host = localhost
 | 
			
		||||
storage.type.postgresql.username = security
 | 
			
		||||
storage.type.postgresql.password = security
 | 
			
		||||
storage.type.postgresql.database = security
 | 
			
		||||
storage.type.mysql.port = 3306
 | 
			
		||||
storage.type.mysql.connectiontimeout = 60
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Logging Parameters
 | 
			
		||||
The microservice provides extensive logging. If you would like to keep logging on disk, set the `logging.type = file`. If you only want
 | 
			
		||||
console logging, `set logging.type = console`. When selecting file, `logging.path` must exist. `logging.level` sets the
 | 
			
		||||
basic logging level for the entire controller. `logging.websocket` disables WebSocket logging.
 | 
			
		||||
 | 
			
		||||
```properties
 | 
			
		||||
logging.type = file
 | 
			
		||||
logging.path = $OWSEC_ROOT/logs
 | 
			
		||||
logging.level = information
 | 
			
		||||
logging.asynch = true
 | 
			
		||||
logging.websocket = false
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										38
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,38 @@
 | 
			
		||||
# How to Contribute
 | 
			
		||||
 | 
			
		||||
We'd love to accept your patches and contributions to this project. There are
 | 
			
		||||
just a few small guidelines you need to follow.
 | 
			
		||||
 | 
			
		||||
## Version of C++
 | 
			
		||||
This project is based on the C++17 standard and compiles as-is on most platforms 
 | 
			
		||||
using either clang or g++. Do not use C++21 or C++23 features for now. Some core 
 | 
			
		||||
libraries used in this project do not support C++21 or C++23 yet.
 | 
			
		||||
 | 
			
		||||
## Variable Naming
 | 
			
		||||
Naming of pretty much anything uses Pascal naming. Longer explicit names using casing. 
 | 
			
		||||
Member variable naming adds a `_` at the end of the vars. Try to
 | 
			
		||||
keep this standard going. Sometimes you must override a base class function and then of course
 | 
			
		||||
you need to follow the base class.
 | 
			
		||||
 | 
			
		||||
## This is a cmake project
 | 
			
		||||
This is a cmake project, and you need to adhere to the cmake rules. If you need
 | 
			
		||||
to add a package to the CMakeList, you need to ensure that the package is available
 | 
			
		||||
on all required platforms and compiles. Remember that this project runs on Linux, OS X, 
 | 
			
		||||
and the Raspberry PI.
 | 
			
		||||
 | 
			
		||||
## Licensed packages
 | 
			
		||||
When adding a package, you must also state the licensing for the package. MIT, BSD, Apache licenses
 | 
			
		||||
are acceptable. No commercial licenses are allowed. 
 | 
			
		||||
 | 
			
		||||
## clang formatting
 | 
			
		||||
Please format your code using the included `.clang-format` file included in the project.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
clang-format -i --style=<project root>/.clang-format myfile.cpp
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Pull Requests
 | 
			
		||||
All submissions, including submissions by project members, require review. We
 | 
			
		||||
accept GitHub pull requests. Please create a branch with the Jira name for addressing the issue you are fixing or the 
 | 
			
		||||
feature you are implementing.
 | 
			
		||||
Create a pull-request from the branch into master. 
 | 
			
		||||
							
								
								
									
										24
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						@@ -1,6 +1,7 @@
 | 
			
		||||
ARG DEBIAN_VERSION=11.5-slim
 | 
			
		||||
ARG POCO_VERSION=poco-tip-v2
 | 
			
		||||
ARG CPPKAFKA_VERSION=tip-v1
 | 
			
		||||
ARG VALIJASON_VERSION=tip-v1
 | 
			
		||||
 | 
			
		||||
FROM debian:$DEBIAN_VERSION AS build-base
 | 
			
		||||
 | 
			
		||||
@@ -14,8 +15,8 @@ FROM build-base AS poco-build
 | 
			
		||||
 | 
			
		||||
ARG POCO_VERSION
 | 
			
		||||
 | 
			
		||||
ADD https://api.github.com/repos/AriliaWireless/poco/git/refs/tags/${POCO_VERSION} version.json
 | 
			
		||||
RUN git clone https://github.com/AriliaWireless/poco --branch ${POCO_VERSION} /poco
 | 
			
		||||
ADD https://api.github.com/repos/Telecominfraproject/wlan-cloud-lib-poco/git/refs/tags/${POCO_VERSION} version.json
 | 
			
		||||
RUN git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch ${POCO_VERSION} /poco
 | 
			
		||||
 | 
			
		||||
WORKDIR /poco
 | 
			
		||||
RUN mkdir cmake-build
 | 
			
		||||
@@ -28,8 +29,8 @@ FROM build-base AS cppkafka-build
 | 
			
		||||
 | 
			
		||||
ARG CPPKAFKA_VERSION
 | 
			
		||||
 | 
			
		||||
ADD https://api.github.com/repos/AriliaWireless/cppkafka/git/refs/tags/${CPPKAFKA_VERSION} version.json
 | 
			
		||||
RUN git clone https://github.com/AriliaWireless/cppkafka --branch ${CPPKAFKA_VERSION} /cppkafka
 | 
			
		||||
ADD https://api.github.com/repos/Telecominfraproject/wlan-cloud-lib-cppkafka/git/refs/tags/${CPPKAFKA_VERSION} version.json
 | 
			
		||||
RUN git clone https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka --branch ${CPPKAFKA_VERSION} /cppkafka
 | 
			
		||||
 | 
			
		||||
WORKDIR /cppkafka
 | 
			
		||||
RUN mkdir cmake-build
 | 
			
		||||
@@ -38,6 +39,20 @@ RUN cmake ..
 | 
			
		||||
RUN cmake --build . --config Release -j8
 | 
			
		||||
RUN cmake --build . --target install
 | 
			
		||||
 | 
			
		||||
FROM build-base AS valijson-build
 | 
			
		||||
 | 
			
		||||
ARG VALIJASON_VERSION
 | 
			
		||||
 | 
			
		||||
ADD https://api.github.com/repos/Telecominfraproject/wlan-cloud-lib-valijson/git/refs/tags/${VALIJASON_VERSION} version.json
 | 
			
		||||
RUN git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch ${VALIJASON_VERSION} /valijson
 | 
			
		||||
 | 
			
		||||
WORKDIR /valijson
 | 
			
		||||
RUN mkdir cmake-build
 | 
			
		||||
WORKDIR cmake-build
 | 
			
		||||
RUN cmake ..
 | 
			
		||||
RUN cmake --build . --config Release -j8
 | 
			
		||||
RUN cmake --build . --target install
 | 
			
		||||
 | 
			
		||||
FROM build-base AS owsec-build
 | 
			
		||||
 | 
			
		||||
ADD CMakeLists.txt build /owsec/
 | 
			
		||||
@@ -95,6 +110,7 @@ COPY --from=owsec-build /owsec/cmake-build/owsec /openwifi/owsec
 | 
			
		||||
COPY --from=owsec-build /vcpkg/installed/x64-linux/lib/ /usr/local/lib/
 | 
			
		||||
COPY --from=cppkafka-build /cppkafka/cmake-build/src/lib/ /usr/local/lib/
 | 
			
		||||
COPY --from=poco-build /poco/cmake-build/lib/ /usr/local/lib/
 | 
			
		||||
COPY --from=valijson-build /usr/local/include /usr/local/include
 | 
			
		||||
 | 
			
		||||
RUN ldconfig
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										308
									
								
								README.md
									
									
									
									
									
								
							
							
						
						@@ -1,70 +1,100 @@
 | 
			
		||||
# ucentralsec
 | 
			
		||||
<p align="center">
 | 
			
		||||
    <img src="images/project/logo.svg" width="200"/>
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
uCentralSec is the Authentication & Resource Policy Access service for the uCentral system. In order to use the uCentral system
 | 
			
		||||
you must have at least 1 uCentralSec. uCentralSec is the first point of contact for the entire architecture. We strongly recommend using Docker 
 | 
			
		||||
to deploy all the uCentral services. If you would like to develop and play with the source, please do.
 | 
			
		||||
# OpenWiFi Security  (OWSEC)
 | 
			
		||||
 | 
			
		||||
## What is it?
 | 
			
		||||
The OWSEC is a service for the TIP OpenWiFi CloudSDK (OWSDK).
 | 
			
		||||
OWSEC is the Authentication and Resource Policy Access service for the TIP
 | 
			
		||||
OpenWiFi Cloud SDK (OWSDK). OWSEC,
 | 
			
		||||
like all other OWSDK microservices, is defined using an OpenAPI definition and uses the ucentral communication
 | 
			
		||||
protocol to interact with Access Points. To use the OWSUB, you either need to [build it](#building) or use the
 | 
			
		||||
[Docker version](#docker).
 | 
			
		||||
 | 
			
		||||
## Building
 | 
			
		||||
To build the microservice from source, please follow the instructions in [here](./BUILDING.md)
 | 
			
		||||
 | 
			
		||||
## Docker
 | 
			
		||||
To use the CLoudSDK deployment please follow [here](https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy)
 | 
			
		||||
 | 
			
		||||
## OpenAPI
 | 
			
		||||
Like all other uCentral services, uCentralSec is defined through an OpenAPI. You can use this API to build your own applications or integration modules
 | 
			
		||||
into your own systems. If all you need it to access the uCentralGW for example (the service that manages the APs), you will need to:
 | 
			
		||||
You may get static page with OpenAPI docs generated from the definition on [GitHub Page](https://telecominfraproject.github.io/wlan-cloud-ucentralsec/).
 | 
			
		||||
Also, you may use [Swagger UI](https://petstore.swagger.io/#/) with OpenAPI definition file raw link (i.e. [latest version file](https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentralsec/main/openapi/owsec.yaml)) to get interactive docs page.
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
Like all other OWSDK services, OWSEC is defined through an OpenAPI. You can use this API to build your own 
 | 
			
		||||
applications or integration modules into your own systems. If all you need it to access the OWGW for 
 | 
			
		||||
example (the service that manages the APs), you will need to:
 | 
			
		||||
- get a token (`/oauth2`)
 | 
			
		||||
- find the endpoints on the system (`/systemEndpoints`) 
 | 
			
		||||
- choose one to manage (pick an endpoint that matches what you are trying to do by looking at its `type`. For the gateway, type = ucentrtalgw)
 | 
			
		||||
- make your calls (use the PublicEndPoint of the corresponding entry to make your calls, do not forget to add `/api/v1` as the root os the call)
 | 
			
		||||
- choose a microservice to manage (pick an endpoint that matches what you are trying to do by looking at its 
 | 
			
		||||
`type`. For the Cloud SDK Controller, type = owgw)
 | 
			
		||||
- make your calls (use the PublicEndPoint of the corresponding entry to make your calls, 
 | 
			
		||||
do not forget to add `/api/v1` as the root os the call)
 | 
			
		||||
 | 
			
		||||
The CLI for the [uCentralGW](https://github.com/telecominfraproject/wlan-cloud-ucentralgw/blob/main/test_scripts/curl/cli) has a very good example of this. 
 | 
			
		||||
Look for the `setgateway` function.
 | 
			
		||||
The CLI for the [OWGW](https://github.com/telecominfraproject/wlan-cloud-ucentralsec/blob/main/test_scripts/curl/cli) has 
 | 
			
		||||
a very good example of this. Look for the `setgateway` function.
 | 
			
		||||
 | 
			
		||||
You may get static page with OpenAPI docs generated from the definition on [GitHub Page](https://telecominfraproject.github.io/wlan-cloud-ucentralsec/).
 | 
			
		||||
 | 
			
		||||
Also you may use [Swagger UI](https://petstore.swagger.io/#/) with OpenAPI definition file raw link (i.e. [latest version file](https://validator.swagger.io/validator?url=https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentralsec/main/openpapi/owsec.yaml)) to get interactive docs page.
 | 
			
		||||
Also, you may use [Swagger UI](https://petstore.swagger.io/#/) with OpenAPI definition file raw link (i.e. [latest version file](https://validator.swagger.io/validator?url=https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentralsec/main/openpapi/owsec.yaml)) to get interactive docs page.
 | 
			
		||||
 | 
			
		||||
#### Expected directory layout
 | 
			
		||||
From the directory where your cloned source is, you will need to create the `certs`, `logs`, and `uploads` directories.
 | 
			
		||||
```bash
 | 
			
		||||
mkdir certs
 | 
			
		||||
mkdir certs/cas
 | 
			
		||||
mkdir logs
 | 
			
		||||
mkdir uploads
 | 
			
		||||
```
 | 
			
		||||
You should now have the following:
 | 
			
		||||
```text
 | 
			
		||||
--+-- certs
 | 
			
		||||
  |   +--- cas
 | 
			
		||||
  +-- cmake
 | 
			
		||||
  +-- cmake-build
 | 
			
		||||
  +-- logs
 | 
			
		||||
  +-- src
 | 
			
		||||
  +-- test_scripts
 | 
			
		||||
  +-- openapi
 | 
			
		||||
  +-- uploads
 | 
			
		||||
  +-- owsec.properties
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Certificate
 | 
			
		||||
The OWSEC uses a certificate to provide security for the REST API Certificate to secure the Northbound API.
 | 
			
		||||
 | 
			
		||||
#### The `certs` directory
 | 
			
		||||
For all deployments, you will need the following `certs` directory, populated with the proper files.
 | 
			
		||||
 | 
			
		||||
```text
 | 
			
		||||
certs ---+--- restapi-ca.pem
 | 
			
		||||
         +--- restapi-cert.pem
 | 
			
		||||
         +--- restapi-key.pem
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Firewall Considerations
 | 
			
		||||
The entire uCentral systems uses several MicroServices. In order for the whole system to work, you should provide the following port
 | 
			
		||||
access:
 | 
			
		||||
| Port  | Description                                | Configurable |
 | 
			
		||||
|:------|:-------------------------------------------|:------------:|
 | 
			
		||||
| 16001 | Default port from the devices to the OWSEC |     yes      |
 | 
			
		||||
 | 
			
		||||
- Security
 | 
			
		||||
  - Properties file: owsec.properties
 | 
			
		||||
  - Ports
 | 
			
		||||
    - Public: 16001
 | 
			
		||||
    - Private: 17001
 | 
			
		||||
    - ALB: 16101
 | 
			
		||||
### Environment variables
 | 
			
		||||
The following environment variables should be set from the root directory of the service. They tell the OWGW process where to find
 | 
			
		||||
the configuration and the root directory.
 | 
			
		||||
```bash
 | 
			
		||||
export OWGW_ROOT=`pwd`
 | 
			
		||||
export OWGW_CONFIG=`pwd`
 | 
			
		||||
```
 | 
			
		||||
You can run the shell script `set_env.sh` from the microservice root.
 | 
			
		||||
 | 
			
		||||
- Gateway:
 | 
			
		||||
  - Properties file: owgw.properties
 | 
			
		||||
  - Ports
 | 
			
		||||
    - Public: 16002
 | 
			
		||||
    - Private: 17002
 | 
			
		||||
    - ALB: 16102
 | 
			
		||||
 | 
			
		||||
- Firmware:
 | 
			
		||||
  - Properties file: owfms.properties
 | 
			
		||||
  - Ports
 | 
			
		||||
    - Public: 16004
 | 
			
		||||
    - Private: 17004
 | 
			
		||||
    - ALB: 16104
 | 
			
		||||
 | 
			
		||||
- Provisioning:
 | 
			
		||||
  - Properties file: owprov.properties
 | 
			
		||||
  - Ports
 | 
			
		||||
    - Public: 16004
 | 
			
		||||
    - Private: 17004
 | 
			
		||||
    - ALB: 16104
 | 
			
		||||
 | 
			
		||||
## Security Configuration
 | 
			
		||||
The service relies on a properties configuration file called `owsec.properties`. In this file, you should configure several entries. Many values are optional 
 | 
			
		||||
and you can rely on the defaults. Here are some values of note:
 | 
			
		||||
 | 
			
		||||
### `authentication.default.password`
 | 
			
		||||
Set the hash of the default username and password. Please look below on how to do this. 
 | 
			
		||||
 | 
			
		||||
### `authentication.default.username`
 | 
			
		||||
Set the default username to use to login.
 | 
			
		||||
### OWSEC Service Configuration
 | 
			
		||||
The configuration is kept in a file called `owsec.properties`. To understand the content of this file,
 | 
			
		||||
please look [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralsec/blob/main/CONFIGURATION.md)
 | 
			
		||||
 | 
			
		||||
### Default username and password
 | 
			
		||||
The default username and password are set in `owsec.properties` file. The following entries manage the username and password
 | 
			
		||||
```text
 | 
			
		||||
```properties
 | 
			
		||||
authentication.default.username = tip@ucentral.com
 | 
			
		||||
authentication.default.password = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 | 
			
		||||
```
 | 
			
		||||
@@ -79,36 +109,17 @@ echo -n "weLoveWifiroot@system.com" | shasum -a 256
 | 
			
		||||
b5bfed31e2a272e52973a57b95042ab842db3999475f3d79f1ce0f45f465e34c  -
 | 
			
		||||
```
 | 
			
		||||
Then you need to modify your properties file like this
 | 
			
		||||
```text
 | 
			
		||||
```properties
 | 
			
		||||
authentication.default.username = root@system.com
 | 
			
		||||
authentication.default.password = b5bfed31e2a272e52973a57b95042ab842db3999475f3d79f1ce0f45f465e34c
 | 
			
		||||
```
 | 
			
		||||
Remember, when you login, use `root@system.com` with the password `weLoveWifi`, not this monster digit sequence.
 | 
			
		||||
 | 
			
		||||
#### Is this safe?
 | 
			
		||||
Is this safe to show the hash in a text file? Let me put it this way, if you can find a way to break this encryption, you
 | 
			
		||||
would have control over the entire internet. It's incredibly safe. If you love math, you can find a lot of videos explaining
 | 
			
		||||
how hashes work and why they are safe.
 | 
			
		||||
 | 
			
		||||
### `authentication.validation.expression`
 | 
			
		||||
This is a regular expression (regex) to verify the incoming password. You can find many examples on the internet on how to create these expressions. I suggest 
 | 
			
		||||
that using Google is your friend. Someone has figured out what you want to do already. Click [here](https://stackoverflow.com/questions/19605150/regex-for-password-must-contain-at-least-eight-characters-at-least-one-number-a)
 | 
			
		||||
to get a sample. The default is
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### `authentication.oldpasswords`
 | 
			
		||||
The number of older passwords to keep. Default is 5.
 | 
			
		||||
 | 
			
		||||
### Changing default password
 | 
			
		||||
 | 
			
		||||
On the first startup of the service new user will be created with the default credentials from properties `authentication.default.username` and `authentication.default.password`, but **you will have to change the password** before making any real requests.
 | 
			
		||||
 | 
			
		||||
You can this using [owgw-ui](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui/) on first login or using the following script:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```bash
 | 
			
		||||
export OWSEC=openwifi.wlan.local:16001 # endpoint to your owsec RESTAPI endpoint
 | 
			
		||||
#export FLAGS="-k" # uncomment and add curl flags that you would like to pass for the request (for example '-k' may be used to pass errors with self-signed certificates)
 | 
			
		||||
export OWSEC_DEFAULT_USERNAME=root@system.com # default username that you've set in property 'authentication.default.username'
 | 
			
		||||
@@ -119,7 +130,7 @@ test_scripts/curl/cli testlogin $OWSEC_DEFAULT_USERNAME $OWSEC_DEFAULT_PASSWORD
 | 
			
		||||
 | 
			
		||||
CLI is also included in Docker image if you want to run it this way:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```bash
 | 
			
		||||
export OWSEC=openwifi.wlan.local:16001
 | 
			
		||||
#export FLAGS="-k"
 | 
			
		||||
export OWSEC_DEFAULT_USERNAME=root@system.com
 | 
			
		||||
@@ -136,136 +147,25 @@ docker run --rm -ti \
 | 
			
		||||
  /cli testlogin $OWSEC_DEFAULT_USERNAME $OWSEC_DEFAULT_PASSWORD $OWSEC_NEW_PASSWORD
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Kafka integration
 | 
			
		||||
This security service uses Kafka to coordinate security with other services that are part of the system. You must have a Kafka service running
 | 
			
		||||
in order to use this. You can find several examples of Kafka services available with Docker. Here are the values you need to configure.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
openwifi.kafka.group.id = security
 | 
			
		||||
openwifi.kafka.client.id = security1
 | 
			
		||||
openwifi.kafka.enable = true
 | 
			
		||||
openwifi.kafka.brokerlist = my.kafkaserver.arilia.com:9092
 | 
			
		||||
openwifi.kafka.auto.commit = false
 | 
			
		||||
openwifi.kafka.queue.buffering.max.ms = 50
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### `openwifi.kafka.brokerlist`
 | 
			
		||||
This is the list of your kafka brokers. This is a comma separated list. You should use IP addresses or FQDNs and the relevant ports, usually 9092 is the 
 | 
			
		||||
default.
 | 
			
		||||
 | 
			
		||||
#### `openwifi.kafka.group.id`
 | 
			
		||||
Every service on the Kafka bux must have a unique value (at least in our case). This should be a string. We suggest using a name corresponding to the 
 | 
			
		||||
function provided. In this case, security.
 | 
			
		||||
 | 
			
		||||
### Certificates
 | 
			
		||||
Of course we need certificates. In our case, we already have existing certificates we have. You should find out how your file name correspond
 | 
			
		||||
to our names. We suggest reusing the same names we use so it is easier to use our default configuration files. We suggest using proper certificates 
 | 
			
		||||
for the publicly visible interfaces. For private interfaces, self-signed certificates are OK. We will not describe how to use/create private certificates 
 | 
			
		||||
here.
 | 
			
		||||
 | 
			
		||||
#### The public interface
 | 
			
		||||
Here are the parameters for the public interface. The important files are:
 | 
			
		||||
- `restapi-ca.pem` : the CA of your certificate
 | 
			
		||||
- `restapi-cert.pem` : the certificate for the public interface
 | 
			
		||||
- `restapi-key.pem` : the key associated with this certificate
 | 
			
		||||
- `openwifi.restapi.host.0.key.password` : if you key is password protected, you may supply that password here.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
openwifi.restapi.host.0.backlog = 100
 | 
			
		||||
openwifi.restapi.host.0.security = relaxed
 | 
			
		||||
openwifi.restapi.host.0.rootca = $OWSEC_ROOT/certs/restapi-ca.pem
 | 
			
		||||
openwifi.restapi.host.0.address = *
 | 
			
		||||
openwifi.restapi.host.0.port = 16001
 | 
			
		||||
openwifi.restapi.host.0.cert = $OWSEC_ROOT/certs/restapi-cert.pem
 | 
			
		||||
openwifi.restapi.host.0.key = $OWSEC_ROOT/certs/restapi-key.pem
 | 
			
		||||
openwifi.restapi.host.0.key.password = mypassword
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### The private interface
 | 
			
		||||
The private interface is used for service-to-service communication. You can use self-signed certificates here or letsencrypt. The file names are similar 
 | 
			
		||||
to the filenames used in the previous section.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
openwifi.internal.restapi.host.0.backlog = 100
 | 
			
		||||
openwifi.internal.restapi.host.0.security = relaxed
 | 
			
		||||
openwifi.internal.restapi.host.0.rootca = $OWSEC_ROOT/certs/restapi-ca.pem
 | 
			
		||||
openwifi.internal.restapi.host.0.address = *
 | 
			
		||||
openwifi.internal.restapi.host.0.port = 17001
 | 
			
		||||
openwifi.internal.restapi.host.0.cert = $OWSEC_ROOT/certs/restapi-cert.pem
 | 
			
		||||
openwifi.internal.restapi.host.0.key = $OWSEC_ROOT/certs/restapi-key.pem
 | 
			
		||||
openwifi.internal.restapi.host.0.key.password = mypassword
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Other important values
 | 
			
		||||
Here are other important values you must set.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
openwifi.system.data = $OWSEC_ROOT/data
 | 
			
		||||
openwifi.system.uri.private = https://localhost:17001
 | 
			
		||||
openwifi.system.uri.public = https://openwifi.dpaas.arilia.com:16001
 | 
			
		||||
openwifi.system.uri.ui = https://ucentral-ui.arilia.com
 | 
			
		||||
openwifi.system.commandchannel = /tmp/app.ucentralsec
 | 
			
		||||
openwifi.service.key = $OWSEC_ROOT/certs/restapi-key.pem
 | 
			
		||||
openwifi.service.key.password = mypassword
 | 
			
		||||
```
 | 
			
		||||
#### `openwifi.system.data`
 | 
			
		||||
The location of some important data files including the user name database.
 | 
			
		||||
 | 
			
		||||
#### `openwifi.system.uri.private`
 | 
			
		||||
This is the FQDN used internally between services.
 | 
			
		||||
 | 
			
		||||
#### `openwifi.system.uri.public`
 | 
			
		||||
This is the FQDN used externally serving the OpenAPI interface.
 | 
			
		||||
 | 
			
		||||
### Sending SMS for Multifactor Aithentication
 | 
			
		||||
`owsec` hs the ability to send SMS messages to users during login or to send notifications. In order to do so,
 | 
			
		||||
an SMS provider must be configured. At present time, 2 providers are supported: Tilio and AWS SNS
 | 
			
		||||
 | 
			
		||||
#### AWS SMS
 | 
			
		||||
For SNS you must create an IAM ID that has sns:sendmessage rights.  
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
smssender.enabled = true
 | 
			
		||||
smssender.provider = aws
 | 
			
		||||
smssender.aws.secretkey = ***************************************
 | 
			
		||||
smssender.aws.accesskey = ***************************************
 | 
			
		||||
smssender.aws.region = **************
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Twilio
 | 
			
		||||
For Twilio, you must provide the following
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
smssender.enabled = true
 | 
			
		||||
smssender.provider = twilio
 | 
			
		||||
smssender.twilio.sid = ***********************
 | 
			
		||||
smssender.twilio.token = **********************
 | 
			
		||||
smssender.twilio.phonenumber = +18888888888
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### `owsec` Messaging Configuration
 | 
			
		||||
`owsec` nay require to send e-mails. In order to do so, you must configure an email sender. We have run tests 
 | 
			
		||||
with GMail and AWS SES. For each, you must obtain the proper credentials and insert them in this configuration as well
 | 
			
		||||
as the proper mail host.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
mailer.enabled = true
 | 
			
		||||
mailer.hostname = smtp.gmail.com
 | 
			
		||||
mailer.username = ************************
 | 
			
		||||
mailer.password = ************************
 | 
			
		||||
mailer.sender = OpenWIFI
 | 
			
		||||
mailer.loginmethod = login
 | 
			
		||||
mailer.port = 587
 | 
			
		||||
mailer.templates = $OWSEC_ROOT/templates
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Google Authenticator
 | 
			
		||||
In order to use the Google Time-based One-Time Password (TOTP), the user must download the Google Authenticator 
 | 
			
		||||
on any other app that support the TOTP protocol. You should include the following in your configuration
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
totp.issuer = OrgName
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
It is very important that you not use spaces in your OrgName.
 | 
			
		||||
## Kafka topics
 | 
			
		||||
Toe read more about Kafka, follow the [document](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/KAFKA.md)
 | 
			
		||||
 | 
			
		||||
## Contributions
 | 
			
		||||
We need more contributors. Should you wish to contribute,
 | 
			
		||||
please follow the [contributions](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/CONTRIBUTING.md) document.
 | 
			
		||||
 | 
			
		||||
## Pull Requests
 | 
			
		||||
Please create a branch with the Jira addressing the issue you are fixing or the feature you are implementing.
 | 
			
		||||
Create a pull-request from the branch into master.
 | 
			
		||||
 | 
			
		||||
## Additional OWSDK Microservices
 | 
			
		||||
Here is a list of additional OWSDK microservices
 | 
			
		||||
| Name | Description | Link | OpenAPI |
 | 
			
		||||
| :--- | :--- | :---: | :---: |
 | 
			
		||||
| OWSEC | Security Service | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralsec) | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralsec/blob/main/openpapi/owsec.yaml) |
 | 
			
		||||
| OWGW | Controller Service | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw) | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/openapi/owgw.yaml) |
 | 
			
		||||
| OWFMS | Firmware Management Service | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralfms) | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralfms/blob/main/openapi/owfms.yaml) |
 | 
			
		||||
| OWPROV | Provisioning Service | [here](https://github.com/Telecominfraproject/wlan-cloud-owprov) | [here](https://github.com/Telecominfraproject/wlan-cloud-owprov/blob/main/openapi/owprov.yaml) |
 | 
			
		||||
| OWANALYTICS | Analytics Service | [here](https://github.com/Telecominfraproject/wlan-cloud-analytics) | [here](https://github.com/Telecominfraproject/wlan-cloud-analytics/blob/main/openapi/owanalytics.yaml) |
 | 
			
		||||
| OWSUB | Subscriber Service | [here](https://github.com/Telecominfraproject/wlan-cloud-userportal) | [here](https://github.com/Telecominfraproject/wlan-cloud-userportal/blob/main/openapi/userportal.yaml) |
 | 
			
		||||
 
 | 
			
		||||
@@ -60,6 +60,16 @@ if [[ "$TEMPLATE_CONFIG" = 'true' ]]; then
 | 
			
		||||
  STORAGE_TYPE_MYSQL_PASSWORD=${STORAGE_TYPE_MYSQL_PASSWORD:-"owsec"} \
 | 
			
		||||
  STORAGE_TYPE_MYSQL_DATABASE=${STORAGE_TYPE_MYSQL_DATABASE:-"owsec"} \
 | 
			
		||||
  STORAGE_TYPE_MYSQL_PORT=${STORAGE_TYPE_MYSQL_PORT:-"3306"} \
 | 
			
		||||
  USER_HELPER_EMAIL=${USER_HELPER_EMAIL:-"openwifi@telecominfraproject.com"} \
 | 
			
		||||
  SUB_HELPER_EMAIL=${SUB_HELPER_EMAIL:-"openwifi@telecominfraproject.com"} \
 | 
			
		||||
  GLOBAL_USER_HELPER_EMAIL=${GLOBAL_USER_HELPER_EMAIL:-"openwifi@telecominfraproject.com"} \
 | 
			
		||||
  GLOBAL_SUB_HELPER_EMAIL=${GLOBAL_SUB_HELPER_EMAIL:-"openwifi@telecominfraproject.com"} \
 | 
			
		||||
  USER_HELPER_SITE=${USER_HELPER_SITE:-"https://openwifi.telecominfraproject.com"} \
 | 
			
		||||
  SUB_HELPER_SITE=${SUB_HELPER_SITE:-"https://openwifi.telecominfraproject.com"} \
 | 
			
		||||
  USER_SYSTEM_LOGIN=${USER_SYSTEM_LOGIN:-"https://openwifi.telecominfraproject.com"} \
 | 
			
		||||
  SUB_SYSTEM_LOGIN=${SUB_SYSTEM_LOGIN:-"https://openwifi.telecominfraproject.com"} \
 | 
			
		||||
  USER_SIGNATURE=${USER_SIGNATURE:-"Telecom Infra Project"} \
 | 
			
		||||
  SUB_SIGNATURE=${SUB_SIGNATURE:-"Telecom Infra Project"} \
 | 
			
		||||
  envsubst < /owsec.properties.tmpl > $OWSEC_CONFIG/owsec.properties
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								images/device_types/cig_wf160d.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 104 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/cig_wf188.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 80 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/cig_wf188n.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 80 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/cig_wf194c.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 75 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/cig_wf194c4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 75 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/cig_wf808.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 218 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/cig_wf809.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 158 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/edgecore_eap101.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 140 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/edgecore_eap102.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 121 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/edgecore_ecs4100-12ph.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 44 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/edgecore_ecw5211.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 192 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/edgecore_ecw5410.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 197 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/edgecore_oap100.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 50 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/edgecore_spw2ac1200-lan-poe.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 59 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/edgecore_spw2ac1200.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 59 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/edgecore_ssw2ac2600.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 51 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/hfcl_ion4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 72 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/hfcl_ion4.yml.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 72 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/indio_um-305ac.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 34 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/linksys_e8450-ubi.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 98 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/linksys_ea6350-v4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 89 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/linksys_ea6350.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 89 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/linksys_ea8300.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 204 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/tp-link_ec420-g1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 159 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/tplink_ec420.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 159 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/tplink_ex227.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 103 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/tplink_ex228.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 103 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/tplink_ex447.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 103 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/wallys_dr40x9.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 59 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/wallys_dr6018.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 80 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/device_types/wallys_dr6018_v4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 80 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								images/project/logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.8 KiB  | 
							
								
								
									
										165
									
								
								images/project/logo.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,165 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 24.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 | 
			
		||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 | 
			
		||||
	 viewBox="0 0 141.5 185.6" style="enable-background:new 0 0 141.5 185.6;" xml:space="preserve">
 | 
			
		||||
<style type="text/css">
 | 
			
		||||
	.st0{fill:#414141;}
 | 
			
		||||
	.st1{fill:#FFFFFF;}
 | 
			
		||||
	.st2{fill:#FED206;}
 | 
			
		||||
	.st3{fill:#EB6F53;}
 | 
			
		||||
	.st4{fill:#3BA9B6;}
 | 
			
		||||
</style>
 | 
			
		||||
<g>
 | 
			
		||||
	<g>
 | 
			
		||||
		<path class="st0" d="M120.7,183.9H21.5c-10.8,0-19.5-8.7-19.5-19.5V20.5c0-10.8,8.7-19.5,19.5-19.5h99.2
 | 
			
		||||
			c10.8,0,19.5,8.7,19.5,19.5v143.9C140.2,175.2,131.5,183.9,120.7,183.9z"/>
 | 
			
		||||
		<g>
 | 
			
		||||
			<g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M46.3,166.2v-3.4h-1.2v-0.6h3.1v0.6H47v3.4H46.3z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M49,166.2v-4h2.7v0.6h-2v1h2v0.6h-2v1.1h2v0.6H49z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M52.6,166.2v-4h0.7v3.4h1.8v0.6H52.6z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M55.7,166.2v-4h2.7v0.6h-2v1h2v0.6h-2v1.1h2v0.6H55.7z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M59.1,164.2c0-1.2,0.9-2.1,2.1-2.1c0.8,0,1.3,0.4,1.6,0.9l-0.6,0.3c-0.2-0.3-0.6-0.6-1-0.6
 | 
			
		||||
						c-0.8,0-1.4,0.6-1.4,1.4c0,0.8,0.6,1.4,1.4,1.4c0.4,0,0.8-0.3,1-0.6l0.6,0.3c-0.3,0.5-0.8,0.9-1.6,0.9
 | 
			
		||||
						C60,166.3,59.1,165.5,59.1,164.2z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M63.2,164.2c0-1.2,0.8-2.1,2-2.1c1.2,0,2,0.9,2,2.1c0,1.2-0.8,2.1-2,2.1C64,166.3,63.2,165.4,63.2,164.2z
 | 
			
		||||
						 M66.5,164.2c0-0.8-0.5-1.4-1.3-1.4c-0.8,0-1.3,0.6-1.3,1.4c0,0.8,0.5,1.4,1.3,1.4C66,165.7,66.5,165,66.5,164.2z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M71.3,166.2v-3.1l-1.2,3.1h-0.3l-1.2-3.1v3.1h-0.7v-4h1l1.1,2.7l1.1-2.7h1v4H71.3z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M75.7,166.2v-4h0.7v4H75.7z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M80.4,166.2l-2.1-2.8v2.8h-0.7v-4h0.7l2,2.8v-2.8h0.7v4H80.4z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M82.3,166.2v-4H85v0.6h-2v1h2v0.6h-2v1.7H82.3z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M87.9,166.2l-0.9-1.5h-0.7v1.5h-0.7v-4h1.7c0.8,0,1.3,0.5,1.3,1.2c0,0.7-0.5,1.1-0.9,1.2l1,1.6H87.9z
 | 
			
		||||
						 M88,163.5c0-0.4-0.3-0.6-0.7-0.6h-1v1.3h1C87.7,164.1,88,163.9,88,163.5z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M92.4,166.2l-0.3-0.8h-1.8l-0.3,0.8h-0.8l1.6-4h0.9l1.6,4H92.4z M91.2,162.9l-0.7,1.9h1.4L91.2,162.9z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M95.8,166.2v-4h1.5c0.8,0,1.2,0.5,1.2,1.2c0,0.6-0.4,1.2-1.2,1.2h-1.2v1.7H95.8z M98.2,163.4
 | 
			
		||||
						c0-0.5-0.3-0.9-0.9-0.9h-1.1v1.7h1.1C97.8,164.3,98.2,163.9,98.2,163.4z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M101.5,166.2l-1.1-1.6h-0.9v1.6h-0.3v-4h1.5c0.7,0,1.2,0.4,1.2,1.2c0,0.7-0.5,1.1-1.1,1.1l1.2,1.7H101.5z
 | 
			
		||||
						 M101.6,163.4c0-0.5-0.4-0.9-0.9-0.9h-1.1v1.7h1.1C101.2,164.3,101.6,163.9,101.6,163.4z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M102.8,164.2c0-1.2,0.8-2.1,1.9-2.1c1.2,0,1.9,0.9,1.9,2.1c0,1.2-0.8,2.1-1.9,2.1
 | 
			
		||||
						C103.6,166.3,102.8,165.4,102.8,164.2z M106.3,164.2c0-1-0.6-1.7-1.6-1.7c-1,0-1.6,0.7-1.6,1.7c0,1,0.6,1.7,1.6,1.7
 | 
			
		||||
						C105.7,166,106.3,165.2,106.3,164.2z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M106.9,165.8l0.2-0.3c0.2,0.2,0.4,0.4,0.8,0.4c0.5,0,0.9-0.4,0.9-0.9v-2.8h0.3v2.8c0,0.8-0.5,1.2-1.2,1.2
 | 
			
		||||
						C107.5,166.3,107.2,166.1,106.9,165.8z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M110.4,166.2v-4h2.5v0.3h-2.2v1.5h2.1v0.3h-2.1v1.6h2.2v0.3H110.4z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M113.5,164.2c0-1.2,0.9-2.1,2-2.1c0.6,0,1.1,0.3,1.5,0.7l-0.3,0.2c-0.3-0.3-0.7-0.6-1.2-0.6
 | 
			
		||||
						c-0.9,0-1.7,0.7-1.7,1.7c0,1,0.7,1.7,1.7,1.7c0.5,0,0.9-0.2,1.2-0.6l0.3,0.2c-0.4,0.4-0.8,0.7-1.5,0.7
 | 
			
		||||
						C114.4,166.3,113.5,165.5,113.5,164.2z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st1" d="M118.7,166.2v-3.7h-1.3v-0.3h2.9v0.3H119v3.7H118.7z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
			</g>
 | 
			
		||||
			<g>
 | 
			
		||||
				<polygon class="st1" points="26.3,163.8 31.6,158.5 36.9,163.8 37.7,163.8 31.6,157.6 25.5,163.8 				"/>
 | 
			
		||||
				<polygon class="st1" points="36.9,164.7 31.6,170 26.3,164.7 25.5,164.7 31.6,170.8 37.7,164.7 				"/>
 | 
			
		||||
				<polygon class="st1" points="31,163.8 36.3,158.5 41.6,163.8 42.5,163.8 36.3,157.6 30.2,163.8 				"/>
 | 
			
		||||
				<polygon class="st1" points="41.6,164.7 36.3,170 31,164.7 30.2,164.7 36.3,170.8 42.5,164.7 				"/>
 | 
			
		||||
			</g>
 | 
			
		||||
		</g>
 | 
			
		||||
		<g>
 | 
			
		||||
			<path class="st1" d="M33.2,100.7c-4.6,0-8.3,3.7-8.3,8.3s3.7,8.3,8.3,8.3s8.3-3.7,8.3-8.3S37.8,100.7,33.2,100.7z"/>
 | 
			
		||||
		</g>
 | 
			
		||||
		<g>
 | 
			
		||||
			<g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st2" d="M33.2,35.2c40.7,0,73.8,33.1,73.8,73.8c0,0.7,0,1.4,0,2.1c0,1.7,0.6,3.3,1.7,4.6c1.2,1.2,2.8,1.9,4.5,2
 | 
			
		||||
						l0.2,0c3.5,0,6.3-2.7,6.4-6.2c0-0.8,0-1.7,0-2.5c0-47.7-38.8-86.6-86.6-86.6c-0.8,0-1.7,0-2.5,0c-1.7,0-3.3,0.8-4.5,2
 | 
			
		||||
						c-1.2,1.2-1.8,2.9-1.7,4.6c0.1,3.5,3,6.3,6.6,6.2C31.8,35.2,32.5,35.2,33.2,35.2z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
			</g>
 | 
			
		||||
		</g>
 | 
			
		||||
		<g>
 | 
			
		||||
			<g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st3" d="M33.2,60.5c26.7,0,48.5,21.7,48.5,48.5c0,0.6,0,1.3,0,2c-0.1,1.7,0.5,3.3,1.7,4.6c1.2,1.3,2.7,2,4.4,2.1
 | 
			
		||||
						c1.7,0.1,3.3-0.5,4.6-1.7c1.2-1.2,2-2.7,2-4.4c0-0.9,0.1-1.8,0.1-2.6c0-33.8-27.5-61.2-61.2-61.2c-0.8,0-1.6,0-2.6,0.1
 | 
			
		||||
						c-1.7,0.1-3.3,0.8-4.4,2.1c-1.2,1.3-1.8,2.9-1.7,4.6s0.8,3.3,2.1,4.4c1.3,1.2,2.9,1.8,4.6,1.7C31.9,60.5,32.6,60.5,33.2,60.5z"
 | 
			
		||||
						/>
 | 
			
		||||
				</g>
 | 
			
		||||
			</g>
 | 
			
		||||
		</g>
 | 
			
		||||
		<g>
 | 
			
		||||
			<g>
 | 
			
		||||
				<g>
 | 
			
		||||
					<path class="st4" d="M33.2,86.7c12.3,0,22.3,10,22.3,22.3c0,0.5,0,1.1-0.1,1.8c-0.3,3.5,2.3,6.6,5.8,6.9
 | 
			
		||||
						c3.5,0.3,6.6-2.3,6.9-5.8c0.1-1,0.1-1.9,0.1-2.8c0-19.3-15.7-35.1-35.1-35.1c-0.9,0-1.8,0-2.8,0.1c-1.7,0.1-3.2,0.9-4.3,2.2
 | 
			
		||||
						c-1.1,1.3-1.6,2.9-1.5,4.6c0.1,1.7,0.9,3.2,2.2,4.3c1.3,1.1,2.9,1.6,4.6,1.5C32.1,86.7,32.7,86.7,33.2,86.7z"/>
 | 
			
		||||
				</g>
 | 
			
		||||
			</g>
 | 
			
		||||
		</g>
 | 
			
		||||
	</g>
 | 
			
		||||
	<g>
 | 
			
		||||
		<path class="st1" d="M35.8,130.4c1.1,0.6,2.1,1.5,2.7,2.6c0.7,1.1,1,2.3,1,3.7s-0.3,2.6-1,3.7c-0.7,1.1-1.6,2-2.7,2.6
 | 
			
		||||
			c-1.1,0.6-2.4,1-3.8,1s-2.7-0.3-3.8-1c-1.1-0.6-2.1-1.5-2.7-2.6c-0.7-1.1-1-2.3-1-3.7c0-1.3,0.3-2.6,1-3.7c0.7-1.1,1.6-2,2.7-2.6
 | 
			
		||||
			c1.1-0.6,2.4-0.9,3.8-0.9C33.4,129.5,34.7,129.8,35.8,130.4z M29.9,132.9c-0.7,0.4-1.2,0.9-1.6,1.6s-0.6,1.4-0.6,2.2
 | 
			
		||||
			c0,0.8,0.2,1.6,0.6,2.3c0.4,0.7,0.9,1.2,1.6,1.6c0.7,0.4,1.4,0.6,2.1,0.6c0.8,0,1.5-0.2,2.1-0.6c0.6-0.4,1.2-0.9,1.5-1.6
 | 
			
		||||
			c0.4-0.7,0.6-1.4,0.6-2.3c0-0.8-0.2-1.6-0.6-2.2s-0.9-1.2-1.5-1.6c-0.6-0.4-1.4-0.6-2.1-0.6C31.3,132.3,30.6,132.5,29.9,132.9z"/>
 | 
			
		||||
		<path class="st1" d="M50.6,133.6c0.8,0.5,1.4,1.1,1.8,2c0.4,0.8,0.6,1.8,0.6,2.9c0,1.1-0.2,2-0.6,2.8c-0.4,0.8-1,1.5-1.8,1.9
 | 
			
		||||
			c-0.8,0.5-1.6,0.7-2.6,0.7c-0.7,0-1.4-0.1-2-0.4s-1.1-0.7-1.5-1.2v5.4h-3.1V133h3.1v1.6c0.4-0.5,0.9-1,1.4-1.2s1.2-0.4,2-0.4
 | 
			
		||||
			C48.9,132.9,49.8,133.1,50.6,133.6z M49.1,140.5c0.5-0.6,0.7-1.3,0.7-2.2c0-0.9-0.2-1.6-0.7-2.1c-0.5-0.6-1.1-0.8-1.9-0.8
 | 
			
		||||
			s-1.4,0.3-1.9,0.8c-0.5,0.6-0.8,1.3-0.8,2.1c0,0.9,0.2,1.6,0.8,2.2s1.1,0.8,1.9,0.8S48.6,141,49.1,140.5z"/>
 | 
			
		||||
		<path class="st1" d="M63.4,134.4c0.9,1,1.4,2.4,1.4,4.2c0,0.3,0,0.6,0,0.7H57c0.2,0.7,0.5,1.2,1,1.6c0.5,0.4,1.1,0.6,1.8,0.6
 | 
			
		||||
			c0.5,0,1-0.1,1.5-0.3s0.9-0.5,1.3-0.9l1.6,1.6c-0.5,0.6-1.2,1.1-2,1.4c-0.8,0.3-1.6,0.5-2.6,0.5c-1.1,0-2.1-0.2-3-0.7
 | 
			
		||||
			s-1.5-1.1-2-1.9c-0.5-0.8-0.7-1.8-0.7-2.9c0-1.1,0.2-2.1,0.7-2.9s1.1-1.5,2-1.9c0.8-0.5,1.8-0.7,2.9-0.7
 | 
			
		||||
			C61.2,132.9,62.5,133.4,63.4,134.4z M61.8,137.5c0-0.7-0.3-1.3-0.7-1.7s-1-0.6-1.7-0.6c-0.7,0-1.2,0.2-1.7,0.6
 | 
			
		||||
			c-0.4,0.4-0.7,1-0.9,1.7H61.8z"/>
 | 
			
		||||
		<path class="st1" d="M76.2,134c0.7,0.7,1.1,1.7,1.1,3v6.8h-3.1v-5.9c0-0.7-0.2-1.2-0.6-1.6s-0.9-0.6-1.5-0.6
 | 
			
		||||
			c-0.8,0-1.4,0.3-1.8,0.8c-0.4,0.5-0.7,1.2-0.7,2v5.3h-3.1V133h3.1v1.9c0.7-1.3,2-2,3.7-2C74.6,132.8,75.5,133.2,76.2,134z"/>
 | 
			
		||||
		<path class="st1" d="M96,129.7h3.3l-4.7,14h-3.3l-2.9-10.1l-3,10.1h-3.2l-4.7-14h3.4l3,10.7l3-10.7H90l3.1,10.7L96,129.7z"/>
 | 
			
		||||
		<path class="st1" d="M103.3,128.7c0.3,0.3,0.5,0.7,0.5,1.2s-0.2,0.9-0.5,1.2c-0.3,0.3-0.7,0.5-1.2,0.5c-0.5,0-0.9-0.2-1.2-0.5
 | 
			
		||||
			c-0.3-0.3-0.5-0.7-0.5-1.2c0-0.5,0.2-0.9,0.5-1.2c0.3-0.3,0.7-0.5,1.2-0.5C102.6,128.2,103,128.3,103.3,128.7z M100.6,133h3.1
 | 
			
		||||
			v10.8h-3.1V133z"/>
 | 
			
		||||
		<path class="st1" d="M106.5,129.7h10.1l0,2.6h-6.9v3.4h6.3v2.6h-6.3v5.3h-3.2V129.7z"/>
 | 
			
		||||
		<path class="st1" d="M120.9,128.7c0.3,0.3,0.5,0.7,0.5,1.2s-0.2,0.9-0.5,1.2c-0.3,0.3-0.7,0.5-1.2,0.5c-0.5,0-0.9-0.2-1.2-0.5
 | 
			
		||||
			c-0.3-0.3-0.5-0.7-0.5-1.2c0-0.5,0.2-0.9,0.5-1.2c0.3-0.3,0.7-0.5,1.2-0.5C120.1,128.2,120.5,128.3,120.9,128.7z M118.1,133h3.1
 | 
			
		||||
			v10.8h-3.1V133z"/>
 | 
			
		||||
	</g>
 | 
			
		||||
</g>
 | 
			
		||||
<g>
 | 
			
		||||
</g>
 | 
			
		||||
<g>
 | 
			
		||||
</g>
 | 
			
		||||
<g>
 | 
			
		||||
</g>
 | 
			
		||||
<g>
 | 
			
		||||
</g>
 | 
			
		||||
<g>
 | 
			
		||||
</g>
 | 
			
		||||
<g>
 | 
			
		||||
</g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 8.0 KiB  | 
@@ -71,7 +71,8 @@ components:
 | 
			
		||||
                  - 11    # BAD_MFA_TRANSACTION
 | 
			
		||||
                  - 12    # MFA_FAILURE
 | 
			
		||||
                  - 13    # SECURITY_SERVICE_UNREACHABLE
 | 
			
		||||
                  - 14    # CANNOT REFRESH TOKEN
 | 
			
		||||
                  - 14    # CANNOT_REFRESH_TOKEN
 | 
			
		||||
                  - 15    # ACCOUNT_SUSPENDED
 | 
			
		||||
              ErrorDetails:
 | 
			
		||||
                type: string
 | 
			
		||||
              ErrorDescription:
 | 
			
		||||
@@ -242,7 +243,7 @@ components:
 | 
			
		||||
        apiKey:
 | 
			
		||||
          type: string
 | 
			
		||||
        rights:
 | 
			
		||||
          $ref: '#/components/schemas/ApiKeyAccessRights'
 | 
			
		||||
          $ref: '#/components/schemas/ApiKeyAccessRightList'
 | 
			
		||||
 | 
			
		||||
    AclTemplate:
 | 
			
		||||
      type: object
 | 
			
		||||
@@ -492,6 +493,16 @@ components:
 | 
			
		||||
        sms:
 | 
			
		||||
          type: string
 | 
			
		||||
 | 
			
		||||
    ExtraSystemConfiguration:
 | 
			
		||||
      type: array
 | 
			
		||||
      items:
 | 
			
		||||
        type: object
 | 
			
		||||
        properties:
 | 
			
		||||
          parameterName:
 | 
			
		||||
            type: string
 | 
			
		||||
          parameterValue:
 | 
			
		||||
            type: string
 | 
			
		||||
 | 
			
		||||
    #########################################################################################
 | 
			
		||||
    ##
 | 
			
		||||
    ## These are endpoints that all services in the uCentral stack must provide
 | 
			
		||||
@@ -554,12 +565,6 @@ components:
 | 
			
		||||
            - $ref: '#/components/schemas/StringList'
 | 
			
		||||
            - $ref: '#/components/schemas/TagValuePairList'
 | 
			
		||||
 | 
			
		||||
    SystemCommandResults:
 | 
			
		||||
      type: object
 | 
			
		||||
      oneOf:
 | 
			
		||||
        - $ref: '#/components/schemas/StringList'
 | 
			
		||||
        - $ref: '#/components/schemas/TagValuePairList'
 | 
			
		||||
 | 
			
		||||
    SystemInfoResults:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
@@ -588,6 +593,33 @@ components:
 | 
			
		||||
                type: integer
 | 
			
		||||
                format: int64
 | 
			
		||||
 | 
			
		||||
    SystemResources:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        numberOfFileDescriptors:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        currRealMem:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        peakRealMem:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        currVirtMem:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        peakVirtMem:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
 | 
			
		||||
    SystemCommandResults:
 | 
			
		||||
      type: object
 | 
			
		||||
      oneOf:
 | 
			
		||||
        - $ref: '#/components/schemas/SystemResources'
 | 
			
		||||
        - $ref: '#/components/schemas/SystemInfoResults'
 | 
			
		||||
        - $ref: '#/components/schemas/StringList'
 | 
			
		||||
        - $ref: '#/components/schemas/TagValuePairList'
 | 
			
		||||
 | 
			
		||||
    ProfileAction:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
@@ -752,6 +784,23 @@ components:
 | 
			
		||||
              value:
 | 
			
		||||
                type: string
 | 
			
		||||
 | 
			
		||||
    SystemSecretEntry:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        name:
 | 
			
		||||
          type: string
 | 
			
		||||
        value:
 | 
			
		||||
          type: string
 | 
			
		||||
 | 
			
		||||
    SystemSecretEntryList:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        secrets:
 | 
			
		||||
          type: array
 | 
			
		||||
          items:
 | 
			
		||||
            $ref: '#/components/schemas/SystemSecretEntry'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #########################################################################################
 | 
			
		||||
    ##
 | 
			
		||||
    ## End of uCentral system wide values
 | 
			
		||||
@@ -1082,6 +1131,12 @@ paths:
 | 
			
		||||
            type: string
 | 
			
		||||
            format: uuid
 | 
			
		||||
          required: true
 | 
			
		||||
        - in: query
 | 
			
		||||
          description: When used, signifies the the id is actually the email address of the user, and not its uuid
 | 
			
		||||
          name: byEmail
 | 
			
		||||
          schema:
 | 
			
		||||
            type: boolean
 | 
			
		||||
          required: false
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/schemas/UserInfo'
 | 
			
		||||
@@ -1198,6 +1253,12 @@ paths:
 | 
			
		||||
            type: string
 | 
			
		||||
            format: uuid
 | 
			
		||||
          required: true
 | 
			
		||||
        - in: query
 | 
			
		||||
          description: When used, signifies the the id is actually the email address of the user, and not its uuid
 | 
			
		||||
          name: byEmail
 | 
			
		||||
          schema:
 | 
			
		||||
            type: boolean
 | 
			
		||||
          required: false
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/schemas/UserInfo'
 | 
			
		||||
@@ -1821,19 +1882,6 @@ paths:
 | 
			
		||||
  #########################################################################################
 | 
			
		||||
  ##  The following calls are restricted to the private system side APIs
 | 
			
		||||
  #########################################################################################
 | 
			
		||||
  /systemServices:
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Security
 | 
			
		||||
      summary: Retrieve the basic system information. This information is used between services only.
 | 
			
		||||
      operationId: getSystemServices
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/schemas/InternalSystemServices'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /validateToken:
 | 
			
		||||
    get:
 | 
			
		||||
@@ -1940,21 +1988,167 @@ paths:
 | 
			
		||||
            type: string
 | 
			
		||||
            enum:
 | 
			
		||||
              - info
 | 
			
		||||
              - extraConfiguration
 | 
			
		||||
              - resources
 | 
			
		||||
          required: true
 | 
			
		||||
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          description: Successful command execution
 | 
			
		||||
          content:
 | 
			
		||||
            application/json:
 | 
			
		||||
              schema:
 | 
			
		||||
                oneOf:
 | 
			
		||||
                  - $ref: '#/components/schemas/SystemInfoResults'
 | 
			
		||||
          $ref: '#/components/schemas/SystemCommandResults'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /systemSecret/{secret}:
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
        - System Secrets
 | 
			
		||||
      description: Retrieve a specific secret
 | 
			
		||||
      operationId: getSecret
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: secret
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
        - in: query
 | 
			
		||||
          name: all
 | 
			
		||||
          schema:
 | 
			
		||||
            type: boolean
 | 
			
		||||
          required: false
 | 
			
		||||
        - in: query
 | 
			
		||||
          name: dictionary
 | 
			
		||||
          schema:
 | 
			
		||||
            type: boolean
 | 
			
		||||
          required: false
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          description: Successfull retrieval
 | 
			
		||||
          content:
 | 
			
		||||
            application/json:
 | 
			
		||||
              schema:
 | 
			
		||||
                oneOf:
 | 
			
		||||
                  - type: object
 | 
			
		||||
                    properties:
 | 
			
		||||
                      knownKeys:
 | 
			
		||||
                        type: array
 | 
			
		||||
                        items:
 | 
			
		||||
                          type: object
 | 
			
		||||
                          properties:
 | 
			
		||||
                            key:
 | 
			
		||||
                              type: string
 | 
			
		||||
                            helper:
 | 
			
		||||
                              type: string
 | 
			
		||||
                  - $ref: '#/components/schemas/SystemSecretEntry'
 | 
			
		||||
                  - $ref: '#/components/schemas/SystemSecretEntryList'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
    put:
 | 
			
		||||
      tags:
 | 
			
		||||
        - System Secrets
 | 
			
		||||
      description: Modify a specific secret
 | 
			
		||||
      operationId: modifySecret
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: secret
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
        - in: query
 | 
			
		||||
          name: value
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/schemas/SystemSecretEntry'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
    delete:
 | 
			
		||||
      description: Remove a specific secret
 | 
			
		||||
      operationId: deleteSecret
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: secret
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/responses/Success'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /systemConfiguration:
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
        - SystemConfiguration
 | 
			
		||||
      summary: Retrieve system configuration items
 | 
			
		||||
      operationId: getSystemConfiguration
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: query
 | 
			
		||||
          description: Which parameters you want to retrieve
 | 
			
		||||
          name: entries
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
            example:
 | 
			
		||||
              - element1
 | 
			
		||||
              - element1,element2,element3
 | 
			
		||||
          required: false
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          description: List of configuration elements
 | 
			
		||||
          content:
 | 
			
		||||
            application/json:
 | 
			
		||||
              schema:
 | 
			
		||||
                type: array
 | 
			
		||||
                items:
 | 
			
		||||
                  $ref: '#/components/schemas/ExtraSystemConfiguration'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
    put:
 | 
			
		||||
      tags:
 | 
			
		||||
        - SystemConfiguration
 | 
			
		||||
      summary: Set some or all system configuration
 | 
			
		||||
      operationId: setSystemConfiguration
 | 
			
		||||
      requestBody:
 | 
			
		||||
        content:
 | 
			
		||||
          application/json:
 | 
			
		||||
            schema:
 | 
			
		||||
              $ref: '#/components/schemas/ExtraSystemConfiguration'
 | 
			
		||||
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/schemas/ExtraSystemConfiguration'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
    delete:
 | 
			
		||||
      tags:
 | 
			
		||||
        - SystemConfiguration
 | 
			
		||||
      summary: Delete all additional system configuration
 | 
			
		||||
      operationId: deleteSystemConfiguration
 | 
			
		||||
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/responses/Success'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
#########################################################################################
 | 
			
		||||
##
 | 
			
		||||
 
 | 
			
		||||
@@ -34,8 +34,8 @@ authentication.default.username = tip@ucentral.com
 | 
			
		||||
authentication.default.password = 13268b7daa751240369d125e79c873bd8dd3bef7981bdfd38ea03dbb1fbe7dcf
 | 
			
		||||
openwifi.system.data = $OWSEC_ROOT/data
 | 
			
		||||
openwifi.system.uri.private = https://localhost:17001
 | 
			
		||||
openwifi.system.uri.public = https://local.dpaas.arilia.com:16001
 | 
			
		||||
openwifi.system.uri.ui = https://ucentral-ui.arilia.com
 | 
			
		||||
openwifi.system.uri.public = https://main.server.com:16001
 | 
			
		||||
openwifi.system.uri.ui = https://ucentral-ui.main.server.com
 | 
			
		||||
openwifi.security.restapi.disable = false
 | 
			
		||||
openwifi.system.commandchannel = /tmp/app.ucentralsec
 | 
			
		||||
openwifi.service.key = $OWSEC_ROOT/certs/restapi-key.pem
 | 
			
		||||
@@ -64,9 +64,19 @@ mailer.loginmethod = login
 | 
			
		||||
mailer.port = 587
 | 
			
		||||
mailer.templates = $OWSEC_ROOT/templates
 | 
			
		||||
 | 
			
		||||
helper.user.email = openwifi@telecominfraproject.com
 | 
			
		||||
helper.sub.email = openwifi@telecominfraproject.com
 | 
			
		||||
helper.user.global.email = openwifi@telecominfraproject.com
 | 
			
		||||
helper.sub.global.email = openwifi@telecominfraproject.com
 | 
			
		||||
helper.user.site = https://openwifi.telecominfraproject.com
 | 
			
		||||
helper.sub.site = https://openwifi.telecominfraproject.com
 | 
			
		||||
helper.user.login = https://openwifi.telecominfraproject.com
 | 
			
		||||
helper.sub.login = https://openwifi.telecominfraproject.com
 | 
			
		||||
helper.user.signature = Telecom Infra Project
 | 
			
		||||
helper.sub.signature = Telecom Infra Project
 | 
			
		||||
 | 
			
		||||
#############################
 | 
			
		||||
# Generic information for all micro services
 | 
			
		||||
# Generic information for all micro-services
 | 
			
		||||
#############################
 | 
			
		||||
#
 | 
			
		||||
# NLB Support
 | 
			
		||||
@@ -80,7 +90,7 @@ alb.port = 16101
 | 
			
		||||
openwifi.kafka.group.id = security
 | 
			
		||||
openwifi.kafka.client.id = security1
 | 
			
		||||
openwifi.kafka.enable = true
 | 
			
		||||
openwifi.kafka.brokerlist = a1.arilia.com:9092
 | 
			
		||||
openwifi.kafka.brokerlist = kafka:9092
 | 
			
		||||
openwifi.kafka.auto.commit = false
 | 
			
		||||
openwifi.kafka.queue.buffering.max.ms = 50
 | 
			
		||||
openwifi.kafka.ssl.ca.location =
 | 
			
		||||
@@ -110,18 +120,18 @@ storage.type.sqlite.maxsessions = 128
 | 
			
		||||
storage.type.postgresql.maxsessions = 64
 | 
			
		||||
storage.type.postgresql.idletime = 60
 | 
			
		||||
storage.type.postgresql.host = localhost
 | 
			
		||||
storage.type.postgresql.username = stephb
 | 
			
		||||
storage.type.postgresql.password = snoopy99
 | 
			
		||||
storage.type.postgresql.database = ucentral
 | 
			
		||||
storage.type.postgresql.username = owsec
 | 
			
		||||
storage.type.postgresql.password = owsec
 | 
			
		||||
storage.type.postgresql.database = owsec
 | 
			
		||||
storage.type.postgresql.port = 5432
 | 
			
		||||
storage.type.postgresql.connectiontimeout = 60
 | 
			
		||||
 | 
			
		||||
storage.type.mysql.maxsessions = 64
 | 
			
		||||
storage.type.mysql.idletime = 60
 | 
			
		||||
storage.type.mysql.host = localhost
 | 
			
		||||
storage.type.mysql.username = stephb
 | 
			
		||||
storage.type.mysql.password = snoopy99
 | 
			
		||||
storage.type.mysql.database = ucentral
 | 
			
		||||
storage.type.mysql.username = owsec
 | 
			
		||||
storage.type.mysql.password = owsec
 | 
			
		||||
storage.type.mysql.database = owsec
 | 
			
		||||
storage.type.mysql.port = 3306
 | 
			
		||||
storage.type.mysql.connectiontimeout = 60
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,16 @@ mailer.loginmethod = login
 | 
			
		||||
mailer.port = ${MAILER_PORT}
 | 
			
		||||
mailer.templates = ${MAILER_TEMPLATES}
 | 
			
		||||
 | 
			
		||||
helper.user.email = ${USER_HELPER_EMAIL}
 | 
			
		||||
helper.sub.email = ${SUB_HELPER_EMAIL}
 | 
			
		||||
helper.user.global.email = ${GLOBAL_USER_HELPER_EMAIL}
 | 
			
		||||
helper.sub.global.email = ${GLOBAL_SUB_HELPER_EMAIL}
 | 
			
		||||
helper.user.site = ${USER_HELPER_SITE}
 | 
			
		||||
helper.sub.site = ${SUB_HELPER_SITE}
 | 
			
		||||
helper.user.login = ${USER_SYSTEM_LOGIN}
 | 
			
		||||
helper.sub.login = ${SUB_SYSTEM_LOGIN}
 | 
			
		||||
helper.user.signature = ${USER_SIGNATURE}
 | 
			
		||||
helper.sub.signature = ${SUB_SIGNATURE}
 | 
			
		||||
 | 
			
		||||
#############################
 | 
			
		||||
# Generic information for all micro services
 | 
			
		||||
 
 | 
			
		||||
@@ -11,31 +11,26 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	class ACLProcessor {
 | 
			
		||||
	  public:
 | 
			
		||||
        enum ACL_OPS {
 | 
			
		||||
            READ,
 | 
			
		||||
            MODIFY,
 | 
			
		||||
            DELETE,
 | 
			
		||||
            CREATE
 | 
			
		||||
        };
 | 
			
		||||
/*
 | 
			
		||||
		enum ACL_OPS { READ, MODIFY, DELETE, CREATE };
 | 
			
		||||
		/*
 | 
			
		||||
		 *  0) You can only delete yourself if you are a subscriber
 | 
			
		||||
			1) You cannot delete yourself
 | 
			
		||||
			2) If you are root, you can do anything.
 | 
			
		||||
			3) You can do anything to yourself
 | 
			
		||||
    4) Nobody can touch a root, unless they are a root, unless it is to get information on a ROOT
 | 
			
		||||
    5) Creation rules:
 | 
			
		||||
        ROOT -> create anything
 | 
			
		||||
        PARTNER -> (multi-tenant owner) admin,subs,csr,installer,noc,accounting - matches to an entity in provisioning
 | 
			
		||||
        ADMIN -> admin-subs-csr-installer-noc-accounting
 | 
			
		||||
        ACCOUNTING -> subs-installer-csr
 | 
			
		||||
			4) Nobody can touch a root, unless they are a root, unless it is to get information on a
 | 
			
		||||
		 ROOT 5) Creation rules: ROOT -> create anything PARTNER -> (multi-tenant owner)
 | 
			
		||||
		 admin,subs,csr,installer,noc,accounting - matches to an entity in provisioning ADMIN ->
 | 
			
		||||
		 admin-subs-csr-installer-noc-accounting ACCOUNTING -> subs-installer-csr
 | 
			
		||||
 | 
			
		||||
		 */
 | 
			
		||||
        static inline bool Can( const SecurityObjects::UserInfo & User, const SecurityObjects::UserInfo & Target, ACL_OPS Op) {
 | 
			
		||||
		static inline bool Can(const SecurityObjects::UserInfo &User,
 | 
			
		||||
							   const SecurityObjects::UserInfo &Target, ACL_OPS Op) {
 | 
			
		||||
 | 
			
		||||
            switch(Op) {
 | 
			
		||||
			switch (Op) {
 | 
			
		||||
			case DELETE: {
 | 
			
		||||
                    //  can a user delete themselves - yes - only if not root. We do not want a system to end up rootless
 | 
			
		||||
                    if(User.id==Target.id) {
 | 
			
		||||
				//  can a user delete themselves - yes - only if not root. We do not want a system
 | 
			
		||||
				//  to end up rootless
 | 
			
		||||
				if (User.id == Target.id) {
 | 
			
		||||
					return User.userRole != SecurityObjects::ROOT;
 | 
			
		||||
				}
 | 
			
		||||
				//  Root can delete anyone
 | 
			
		||||
@@ -43,97 +38,95 @@ namespace OpenWifi {
 | 
			
		||||
				case SecurityObjects::ROOT:
 | 
			
		||||
					return true;
 | 
			
		||||
				case SecurityObjects::ADMIN:
 | 
			
		||||
                            return Target.userRole!=SecurityObjects::ROOT && Target.userRole!=SecurityObjects::PARTNER;
 | 
			
		||||
					return Target.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
						   Target.userRole != SecurityObjects::PARTNER;
 | 
			
		||||
				case SecurityObjects::SUBSCRIBER:
 | 
			
		||||
                            return User.id==Target.id;
 | 
			
		||||
					return User.id == Target.id;
 | 
			
		||||
				case SecurityObjects::CSR:
 | 
			
		||||
					return false;
 | 
			
		||||
				case SecurityObjects::SYSTEM:
 | 
			
		||||
                            return Target.userRole!=SecurityObjects::ROOT && Target.userRole!=SecurityObjects::PARTNER;
 | 
			
		||||
					return Target.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
						   Target.userRole != SecurityObjects::PARTNER;
 | 
			
		||||
				case SecurityObjects::INSTALLER:
 | 
			
		||||
                            return User.id==Target.id;
 | 
			
		||||
					return User.id == Target.id;
 | 
			
		||||
				case SecurityObjects::NOC:
 | 
			
		||||
                            return Target.userRole==SecurityObjects::NOC;
 | 
			
		||||
					return Target.userRole == SecurityObjects::NOC;
 | 
			
		||||
				case SecurityObjects::ACCOUNTING:
 | 
			
		||||
                            return Target.userRole==SecurityObjects::ACCOUNTING;
 | 
			
		||||
					return Target.userRole == SecurityObjects::ACCOUNTING;
 | 
			
		||||
				case SecurityObjects::PARTNER:
 | 
			
		||||
                            return Target.userRole!=SecurityObjects::ROOT;
 | 
			
		||||
					return Target.userRole != SecurityObjects::ROOT;
 | 
			
		||||
				default:
 | 
			
		||||
					return false;
 | 
			
		||||
				}
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
			} break;
 | 
			
		||||
 | 
			
		||||
			case READ: {
 | 
			
		||||
				return User.userRole == SecurityObjects::ROOT ||
 | 
			
		||||
					   User.userRole == SecurityObjects::ADMIN ||
 | 
			
		||||
					   User.userRole == SecurityObjects::PARTNER;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
			} break;
 | 
			
		||||
 | 
			
		||||
			case CREATE: {
 | 
			
		||||
                    switch(User.userRole) {
 | 
			
		||||
				switch (User.userRole) {
 | 
			
		||||
				case SecurityObjects::ROOT:
 | 
			
		||||
					return true;
 | 
			
		||||
				case SecurityObjects::ADMIN:
 | 
			
		||||
                            return  Target.userRole!=SecurityObjects::ROOT &&
 | 
			
		||||
                                    Target.userRole!=SecurityObjects::PARTNER;
 | 
			
		||||
					return Target.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
						   Target.userRole != SecurityObjects::PARTNER;
 | 
			
		||||
				case SecurityObjects::SUBSCRIBER:
 | 
			
		||||
					return false;
 | 
			
		||||
				case SecurityObjects::CSR:
 | 
			
		||||
                            return Target.userRole==SecurityObjects::CSR;
 | 
			
		||||
					return Target.userRole == SecurityObjects::CSR;
 | 
			
		||||
				case SecurityObjects::SYSTEM:
 | 
			
		||||
                            return Target.userRole!=SecurityObjects::ROOT && Target.userRole!=SecurityObjects::PARTNER;
 | 
			
		||||
					return Target.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
						   Target.userRole != SecurityObjects::PARTNER;
 | 
			
		||||
				case SecurityObjects::INSTALLER:
 | 
			
		||||
                            return Target.userRole==SecurityObjects::INSTALLER;
 | 
			
		||||
					return Target.userRole == SecurityObjects::INSTALLER;
 | 
			
		||||
				case SecurityObjects::NOC:
 | 
			
		||||
                            return Target.userRole==SecurityObjects::NOC;
 | 
			
		||||
					return Target.userRole == SecurityObjects::NOC;
 | 
			
		||||
				case SecurityObjects::ACCOUNTING:
 | 
			
		||||
                            return Target.userRole==SecurityObjects::ACCOUNTING;
 | 
			
		||||
					return Target.userRole == SecurityObjects::ACCOUNTING;
 | 
			
		||||
				case SecurityObjects::PARTNER:
 | 
			
		||||
                            return Target.userRole!=SecurityObjects::ROOT;
 | 
			
		||||
					return Target.userRole != SecurityObjects::ROOT;
 | 
			
		||||
				default:
 | 
			
		||||
					return false;
 | 
			
		||||
				}
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
			} break;
 | 
			
		||||
 | 
			
		||||
			case MODIFY: {
 | 
			
		||||
                    switch(User.userRole) {
 | 
			
		||||
				switch (User.userRole) {
 | 
			
		||||
				case SecurityObjects::ROOT:
 | 
			
		||||
					return true;
 | 
			
		||||
				case SecurityObjects::ADMIN:
 | 
			
		||||
                            return  Target.userRole!=SecurityObjects::ROOT &&
 | 
			
		||||
                                    Target.userRole!=SecurityObjects::PARTNER;
 | 
			
		||||
					return Target.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
						   Target.userRole != SecurityObjects::PARTNER;
 | 
			
		||||
				case SecurityObjects::SUBSCRIBER:
 | 
			
		||||
                            return  User.id==Target.id;
 | 
			
		||||
					return User.id == Target.id;
 | 
			
		||||
				case SecurityObjects::CSR:
 | 
			
		||||
                            return  Target.userRole==SecurityObjects::CSR;
 | 
			
		||||
					return Target.userRole == SecurityObjects::CSR;
 | 
			
		||||
				case SecurityObjects::SYSTEM:
 | 
			
		||||
                            return  Target.userRole!=SecurityObjects::ROOT &&
 | 
			
		||||
                                    Target.userRole!=SecurityObjects::PARTNER;
 | 
			
		||||
					return Target.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
						   Target.userRole != SecurityObjects::PARTNER;
 | 
			
		||||
				case SecurityObjects::INSTALLER:
 | 
			
		||||
                            return  Target.userRole==SecurityObjects::INSTALLER;
 | 
			
		||||
					return Target.userRole == SecurityObjects::INSTALLER;
 | 
			
		||||
				case SecurityObjects::NOC:
 | 
			
		||||
                            return  Target.userRole==SecurityObjects::NOC;
 | 
			
		||||
					return Target.userRole == SecurityObjects::NOC;
 | 
			
		||||
				case SecurityObjects::ACCOUNTING:
 | 
			
		||||
                            return  Target.userRole==SecurityObjects::ACCOUNTING;
 | 
			
		||||
					return Target.userRole == SecurityObjects::ACCOUNTING;
 | 
			
		||||
				case SecurityObjects::PARTNER:
 | 
			
		||||
                            return  Target.userRole!=SecurityObjects::ROOT;
 | 
			
		||||
					return Target.userRole != SecurityObjects::ROOT;
 | 
			
		||||
				default:
 | 
			
		||||
					return false;
 | 
			
		||||
				}
 | 
			
		||||
			} break;
 | 
			
		||||
			default:
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    private:
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif //OWSEC_ACLPROCESSOR_H
 | 
			
		||||
#endif // OWSEC_ACLPROCESSOR_H
 | 
			
		||||
 
 | 
			
		||||
@@ -3,38 +3,41 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "ActionLinkManager.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
 | 
			
		||||
#include "MessagingTemplates.h"
 | 
			
		||||
#include "framework/utils.h"
 | 
			
		||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "fmt/format.h"
 | 
			
		||||
#include "framework/utils.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	int ActionLinkManager::Start() {
 | 
			
		||||
        poco_information(Logger(),"Starting...");
 | 
			
		||||
        if(!Running_)
 | 
			
		||||
		poco_information(Logger(), "Starting...");
 | 
			
		||||
		if (!Running_)
 | 
			
		||||
			Thr_.start(*this);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void ActionLinkManager::Stop() {
 | 
			
		||||
        poco_information(Logger(),"Stopping...");
 | 
			
		||||
        if(Running_) {
 | 
			
		||||
		poco_information(Logger(), "Stopping...");
 | 
			
		||||
		if (Running_) {
 | 
			
		||||
			Running_ = false;
 | 
			
		||||
			Thr_.wakeUp();
 | 
			
		||||
			Thr_.join();
 | 
			
		||||
		}
 | 
			
		||||
        poco_information(Logger(),"Stopped...");
 | 
			
		||||
		poco_information(Logger(), "Stopped...");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void ActionLinkManager::run() {
 | 
			
		||||
        Running_ = true ;
 | 
			
		||||
		Running_ = true;
 | 
			
		||||
		Utils::SetThreadName("action-mgr");
 | 
			
		||||
 | 
			
		||||
        while(Running_) {
 | 
			
		||||
        Poco::Thread::trySleep(10000);
 | 
			
		||||
 | 
			
		||||
		while (Running_) {
 | 
			
		||||
			Poco::Thread::trySleep(2000);
 | 
			
		||||
            if(!Running_)
 | 
			
		||||
			if (!Running_)
 | 
			
		||||
				break;
 | 
			
		||||
			std::vector<SecurityObjects::ActionLink> Links;
 | 
			
		||||
			{
 | 
			
		||||
@@ -42,80 +45,93 @@ namespace OpenWifi {
 | 
			
		||||
				StorageService()->ActionLinksDB().GetActions(Links);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            if(Links.empty())
 | 
			
		||||
			if (Links.empty())
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
            for(auto &i:Links) {
 | 
			
		||||
                if(!Running_)
 | 
			
		||||
			for (auto &i : Links) {
 | 
			
		||||
				if (!Running_)
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				SecurityObjects::UserInfo UInfo;
 | 
			
		||||
                if((i.action==OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD ||
 | 
			
		||||
                    i.action==OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL) && !StorageService()->UserDB().GetUserById(i.userId,UInfo)) {
 | 
			
		||||
				if ((i.action == OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD ||
 | 
			
		||||
					 i.action == OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL) &&
 | 
			
		||||
					!StorageService()->UserDB().GetUserById(i.userId, UInfo)) {
 | 
			
		||||
					StorageService()->ActionLinksDB().CancelAction(i.id);
 | 
			
		||||
					continue;
 | 
			
		||||
                } else if(( i.action==OpenWifi::SecurityObjects::LinkActions::SUB_FORGOT_PASSWORD ||
 | 
			
		||||
                            i.action==OpenWifi::SecurityObjects::LinkActions::SUB_VERIFY_EMAIL ||
 | 
			
		||||
                            i.action==OpenWifi::SecurityObjects::LinkActions::SUB_SIGNUP ) && !StorageService()->SubDB().GetUserById(i.userId,UInfo)) {
 | 
			
		||||
				} else if ((i.action ==
 | 
			
		||||
								OpenWifi::SecurityObjects::LinkActions::SUB_FORGOT_PASSWORD ||
 | 
			
		||||
							i.action == OpenWifi::SecurityObjects::LinkActions::SUB_VERIFY_EMAIL ||
 | 
			
		||||
							i.action == OpenWifi::SecurityObjects::LinkActions::SUB_SIGNUP) &&
 | 
			
		||||
						   !StorageService()->SubDB().GetUserById(i.userId, UInfo)) {
 | 
			
		||||
					StorageService()->ActionLinksDB().CancelAction(i.id);
 | 
			
		||||
					continue;
 | 
			
		||||
                } else if((i.action==OpenWifi::SecurityObjects::LinkActions::EMAIL_INVITATION) &&
 | 
			
		||||
                        (OpenWifi::Now()-i.created)>(24*60*60)) {
 | 
			
		||||
				} else if ((i.action == OpenWifi::SecurityObjects::LinkActions::EMAIL_INVITATION) &&
 | 
			
		||||
						   (OpenWifi::Now() - i.created) > (24 * 60 * 60)) {
 | 
			
		||||
					StorageService()->ActionLinksDB().CancelAction(i.id);
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
                switch(i.action) {
 | 
			
		||||
				switch (i.action) {
 | 
			
		||||
				case OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD: {
 | 
			
		||||
                            if(AuthService::SendEmailToUser(i.id, UInfo.email, MessagingTemplates::FORGOT_PASSWORD)) {
 | 
			
		||||
                                poco_information(Logger(),fmt::format("Send password reset link to {}",UInfo.email));
 | 
			
		||||
					if (AuthService()->SendEmailToUser(i.id, UInfo.email,
 | 
			
		||||
													   MessagingTemplates::FORGOT_PASSWORD)) {
 | 
			
		||||
						poco_information(
 | 
			
		||||
							Logger(), fmt::format("Send password reset link to {}", UInfo.email));
 | 
			
		||||
					}
 | 
			
		||||
					StorageService()->ActionLinksDB().SentAction(i.id);
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
				} break;
 | 
			
		||||
 | 
			
		||||
				case OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL: {
 | 
			
		||||
                            if(AuthService::SendEmailToUser(i.id, UInfo.email, MessagingTemplates::EMAIL_VERIFICATION)) {
 | 
			
		||||
                                poco_information(Logger(),fmt::format("Send email verification link to {}",UInfo.email));
 | 
			
		||||
					if (AuthService()->SendEmailToUser(i.id, UInfo.email,
 | 
			
		||||
													   MessagingTemplates::EMAIL_VERIFICATION)) {
 | 
			
		||||
						poco_information(Logger(), fmt::format("Send email verification link to {}",
 | 
			
		||||
															   UInfo.email));
 | 
			
		||||
					}
 | 
			
		||||
					StorageService()->ActionLinksDB().SentAction(i.id);
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
				} break;
 | 
			
		||||
 | 
			
		||||
				case OpenWifi::SecurityObjects::LinkActions::EMAIL_INVITATION: {
 | 
			
		||||
                            if(AuthService::SendEmailToUser(i.id, UInfo.email, MessagingTemplates::EMAIL_INVITATION)) {
 | 
			
		||||
                                poco_information(Logger(),fmt::format("Send new subscriber email invitation link to {}",UInfo.email));
 | 
			
		||||
					if (AuthService()->SendEmailToUser(i.id, UInfo.email,
 | 
			
		||||
													   MessagingTemplates::EMAIL_INVITATION)) {
 | 
			
		||||
						poco_information(
 | 
			
		||||
							Logger(), fmt::format("Send new subscriber email invitation link to {}",
 | 
			
		||||
												  UInfo.email));
 | 
			
		||||
					}
 | 
			
		||||
					StorageService()->ActionLinksDB().SentAction(i.id);
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
				} break;
 | 
			
		||||
 | 
			
		||||
				case OpenWifi::SecurityObjects::LinkActions::SUB_FORGOT_PASSWORD: {
 | 
			
		||||
                            auto Signup = Poco::StringTokenizer(UInfo.signingUp,":");
 | 
			
		||||
                            if(AuthService::SendEmailToSubUser(i.id, UInfo.email,MessagingTemplates::SUB_FORGOT_PASSWORD, Signup.count()==1 ? "" : Signup[0])) {
 | 
			
		||||
                                poco_information(Logger(),fmt::format("Send subscriber password reset link to {}",UInfo.email));
 | 
			
		||||
					if (AuthService()->SendEmailToSubUser(i.id, UInfo.email,
 | 
			
		||||
														  MessagingTemplates::SUB_FORGOT_PASSWORD,"")) {
 | 
			
		||||
						poco_information(
 | 
			
		||||
							Logger(),
 | 
			
		||||
							fmt::format("Send subscriber password reset link to {}", UInfo.email));
 | 
			
		||||
					}
 | 
			
		||||
					StorageService()->ActionLinksDB().SentAction(i.id);
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
				} break;
 | 
			
		||||
 | 
			
		||||
				case OpenWifi::SecurityObjects::LinkActions::SUB_VERIFY_EMAIL: {
 | 
			
		||||
                            auto Signup = Poco::StringTokenizer(UInfo.signingUp,":");
 | 
			
		||||
                            if(AuthService::SendEmailToSubUser(i.id, UInfo.email, MessagingTemplates::SUB_EMAIL_VERIFICATION, Signup.count()==1 ? "" : Signup[0])) {
 | 
			
		||||
                                poco_information(Logger(),fmt::format("Send subscriber email verification link to {}",UInfo.email));
 | 
			
		||||
					if (AuthService()->SendEmailToSubUser(
 | 
			
		||||
							i.id, UInfo.email, MessagingTemplates::SUB_EMAIL_VERIFICATION,"")) {
 | 
			
		||||
						poco_information(
 | 
			
		||||
							Logger(), fmt::format("Send subscriber email verification link to {}",
 | 
			
		||||
												  UInfo.email));
 | 
			
		||||
					}
 | 
			
		||||
					StorageService()->ActionLinksDB().SentAction(i.id);
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
				} break;
 | 
			
		||||
 | 
			
		||||
				case OpenWifi::SecurityObjects::LinkActions::SUB_SIGNUP: {
 | 
			
		||||
                        auto Signup = Poco::StringTokenizer(UInfo.signingUp,":");
 | 
			
		||||
                        if(AuthService::SendEmailToSubUser(i.id, UInfo.email, MessagingTemplates::SUB_SIGNUP_VERIFICATION, Signup.count()==1 ? "" : Signup[0])) {
 | 
			
		||||
                            poco_information(Logger(),fmt::format("Send new subscriber email verification link to {}",UInfo.email));
 | 
			
		||||
					auto Signup = Poco::StringTokenizer(UInfo.signingUp, ":");
 | 
			
		||||
					if (AuthService()->SendEmailToSubUser(
 | 
			
		||||
							i.id, UInfo.email, MessagingTemplates::SUB_SIGNUP_VERIFICATION,
 | 
			
		||||
							Signup.count() == 1 ? "" : Signup[0])) {
 | 
			
		||||
						poco_information(
 | 
			
		||||
							Logger(),
 | 
			
		||||
							fmt::format("Send new subscriber email verification link to {}",
 | 
			
		||||
										UInfo.email));
 | 
			
		||||
					}
 | 
			
		||||
					StorageService()->ActionLinksDB().SentAction(i.id);
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
				} break;
 | 
			
		||||
 | 
			
		||||
				default: {
 | 
			
		||||
					StorageService()->ActionLinksDB().SentAction(i.id);
 | 
			
		||||
@@ -125,4 +141,4 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -10,8 +10,7 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	class ActionLinkManager : public SubSystemServer, Poco::Runnable {
 | 
			
		||||
	  public:
 | 
			
		||||
 | 
			
		||||
        static ActionLinkManager * instance() {
 | 
			
		||||
		static ActionLinkManager *instance() {
 | 
			
		||||
			static auto instance_ = new ActionLinkManager;
 | 
			
		||||
			return instance_;
 | 
			
		||||
		}
 | 
			
		||||
@@ -24,11 +23,8 @@ namespace OpenWifi {
 | 
			
		||||
		Poco::Thread Thr_;
 | 
			
		||||
		std::atomic_bool Running_ = false;
 | 
			
		||||
 | 
			
		||||
        ActionLinkManager() noexcept:
 | 
			
		||||
            SubSystemServer("ActionLinkManager", "ACTION-SVR", "action.server")
 | 
			
		||||
                {
 | 
			
		||||
                }
 | 
			
		||||
		ActionLinkManager() noexcept
 | 
			
		||||
			: SubSystemServer("ActionLinkManager", "ACTION-SVR", "action.server") {}
 | 
			
		||||
	};
 | 
			
		||||
    inline ActionLinkManager * ActionLinkManager() { return ActionLinkManager::instance(); }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	inline ActionLinkManager *ActionLinkManager() { return ActionLinkManager::instance(); }
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -10,34 +10,29 @@
 | 
			
		||||
 | 
			
		||||
#include <regex>
 | 
			
		||||
 | 
			
		||||
#include "framework/SubSystemServer.h"
 | 
			
		||||
#include "Poco/Crypto/DigestEngine.h"
 | 
			
		||||
#include "Poco/ExpireLRUCache.h"
 | 
			
		||||
#include "Poco/HMACEngine.h"
 | 
			
		||||
#include "Poco/JSON/Object.h"
 | 
			
		||||
#include "Poco/JWT/Signer.h"
 | 
			
		||||
#include "Poco/Net/HTTPServerRequest.h"
 | 
			
		||||
#include "Poco/Net/HTTPServerResponse.h"
 | 
			
		||||
#include "Poco/JWT/Signer.h"
 | 
			
		||||
#include "Poco/SHA2Engine.h"
 | 
			
		||||
#include "Poco/Crypto/DigestEngine.h"
 | 
			
		||||
#include "Poco/HMACEngine.h"
 | 
			
		||||
#include "Poco/ExpireLRUCache.h"
 | 
			
		||||
#include "framework/SubSystemServer.h"
 | 
			
		||||
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
 | 
			
		||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
 | 
			
		||||
#include "MessagingTemplates.h"
 | 
			
		||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi{
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	static const std::string AUTHENTICATION_SYSTEM{"SYSTEM"};
 | 
			
		||||
 | 
			
		||||
	class AuthService : public SubSystemServer {
 | 
			
		||||
	  public:
 | 
			
		||||
 | 
			
		||||
        enum ACCESS_TYPE {
 | 
			
		||||
            USERNAME,
 | 
			
		||||
            SERVER,
 | 
			
		||||
            CUSTOM
 | 
			
		||||
        };
 | 
			
		||||
		enum ACCESS_TYPE { USERNAME, SERVER, CUSTOM };
 | 
			
		||||
 | 
			
		||||
		static ACCESS_TYPE IntToAccessType(int C);
 | 
			
		||||
		static int AccessTypeToInt(ACCESS_TYPE T);
 | 
			
		||||
@@ -50,57 +45,101 @@ namespace OpenWifi{
 | 
			
		||||
		int Start() override;
 | 
			
		||||
		void Stop() override;
 | 
			
		||||
 | 
			
		||||
        [[nodiscard]] bool IsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, std::uint64_t TID, bool & Expired);
 | 
			
		||||
        [[nodiscard]] bool IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, std::uint64_t TID, bool & Expired);
 | 
			
		||||
		[[nodiscard]] bool IsAuthorized(Poco::Net::HTTPServerRequest &Request,
 | 
			
		||||
										std::string &SessionToken,
 | 
			
		||||
										SecurityObjects::UserInfoAndPolicy &UInfo,
 | 
			
		||||
										std::uint64_t TID, bool &Expired);
 | 
			
		||||
		[[nodiscard]] bool IsAuthorized(const std::string &SessionToken,
 | 
			
		||||
										SecurityObjects::UserInfoAndPolicy &UInfo,
 | 
			
		||||
										std::uint64_t TID, bool &Expired);
 | 
			
		||||
 | 
			
		||||
        [[nodiscard]] UNAUTHORIZED_REASON Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired );
 | 
			
		||||
        void CreateToken(const std::string & UserName, SecurityObjects::UserInfoAndPolicy &UInfo);
 | 
			
		||||
        [[nodiscard]] bool SetPassword(const std::string &Password, SecurityObjects::UserInfo & UInfo);
 | 
			
		||||
        [[nodiscard]] const std:: string & PasswordValidationExpression() const { return PasswordValidationStr_;};
 | 
			
		||||
        void Logout(const std::string &token, bool EraseFromCache=true);
 | 
			
		||||
		[[nodiscard]] UNAUTHORIZED_REASON Authorize(std::string &UserName,
 | 
			
		||||
													const std::string &Password,
 | 
			
		||||
													const std::string &NewPassword,
 | 
			
		||||
													SecurityObjects::UserInfoAndPolicy &UInfo,
 | 
			
		||||
													bool &Expired);
 | 
			
		||||
		void CreateToken(const std::string &UserName, SecurityObjects::UserInfoAndPolicy &UInfo);
 | 
			
		||||
		[[nodiscard]] bool SetPassword(const std::string &Password,
 | 
			
		||||
									   SecurityObjects::UserInfo &UInfo);
 | 
			
		||||
		[[nodiscard]] const std::string &PasswordValidationExpression() const {
 | 
			
		||||
			return PasswordValidationStr_;
 | 
			
		||||
		};
 | 
			
		||||
		void Logout(const std::string &token, bool EraseFromCache = true);
 | 
			
		||||
 | 
			
		||||
        [[nodiscard]] bool IsSubAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, std::uint64_t TID, bool & Expired);
 | 
			
		||||
        [[nodiscard]] UNAUTHORIZED_REASON AuthorizeSub( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired );
 | 
			
		||||
		[[nodiscard]] bool IsSubAuthorized(Poco::Net::HTTPServerRequest &Request,
 | 
			
		||||
										   std::string &SessionToken,
 | 
			
		||||
										   SecurityObjects::UserInfoAndPolicy &UInfo,
 | 
			
		||||
										   std::uint64_t TID, bool &Expired);
 | 
			
		||||
		[[nodiscard]] UNAUTHORIZED_REASON AuthorizeSub(std::string &UserName,
 | 
			
		||||
													   const std::string &Password,
 | 
			
		||||
													   const std::string &NewPassword,
 | 
			
		||||
													   SecurityObjects::UserInfoAndPolicy &UInfo,
 | 
			
		||||
													   bool &Expired);
 | 
			
		||||
 | 
			
		||||
        void CreateSubToken(const std::string & UserName, SecurityObjects::UserInfoAndPolicy &UInfo);
 | 
			
		||||
        [[nodiscard]] bool SetSubPassword(const std::string &Password, SecurityObjects::UserInfo & UInfo);
 | 
			
		||||
        [[nodiscard]] const std:: string & SubPasswordValidationExpression() const { return PasswordValidationStr_;};
 | 
			
		||||
        void SubLogout(const std::string &token, bool EraseFromCache=true);
 | 
			
		||||
		void CreateSubToken(const std::string &UserName, SecurityObjects::UserInfoAndPolicy &UInfo);
 | 
			
		||||
		[[nodiscard]] bool SetSubPassword(const std::string &Password,
 | 
			
		||||
										  SecurityObjects::UserInfo &UInfo);
 | 
			
		||||
		[[nodiscard]] const std::string &SubPasswordValidationExpression() const {
 | 
			
		||||
			return PasswordValidationStr_;
 | 
			
		||||
		};
 | 
			
		||||
		void SubLogout(const std::string &token, bool EraseFromCache = true);
 | 
			
		||||
 | 
			
		||||
		void RemoveTokenSystemWide(const std::string &token);
 | 
			
		||||
 | 
			
		||||
		bool ValidatePassword(const std::string &pwd);
 | 
			
		||||
		bool ValidateSubPassword(const std::string &pwd);
 | 
			
		||||
 | 
			
		||||
        [[nodiscard]] bool IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo, bool & Expired);
 | 
			
		||||
        [[nodiscard]] bool IsValidSubToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo, bool & Expired);
 | 
			
		||||
        [[nodiscard]] std::string GenerateTokenJWT(const std::string & UserName, ACCESS_TYPE Type);
 | 
			
		||||
        [[nodiscard]] std::string GenerateTokenHMAC(const std::string & UserName, ACCESS_TYPE Type);
 | 
			
		||||
		[[nodiscard]] bool IsValidToken(const std::string &Token,
 | 
			
		||||
										SecurityObjects::WebToken &WebToken,
 | 
			
		||||
										SecurityObjects::UserInfo &UserInfo, bool &Expired);
 | 
			
		||||
		[[nodiscard]] bool IsValidSubToken(const std::string &Token,
 | 
			
		||||
										   SecurityObjects::WebToken &WebToken,
 | 
			
		||||
										   SecurityObjects::UserInfo &UserInfo, bool &Expired);
 | 
			
		||||
		[[nodiscard]] std::string GenerateTokenJWT(const std::string &UserName, ACCESS_TYPE Type);
 | 
			
		||||
		[[nodiscard]] std::string GenerateTokenHMAC(const std::string &UserName, ACCESS_TYPE Type);
 | 
			
		||||
 | 
			
		||||
        [[nodiscard]] bool IsValidApiKey(const std::string &ApiKey, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo, bool & Expired, std::uint64_t & expiresOn);
 | 
			
		||||
        [[nodiscard]] std::string ComputeNewPasswordHash(const std::string &UserName, const std::string &Password);
 | 
			
		||||
        [[nodiscard]] bool ValidatePasswordHash(const std::string & UserName, const std::string & Password, const std::string &StoredPassword);
 | 
			
		||||
        [[nodiscard]] bool ValidateSubPasswordHash(const std::string & UserName, const std::string & Password, const std::string &StoredPassword);
 | 
			
		||||
		[[nodiscard]] bool IsValidApiKey(const std::string &ApiKey,
 | 
			
		||||
										 SecurityObjects::WebToken &WebToken,
 | 
			
		||||
										 SecurityObjects::UserInfo &UserInfo, bool &Expired,
 | 
			
		||||
										 std::uint64_t &expiresOn, bool &Suspended);
 | 
			
		||||
		[[nodiscard]] std::string ComputeNewPasswordHash(const std::string &UserName,
 | 
			
		||||
														 const std::string &Password);
 | 
			
		||||
		[[nodiscard]] bool ValidatePasswordHash(const std::string &UserName,
 | 
			
		||||
												const std::string &Password,
 | 
			
		||||
												const std::string &StoredPassword);
 | 
			
		||||
		[[nodiscard]] bool ValidateSubPasswordHash(const std::string &UserName,
 | 
			
		||||
												   const std::string &Password,
 | 
			
		||||
												   const std::string &StoredPassword);
 | 
			
		||||
 | 
			
		||||
        [[nodiscard]] bool UpdatePassword(const std::string &Admin, const std::string &UserName, const std::string & OldPassword, const std::string &NewPassword);
 | 
			
		||||
        [[nodiscard]] std::string ResetPassword(const std::string &Admin, const std::string &UserName);
 | 
			
		||||
		[[nodiscard]] bool UpdatePassword(const std::string &Admin, const std::string &UserName,
 | 
			
		||||
										  const std::string &OldPassword,
 | 
			
		||||
										  const std::string &NewPassword);
 | 
			
		||||
		[[nodiscard]] std::string ResetPassword(const std::string &Admin,
 | 
			
		||||
												const std::string &UserName);
 | 
			
		||||
 | 
			
		||||
        [[nodiscard]] bool UpdateSubPassword(const std::string &Admin, const std::string &UserName, const std::string & OldPassword, const std::string &NewPassword);
 | 
			
		||||
        [[nodiscard]] std::string ResetSubPassword(const std::string &Admin, const std::string &UserName);
 | 
			
		||||
		[[nodiscard]] bool UpdateSubPassword(const std::string &Admin, const std::string &UserName,
 | 
			
		||||
											 const std::string &OldPassword,
 | 
			
		||||
											 const std::string &NewPassword);
 | 
			
		||||
		[[nodiscard]] std::string ResetSubPassword(const std::string &Admin,
 | 
			
		||||
												   const std::string &UserName);
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]] static bool VerifyEmail(SecurityObjects::UserInfo &UInfo);
 | 
			
		||||
		[[nodiscard]] static bool VerifySubEmail(SecurityObjects::UserInfo &UInfo);
 | 
			
		||||
 | 
			
		||||
        [[nodiscard]] static bool SendEmailToUser(const std::string &LinkId, std::string &Email, MessagingTemplates::EMAIL_REASON Reason);
 | 
			
		||||
        [[nodiscard]] static bool SendEmailToSubUser(const std::string &LinkId, std::string &Email, MessagingTemplates::EMAIL_REASON Reason, const std::string &OperatorName);
 | 
			
		||||
		[[nodiscard]] bool SendEmailToUser(const std::string &LinkId, std::string &Email,
 | 
			
		||||
										   MessagingTemplates::EMAIL_REASON Reason);
 | 
			
		||||
		[[nodiscard]] bool SendEmailToSubUser(const std::string &LinkId, std::string &Email,
 | 
			
		||||
											  MessagingTemplates::EMAIL_REASON Reason,
 | 
			
		||||
											  const std::string &OperatorName);
 | 
			
		||||
		[[nodiscard]] bool RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo);
 | 
			
		||||
 | 
			
		||||
        [[nodiscard]] bool SendEmailChallengeCode(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &code);
 | 
			
		||||
		[[nodiscard]] bool SendEmailChallengeCode(const SecurityObjects::UserInfoAndPolicy &UInfo,
 | 
			
		||||
												  const std::string &code);
 | 
			
		||||
 | 
			
		||||
		bool DeleteUserFromCache(const std::string &UserName);
 | 
			
		||||
		bool DeleteSubUserFromCache(const std::string &UserName);
 | 
			
		||||
        void RevokeToken(std::string & Token);
 | 
			
		||||
        void RevokeSubToken(std::string & Token);
 | 
			
		||||
		void RevokeToken(std::string &Token);
 | 
			
		||||
		void RevokeSubToken(std::string &Token);
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]] static inline const std::string GetLogoAssetURI() {
 | 
			
		||||
			return MicroServicePublicEndPoint() + "/wwwassets/logo.png";
 | 
			
		||||
@@ -118,14 +157,28 @@ namespace OpenWifi{
 | 
			
		||||
			return MicroServiceWWWAssetsDir() + "/sub_logo.png";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        inline const std::string & GetPasswordPolicy() const { return PasswordPolicy_; }
 | 
			
		||||
        inline const std::string & GetAccessPolicy() const { return AccessPolicy_; }
 | 
			
		||||
		inline const std::string &GetPasswordPolicy() const { return PasswordPolicy_; }
 | 
			
		||||
		inline const std::string &GetAccessPolicy() const { return AccessPolicy_; }
 | 
			
		||||
 | 
			
		||||
        inline const std::string & GetSubPasswordPolicy() const { return SubPasswordPolicy_; }
 | 
			
		||||
        inline const std::string & GetSubAccessPolicy() const { return SubAccessPolicy_; }
 | 
			
		||||
		inline const std::string &GetSubPasswordPolicy() const { return SubPasswordPolicy_; }
 | 
			
		||||
		inline const std::string &GetSubAccessPolicy() const { return SubAccessPolicy_; }
 | 
			
		||||
 | 
			
		||||
        bool RefreshUserToken(Poco::Net::HTTPServerRequest & Request, const std::string & RefreshToken, SecurityObjects::UserInfoAndPolicy & UI);
 | 
			
		||||
        bool RefreshSubToken(Poco::Net::HTTPServerRequest & Request, const std::string & RefreshToken, SecurityObjects::UserInfoAndPolicy & UI);
 | 
			
		||||
		bool RefreshUserToken(Poco::Net::HTTPServerRequest &Request,
 | 
			
		||||
							  const std::string &RefreshToken,
 | 
			
		||||
							  SecurityObjects::UserInfoAndPolicy &UI);
 | 
			
		||||
		bool RefreshSubToken(Poco::Net::HTTPServerRequest &Request, const std::string &RefreshToken,
 | 
			
		||||
							 SecurityObjects::UserInfoAndPolicy &UI);
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]] inline auto HelperEmail() const { return HelperEmail_; };
 | 
			
		||||
		[[nodiscard]] inline auto SubHelperEmail() const { return SubHelperEmail_; };
 | 
			
		||||
		[[nodiscard]] inline auto GlobalHelperEmail() const { return GlobalHelperEmail_; };
 | 
			
		||||
		[[nodiscard]] inline auto GlobalSubHelperEmail() const { return GlobalSubHelperEmail_; };
 | 
			
		||||
		[[nodiscard]] inline auto HelperSite() const { return HelperSite_; };
 | 
			
		||||
		[[nodiscard]] inline auto SubHelperSite() const { return SubHelperSite_; };
 | 
			
		||||
		[[nodiscard]] inline auto SystemLoginSite() const { return SystemLoginSite_; };
 | 
			
		||||
		[[nodiscard]] inline auto SubSystemLoginSite() const { return SubSystemLoginSite_; };
 | 
			
		||||
		[[nodiscard]] inline auto UserSignature() const { return UserSignature_; };
 | 
			
		||||
		[[nodiscard]] inline auto SubSignature() const { return SubSignature_; };
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
		Poco::SHA2Engine SHA2_;
 | 
			
		||||
@@ -140,41 +193,42 @@ namespace OpenWifi{
 | 
			
		||||
		std::regex SubPasswordValidation_;
 | 
			
		||||
 | 
			
		||||
		uint64_t TokenAging_ = 15 * 24 * 60 * 60;
 | 
			
		||||
        uint64_t            HowManyOldPassword_=5;
 | 
			
		||||
        uint64_t            RefreshTokenLifeSpan_ = 90 * 24 * 60 * 60 ;
 | 
			
		||||
		uint64_t HowManyOldPassword_ = 5;
 | 
			
		||||
		uint64_t RefreshTokenLifeSpan_ = 90 * 24 * 60 * 60;
 | 
			
		||||
 | 
			
		||||
        class SHA256Engine : public Poco::Crypto::DigestEngine
 | 
			
		||||
                {
 | 
			
		||||
		std::string HelperEmail_;
 | 
			
		||||
		std::string SubHelperEmail_;
 | 
			
		||||
		std::string GlobalHelperEmail_;
 | 
			
		||||
		std::string GlobalSubHelperEmail_;
 | 
			
		||||
		std::string HelperSite_;
 | 
			
		||||
		std::string SubHelperSite_;
 | 
			
		||||
		std::string SystemLoginSite_;
 | 
			
		||||
		std::string SubSystemLoginSite_;
 | 
			
		||||
		std::string UserSignature_;
 | 
			
		||||
		std::string SubSignature_;
 | 
			
		||||
 | 
			
		||||
		class SHA256Engine : public Poco::Crypto::DigestEngine {
 | 
			
		||||
		  public:
 | 
			
		||||
                    enum
 | 
			
		||||
                    {
 | 
			
		||||
                        BLOCK_SIZE = 64,
 | 
			
		||||
                        DIGEST_SIZE = 32
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    SHA256Engine()
 | 
			
		||||
                    : DigestEngine("SHA256")
 | 
			
		||||
                    {
 | 
			
		||||
                    }
 | 
			
		||||
			enum { BLOCK_SIZE = 64, DIGEST_SIZE = 32 };
 | 
			
		||||
 | 
			
		||||
			SHA256Engine() : DigestEngine("SHA256") {}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		Poco::HMACEngine<SHA256Engine> HMAC_{"tipopenwifi"};
 | 
			
		||||
 | 
			
		||||
        AuthService() noexcept:
 | 
			
		||||
            SubSystemServer("Authentication", "AUTH-SVR", "authentication")
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
		AuthService() noexcept : SubSystemServer("Authentication", "AUTH-SVR", "authentication") {}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	inline auto AuthService() { return AuthService::instance(); }
 | 
			
		||||
 | 
			
		||||
    [[nodiscard]] inline bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo , std::uint64_t TID, bool & Expired, bool Sub ) {
 | 
			
		||||
        if(Sub)
 | 
			
		||||
            return AuthService()->IsSubAuthorized(Request, SessionToken, UInfo, TID, Expired );
 | 
			
		||||
	[[nodiscard]] inline bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest &Request,
 | 
			
		||||
													  std::string &SessionToken,
 | 
			
		||||
													  SecurityObjects::UserInfoAndPolicy &UInfo,
 | 
			
		||||
													  std::uint64_t TID, bool &Expired, bool Sub) {
 | 
			
		||||
		if (Sub)
 | 
			
		||||
			return AuthService()->IsSubAuthorized(Request, SessionToken, UInfo, TID, Expired);
 | 
			
		||||
		else
 | 
			
		||||
            return AuthService()->IsAuthorized(Request, SessionToken, UInfo, TID, Expired );
 | 
			
		||||
			return AuthService()->IsAuthorized(Request, SessionToken, UInfo, TID, Expired);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
} // end of namespace
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -10,45 +10,36 @@
 | 
			
		||||
//	Arilia Wireless Inc.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "Poco/Environment.h"
 | 
			
		||||
#include "Poco/Util/Application.h"
 | 
			
		||||
#include "Poco/Util/Option.h"
 | 
			
		||||
#include "Poco/Environment.h"
 | 
			
		||||
 | 
			
		||||
#include "Daemon.h"
 | 
			
		||||
 | 
			
		||||
#include <aws/core/Aws.h>
 | 
			
		||||
#include <aws/s3/model/AccessControlPolicy.h>
 | 
			
		||||
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "SMTPMailerService.h"
 | 
			
		||||
#include "ActionLinkManager.h"
 | 
			
		||||
#include "AuthService.h"
 | 
			
		||||
#include "SMSSender.h"
 | 
			
		||||
#include "ActionLinkManager.h"
 | 
			
		||||
#include "SMTPMailerService.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "TotpCache.h"
 | 
			
		||||
#include "framework/RESTAPI_RateLimiter.h"
 | 
			
		||||
#include "framework/UI_WebSocketClientServer.h"
 | 
			
		||||
#include <SecretStore.h>
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class Daemon *Daemon::instance_ = nullptr;
 | 
			
		||||
 | 
			
		||||
	class Daemon *Daemon::instance() {
 | 
			
		||||
		if (instance_ == nullptr) {
 | 
			
		||||
            instance_ = new Daemon(vDAEMON_PROPERTIES_FILENAME,
 | 
			
		||||
                                   vDAEMON_ROOT_ENV_VAR,
 | 
			
		||||
                                   vDAEMON_CONFIG_ENV_VAR,
 | 
			
		||||
                                   vDAEMON_APP_NAME,
 | 
			
		||||
                                   vDAEMON_BUS_TIMER,
 | 
			
		||||
                                   SubSystemVec{
 | 
			
		||||
                                           StorageService(),
 | 
			
		||||
                                           SMSSender(),
 | 
			
		||||
                                           ActionLinkManager(),
 | 
			
		||||
                                           SMTPMailerService(),
 | 
			
		||||
                                           RESTAPI_RateLimiter(),
 | 
			
		||||
                                           TotpCache(),
 | 
			
		||||
                                           AuthService(),
 | 
			
		||||
                                           UI_WebSocketClientServer()
 | 
			
		||||
                                   });
 | 
			
		||||
			instance_ =
 | 
			
		||||
				new Daemon(vDAEMON_PROPERTIES_FILENAME, vDAEMON_ROOT_ENV_VAR,
 | 
			
		||||
						   vDAEMON_CONFIG_ENV_VAR, vDAEMON_APP_NAME, vDAEMON_BUS_TIMER,
 | 
			
		||||
						   SubSystemVec{StorageService(), SMSSender(), AuthService(), ActionLinkManager(),
 | 
			
		||||
										SMTPMailerService(), RESTAPI_RateLimiter(), TotpCache(),
 | 
			
		||||
										UI_WebSocketClientServer(), SecretStore()});
 | 
			
		||||
		}
 | 
			
		||||
		return instance_;
 | 
			
		||||
	}
 | 
			
		||||
@@ -60,7 +51,7 @@ namespace OpenWifi {
 | 
			
		||||
	void DaemonPostInitialization(Poco::Util::Application &self) {
 | 
			
		||||
		Daemon()->PostInitialization(self);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv) {
 | 
			
		||||
	try {
 | 
			
		||||
@@ -72,7 +63,7 @@ int main(int argc, char **argv) {
 | 
			
		||||
 | 
			
		||||
		Aws::InitAPI(AwsOptions);
 | 
			
		||||
 | 
			
		||||
        int ExitCode=0;
 | 
			
		||||
		int ExitCode = 0;
 | 
			
		||||
		{
 | 
			
		||||
			auto App = OpenWifi::Daemon::instance();
 | 
			
		||||
			ExitCode = App->run(argc, argv);
 | 
			
		||||
@@ -85,5 +76,4 @@ int main(int argc, char **argv) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// end of namespace
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								src/Daemon.h
									
									
									
									
									
								
							
							
						
						@@ -4,52 +4,48 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <set>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "framework/MicroServiceNames.h"
 | 
			
		||||
#include "framework/MicroService.h"
 | 
			
		||||
#include "framework/MicroServiceNames.h"
 | 
			
		||||
 | 
			
		||||
#include "Poco/Crypto/Cipher.h"
 | 
			
		||||
#include "Poco/Crypto/CipherFactory.h"
 | 
			
		||||
#include "Poco/Crypto/RSAKey.h"
 | 
			
		||||
#include "Poco/ErrorHandler.h"
 | 
			
		||||
#include "Poco/UUIDGenerator.h"
 | 
			
		||||
#include "Poco/Util/Application.h"
 | 
			
		||||
#include "Poco/Util/ServerApplication.h"
 | 
			
		||||
#include "Poco/Util/Option.h"
 | 
			
		||||
#include "Poco/Util/OptionSet.h"
 | 
			
		||||
#include "Poco/UUIDGenerator.h"
 | 
			
		||||
#include "Poco/ErrorHandler.h"
 | 
			
		||||
#include "Poco/Crypto/RSAKey.h"
 | 
			
		||||
#include "Poco/Crypto/CipherFactory.h"
 | 
			
		||||
#include "Poco/Crypto/Cipher.h"
 | 
			
		||||
 | 
			
		||||
#include "Poco/Util/ServerApplication.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
    [[maybe_unused]] static const char * vDAEMON_PROPERTIES_FILENAME = "owsec.properties";
 | 
			
		||||
    [[maybe_unused]] static const char * vDAEMON_ROOT_ENV_VAR = "OWSEC_ROOT";
 | 
			
		||||
    [[maybe_unused]] static const char * vDAEMON_CONFIG_ENV_VAR = "OWSEC_CONFIG";
 | 
			
		||||
    [[maybe_unused]] static const char * vDAEMON_APP_NAME = uSERVICE_SECURITY.c_str();
 | 
			
		||||
	[[maybe_unused]] static const char *vDAEMON_PROPERTIES_FILENAME = "owsec.properties";
 | 
			
		||||
	[[maybe_unused]] static const char *vDAEMON_ROOT_ENV_VAR = "OWSEC_ROOT";
 | 
			
		||||
	[[maybe_unused]] static const char *vDAEMON_CONFIG_ENV_VAR = "OWSEC_CONFIG";
 | 
			
		||||
	[[maybe_unused]] static const char *vDAEMON_APP_NAME = uSERVICE_SECURITY.c_str();
 | 
			
		||||
	[[maybe_unused]] static const uint64_t vDAEMON_BUS_TIMER = 5000;
 | 
			
		||||
 | 
			
		||||
	class Daemon : public MicroService {
 | 
			
		||||
	  public:
 | 
			
		||||
        explicit Daemon(const std::string & PropFile,
 | 
			
		||||
                        const std::string & RootEnv,
 | 
			
		||||
                        const std::string & ConfigEnv,
 | 
			
		||||
                        const std::string & AppName,
 | 
			
		||||
                        uint64_t BusTimer,
 | 
			
		||||
                        const SubSystemVec & SubSystems) :
 | 
			
		||||
                MicroService( PropFile, RootEnv, ConfigEnv, AppName, BusTimer, SubSystems) {};
 | 
			
		||||
		explicit Daemon(const std::string &PropFile, const std::string &RootEnv,
 | 
			
		||||
						const std::string &ConfigEnv, const std::string &AppName, uint64_t BusTimer,
 | 
			
		||||
						const SubSystemVec &SubSystems)
 | 
			
		||||
			: MicroService(PropFile, RootEnv, ConfigEnv, AppName, BusTimer, SubSystems){};
 | 
			
		||||
 | 
			
		||||
		void PostInitialization(Poco::Util::Application &self);
 | 
			
		||||
		static Daemon *instance();
 | 
			
		||||
        inline const std::string & AssetDir() { return AssetDir_; }
 | 
			
		||||
		inline const std::string &AssetDir() { return AssetDir_; }
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
		static Daemon *instance_;
 | 
			
		||||
		std::string AssetDir_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
    inline Daemon * Daemon() { return Daemon::instance(); }
 | 
			
		||||
	inline Daemon *Daemon() { return Daemon::instance(); }
 | 
			
		||||
	void DaemonPostInitialization(Poco::Util::Application &self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,9 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "MFAServer.h"
 | 
			
		||||
#include "AuthService.h"
 | 
			
		||||
#include "SMSSender.h"
 | 
			
		||||
#include "SMTPMailerService.h"
 | 
			
		||||
#include "AuthService.h"
 | 
			
		||||
#include "TotpCache.h"
 | 
			
		||||
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
@@ -13,41 +13,48 @@
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
    int MFAServer::Start() {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
	int MFAServer::Start() { return 0; }
 | 
			
		||||
 | 
			
		||||
    void MFAServer::Stop() {
 | 
			
		||||
    }
 | 
			
		||||
	void MFAServer::Stop() {}
 | 
			
		||||
 | 
			
		||||
    bool MFAServer::StartMFAChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, Poco::JSON::Object &ChallengeStart) {
 | 
			
		||||
	bool MFAServer::StartMFAChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo,
 | 
			
		||||
									  Poco::JSON::Object &ChallengeStart) {
 | 
			
		||||
		std::lock_guard G(Mutex_);
 | 
			
		||||
 | 
			
		||||
		CleanCache();
 | 
			
		||||
 | 
			
		||||
        if(!MethodEnabled(UInfo.userinfo.userTypeProprietaryInfo.mfa.method))
 | 
			
		||||
		if (!MethodEnabled(UInfo.userinfo.userTypeProprietaryInfo.mfa.method))
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		std::string Challenge = MakeChallenge();
 | 
			
		||||
		std::string uuid = MicroServiceCreateUUID();
 | 
			
		||||
		uint64_t Created = Utils::Now();
 | 
			
		||||
 | 
			
		||||
        ChallengeStart.set("uuid",uuid);
 | 
			
		||||
		ChallengeStart.set("uuid", uuid);
 | 
			
		||||
		ChallengeStart.set("created", Created);
 | 
			
		||||
		ChallengeStart.set("question", "mfa challenge");
 | 
			
		||||
		ChallengeStart.set("method", UInfo.userinfo.userTypeProprietaryInfo.mfa.method);
 | 
			
		||||
 | 
			
		||||
        Cache_[uuid] = MFACacheEntry{ .UInfo = UInfo, .Answer=Challenge, .Created=Created, .Method=UInfo.userinfo.userTypeProprietaryInfo.mfa.method };
 | 
			
		||||
		Cache_[uuid] = MFACacheEntry{.UInfo = UInfo,
 | 
			
		||||
									 .Answer = Challenge,
 | 
			
		||||
									 .Created = Created,
 | 
			
		||||
									 .Method = UInfo.userinfo.userTypeProprietaryInfo.mfa.method};
 | 
			
		||||
		return SendChallenge(UInfo, UInfo.userinfo.userTypeProprietaryInfo.mfa.method, Challenge);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    bool MFAServer::SendChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &Method, const std::string &Challenge) {
 | 
			
		||||
        if(Method==MFAMETHODS::SMS && SMSSender()->Enabled() && !UInfo.userinfo.userTypeProprietaryInfo.mobiles.empty()) {
 | 
			
		||||
            std::string Message = "This is your login code: " + Challenge + " Please enter this in your login screen.";
 | 
			
		||||
            return SMSSender()->Send(UInfo.userinfo.userTypeProprietaryInfo.mobiles[0].number, Message);
 | 
			
		||||
        } else if(Method==MFAMETHODS::EMAIL && SMTPMailerService()->Enabled() && !UInfo.userinfo.email.empty()) {
 | 
			
		||||
            return AuthService()->SendEmailChallengeCode(UInfo,Challenge);
 | 
			
		||||
        } else if(Method==MFAMETHODS::AUTHENTICATOR && !UInfo.userinfo.userTypeProprietaryInfo.authenticatorSecret.empty()) {
 | 
			
		||||
	bool MFAServer::SendChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo,
 | 
			
		||||
								  const std::string &Method, const std::string &Challenge) {
 | 
			
		||||
		if (Method == MFAMETHODS::SMS && SMSSender()->Enabled() &&
 | 
			
		||||
			!UInfo.userinfo.userTypeProprietaryInfo.mobiles.empty()) {
 | 
			
		||||
			std::string Message = "This is your login code: " + Challenge +
 | 
			
		||||
								  " Please enter this in your login screen.";
 | 
			
		||||
			return SMSSender()->Send(UInfo.userinfo.userTypeProprietaryInfo.mobiles[0].number,
 | 
			
		||||
									 Message);
 | 
			
		||||
		} else if (Method == MFAMETHODS::EMAIL && SMTPMailerService()->Enabled() &&
 | 
			
		||||
				   !UInfo.userinfo.email.empty()) {
 | 
			
		||||
			return AuthService()->SendEmailChallengeCode(UInfo, Challenge);
 | 
			
		||||
		} else if (Method == MFAMETHODS::AUTHENTICATOR &&
 | 
			
		||||
				   !UInfo.userinfo.userTypeProprietaryInfo.authenticatorSecret.empty()) {
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -57,30 +64,33 @@ namespace OpenWifi {
 | 
			
		||||
	bool MFAServer::ResendCode(const std::string &uuid) {
 | 
			
		||||
		std::lock_guard G(Mutex_);
 | 
			
		||||
		auto Hint = Cache_.find(uuid);
 | 
			
		||||
        if(Hint==Cache_.end())
 | 
			
		||||
		if (Hint == Cache_.end())
 | 
			
		||||
			return false;
 | 
			
		||||
		return SendChallenge(Hint->second.UInfo, Hint->second.Method, Hint->second.Answer);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    bool MFAServer::CompleteMFAChallenge(const Poco::JSON::Object::Ptr &ChallengeResponse, SecurityObjects::UserInfoAndPolicy &UInfo) {
 | 
			
		||||
	bool MFAServer::CompleteMFAChallenge(const Poco::JSON::Object::Ptr &ChallengeResponse,
 | 
			
		||||
										 SecurityObjects::UserInfoAndPolicy &UInfo) {
 | 
			
		||||
		std::lock_guard G(Mutex_);
 | 
			
		||||
 | 
			
		||||
        if(!ChallengeResponse->has("uuid") || !ChallengeResponse->has("answer"))
 | 
			
		||||
		if (!ChallengeResponse->has("uuid") || !ChallengeResponse->has("answer"))
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		auto uuid = ChallengeResponse->get("uuid").toString();
 | 
			
		||||
		auto Hint = Cache_.find(uuid);
 | 
			
		||||
        if(Hint == end(Cache_)) {
 | 
			
		||||
		if (Hint == end(Cache_)) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto answer = ChallengeResponse->get("answer").toString();
 | 
			
		||||
		std::string Expecting;
 | 
			
		||||
        if(Hint->second.Method==MFAMETHODS::AUTHENTICATOR) {
 | 
			
		||||
            if(!TotpCache()->ValidateCode(Hint->second.UInfo.userinfo.userTypeProprietaryInfo.authenticatorSecret,answer, Expecting)) {
 | 
			
		||||
		if (Hint->second.Method == MFAMETHODS::AUTHENTICATOR) {
 | 
			
		||||
			if (!TotpCache()->ValidateCode(
 | 
			
		||||
					Hint->second.UInfo.userinfo.userTypeProprietaryInfo.authenticatorSecret, answer,
 | 
			
		||||
					Expecting)) {
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
        } else if(Hint->second.Answer!=answer) {
 | 
			
		||||
		} else if (Hint->second.Answer != answer) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -90,13 +100,13 @@ namespace OpenWifi {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool MFAServer::MethodEnabled(const std::string &Method) {
 | 
			
		||||
        if(Method==MFAMETHODS::SMS)
 | 
			
		||||
		if (Method == MFAMETHODS::SMS)
 | 
			
		||||
			return SMSSender()->Enabled();
 | 
			
		||||
 | 
			
		||||
        if(Method==MFAMETHODS::EMAIL)
 | 
			
		||||
		if (Method == MFAMETHODS::EMAIL)
 | 
			
		||||
			return SMTPMailerService()->Enabled();
 | 
			
		||||
 | 
			
		||||
        if(Method==MFAMETHODS::AUTHENTICATOR)
 | 
			
		||||
		if (Method == MFAMETHODS::AUTHENTICATOR)
 | 
			
		||||
			return true;
 | 
			
		||||
 | 
			
		||||
		return false;
 | 
			
		||||
@@ -105,12 +115,12 @@ namespace OpenWifi {
 | 
			
		||||
	void MFAServer::CleanCache() {
 | 
			
		||||
		// it is assumed that you have locked Cache_ at this point.
 | 
			
		||||
		uint64_t Now = Utils::Now();
 | 
			
		||||
        for(auto i=begin(Cache_);i!=end(Cache_);) {
 | 
			
		||||
            if((Now-i->second.Created)>300) {
 | 
			
		||||
		for (auto i = begin(Cache_); i != end(Cache_);) {
 | 
			
		||||
			if ((Now - i->second.Created) > 300) {
 | 
			
		||||
				i = Cache_.erase(i);
 | 
			
		||||
			} else {
 | 
			
		||||
				++i;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -6,8 +6,8 @@
 | 
			
		||||
 | 
			
		||||
#include "Poco/JSON/Object.h"
 | 
			
		||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
 | 
			
		||||
#include "framework/SubSystemServer.h"
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
#include "framework/SubSystemServer.h"
 | 
			
		||||
 | 
			
		||||
#include "fmt/format.h"
 | 
			
		||||
 | 
			
		||||
@@ -17,11 +17,11 @@ namespace OpenWifi {
 | 
			
		||||
		inline const static std::string SMS{"sms"};
 | 
			
		||||
		inline const static std::string EMAIL{"email"};
 | 
			
		||||
		inline const static std::string AUTHENTICATOR{"authenticator"};
 | 
			
		||||
        inline const static std::vector<std::string> Methods{ SMS, EMAIL, AUTHENTICATOR };
 | 
			
		||||
		inline const static std::vector<std::string> Methods{SMS, EMAIL, AUTHENTICATOR};
 | 
			
		||||
		inline bool Validate(const std::string &M) {
 | 
			
		||||
            return std::find(cbegin(Methods), cend(Methods),M)!=Methods.end();
 | 
			
		||||
        }
 | 
			
		||||
			return std::find(cbegin(Methods), cend(Methods), M) != Methods.end();
 | 
			
		||||
		}
 | 
			
		||||
	} // namespace MFAMETHODS
 | 
			
		||||
 | 
			
		||||
	struct MFACacheEntry {
 | 
			
		||||
		SecurityObjects::UserInfoAndPolicy UInfo;
 | 
			
		||||
@@ -30,10 +30,9 @@ namespace OpenWifi {
 | 
			
		||||
		std::string Method;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	typedef std::map<std::string, MFACacheEntry> MFAChallengeCache;
 | 
			
		||||
 | 
			
		||||
    typedef std::map<std::string,MFACacheEntry>     MFAChallengeCache;
 | 
			
		||||
 | 
			
		||||
    class MFAServer : public SubSystemServer{
 | 
			
		||||
	class MFAServer : public SubSystemServer {
 | 
			
		||||
	  public:
 | 
			
		||||
		int Start() override;
 | 
			
		||||
		void Stop() override;
 | 
			
		||||
@@ -42,26 +41,25 @@ namespace OpenWifi {
 | 
			
		||||
			return instance_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        bool StartMFAChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, Poco::JSON::Object &Challenge);
 | 
			
		||||
        bool CompleteMFAChallenge(const Poco::JSON::Object::Ptr &ChallengeResponse, SecurityObjects::UserInfoAndPolicy &UInfo);
 | 
			
		||||
		bool StartMFAChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo,
 | 
			
		||||
							   Poco::JSON::Object &Challenge);
 | 
			
		||||
		bool CompleteMFAChallenge(const Poco::JSON::Object::Ptr &ChallengeResponse,
 | 
			
		||||
								  SecurityObjects::UserInfoAndPolicy &UInfo);
 | 
			
		||||
		static bool MethodEnabled(const std::string &Method);
 | 
			
		||||
		bool ResendCode(const std::string &uuid);
 | 
			
		||||
        static bool SendChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &Method, const std::string &Challenge);
 | 
			
		||||
		static bool SendChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo,
 | 
			
		||||
								  const std::string &Method, const std::string &Challenge);
 | 
			
		||||
 | 
			
		||||
		static inline std::string MakeChallenge() {
 | 
			
		||||
            return fmt::format("{0:06}" , MicroServiceRandom(1,999999) );
 | 
			
		||||
			return fmt::format("{0:06}", MicroServiceRandom(1, 999999));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
		MFAChallengeCache Cache_;
 | 
			
		||||
        MFAServer() noexcept:
 | 
			
		||||
            SubSystemServer("MFServer", "MFA-SVR", "mfa")
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
		MFAServer() noexcept : SubSystemServer("MFServer", "MFA-SVR", "mfa") {}
 | 
			
		||||
 | 
			
		||||
		void CleanCache();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	inline auto MFAServer() { return MFAServer::instance(); }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by stephane bourque on 2022-07-25.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "MessagingTemplates.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
} // OpenWifi
 | 
			
		||||
@@ -11,7 +11,7 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	class MessagingTemplates {
 | 
			
		||||
	  public:
 | 
			
		||||
        static MessagingTemplates & instance() {
 | 
			
		||||
		static MessagingTemplates &instance() {
 | 
			
		||||
			static auto instance = new MessagingTemplates;
 | 
			
		||||
			return *instance;
 | 
			
		||||
		}
 | 
			
		||||
@@ -24,35 +24,67 @@ namespace OpenWifi {
 | 
			
		||||
			VERIFICATION_CODE,
 | 
			
		||||
			SUB_FORGOT_PASSWORD,
 | 
			
		||||
			SUB_EMAIL_VERIFICATION,
 | 
			
		||||
            SUB_VERIFICATION_CODE
 | 
			
		||||
			SUB_VERIFICATION_CODE,
 | 
			
		||||
			CERTIFICATE_TRANSFER_NOTIFICATION,
 | 
			
		||||
			CERTIFICATE_TRANSFER_AUTHORIZATION,
 | 
			
		||||
			CERTIFICATE_DISPUTE_SUCCESS,
 | 
			
		||||
			CERTIFICATE_DISPUTE_REJECTED,
 | 
			
		||||
			CERTIFICATE_TRANSFER_CANCELED,
 | 
			
		||||
			CERTIFICATE_TRANSFER_ACCEPTED,
 | 
			
		||||
			CERTIFICATE_TRANSFER_REJECTED
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
        static std::string AddOperator(const std::string & filename, const std::string &OperatorName) {
 | 
			
		||||
            if(OperatorName.empty())
 | 
			
		||||
		static std::string AddOperator(const std::string &filename,
 | 
			
		||||
									   const std::string &OperatorName) {
 | 
			
		||||
			if (OperatorName.empty())
 | 
			
		||||
				return "/" + filename;
 | 
			
		||||
			return "/" + OperatorName + "/" + filename;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        static std::string TemplateName( EMAIL_REASON r , const std::string &OperatorName="") {
 | 
			
		||||
		static std::string TemplateName(EMAIL_REASON r, const std::string &OperatorName = "") {
 | 
			
		||||
			switch (r) {
 | 
			
		||||
                case FORGOT_PASSWORD: return AddOperator(EmailTemplateNames[FORGOT_PASSWORD],OperatorName);
 | 
			
		||||
                case EMAIL_VERIFICATION: return AddOperator(EmailTemplateNames[EMAIL_VERIFICATION],OperatorName);
 | 
			
		||||
                case SUB_SIGNUP_VERIFICATION: return AddOperator(EmailTemplateNames[SUB_SIGNUP_VERIFICATION],OperatorName);
 | 
			
		||||
                case EMAIL_INVITATION: return AddOperator(EmailTemplateNames[EMAIL_INVITATION],OperatorName);
 | 
			
		||||
                case VERIFICATION_CODE: return AddOperator(EmailTemplateNames[VERIFICATION_CODE],OperatorName);
 | 
			
		||||
                case SUB_FORGOT_PASSWORD: return AddOperator(EmailTemplateNames[SUB_FORGOT_PASSWORD],OperatorName);
 | 
			
		||||
                case SUB_EMAIL_VERIFICATION: return AddOperator(EmailTemplateNames[SUB_EMAIL_VERIFICATION],OperatorName);
 | 
			
		||||
                case SUB_VERIFICATION_CODE: return AddOperator(EmailTemplateNames[SUB_VERIFICATION_CODE],OperatorName);
 | 
			
		||||
			case FORGOT_PASSWORD:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[FORGOT_PASSWORD], OperatorName);
 | 
			
		||||
			case EMAIL_VERIFICATION:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[EMAIL_VERIFICATION], OperatorName);
 | 
			
		||||
			case SUB_SIGNUP_VERIFICATION:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[SUB_SIGNUP_VERIFICATION], OperatorName);
 | 
			
		||||
			case EMAIL_INVITATION:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[EMAIL_INVITATION], OperatorName);
 | 
			
		||||
			case VERIFICATION_CODE:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[VERIFICATION_CODE], OperatorName);
 | 
			
		||||
			case SUB_FORGOT_PASSWORD:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[SUB_FORGOT_PASSWORD], OperatorName);
 | 
			
		||||
			case SUB_EMAIL_VERIFICATION:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[SUB_EMAIL_VERIFICATION], OperatorName);
 | 
			
		||||
			case SUB_VERIFICATION_CODE:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[SUB_VERIFICATION_CODE], OperatorName);
 | 
			
		||||
			case CERTIFICATE_TRANSFER_NOTIFICATION:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[CERTIFICATE_TRANSFER_NOTIFICATION],
 | 
			
		||||
								   OperatorName);
 | 
			
		||||
			case CERTIFICATE_TRANSFER_AUTHORIZATION:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[CERTIFICATE_TRANSFER_AUTHORIZATION],
 | 
			
		||||
								   OperatorName);
 | 
			
		||||
			case CERTIFICATE_DISPUTE_SUCCESS:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[CERTIFICATE_DISPUTE_SUCCESS], OperatorName);
 | 
			
		||||
			case CERTIFICATE_DISPUTE_REJECTED:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[CERTIFICATE_DISPUTE_REJECTED], OperatorName);
 | 
			
		||||
			case CERTIFICATE_TRANSFER_CANCELED:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[CERTIFICATE_TRANSFER_CANCELED], OperatorName);
 | 
			
		||||
			case CERTIFICATE_TRANSFER_ACCEPTED:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[CERTIFICATE_TRANSFER_ACCEPTED], OperatorName);
 | 
			
		||||
			case CERTIFICATE_TRANSFER_REJECTED:
 | 
			
		||||
				return AddOperator(EmailTemplateNames[CERTIFICATE_TRANSFER_REJECTED], OperatorName);
 | 
			
		||||
			default:
 | 
			
		||||
				return "";
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        static std::string Logo(const std::string &OperatorName = "" ) {
 | 
			
		||||
		static std::string Logo(const std::string &OperatorName = "") {
 | 
			
		||||
			return AddOperator("logo.png", OperatorName);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        static std::string SubLogo(const std::string &OperatorName = "" ) {
 | 
			
		||||
		static std::string SubLogo(const std::string &OperatorName = "") {
 | 
			
		||||
			return AddOperator("sub_logo.png", OperatorName);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -65,11 +97,16 @@ namespace OpenWifi {
 | 
			
		||||
			"verification_code",
 | 
			
		||||
			"sub_password_reset",
 | 
			
		||||
			"sub_email_verification",
 | 
			
		||||
                "sub_verification_code"
 | 
			
		||||
        };
 | 
			
		||||
			"sub_verification_code",
 | 
			
		||||
			"certificate_transfer_notification",
 | 
			
		||||
			"certificate_transfer_authorization",
 | 
			
		||||
			"certificate_dispute_success",
 | 
			
		||||
			"certificate_dispute_rejected",
 | 
			
		||||
			"certificate_transfer_canceled",
 | 
			
		||||
			"certificate_transfer_accepted",
 | 
			
		||||
			"certificate_transfer_rejected"};
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
    inline MessagingTemplates & MessagingTemplates() { return MessagingTemplates::instance(); }
 | 
			
		||||
 | 
			
		||||
} // OpenWifi
 | 
			
		||||
	inline MessagingTemplates &MessagingTemplates() { return MessagingTemplates::instance(); }
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -7,134 +7,183 @@
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_action_links.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "framework/RESTAPI_PartHandler.h"
 | 
			
		||||
#include "framework/OpenAPIRequests.h"
 | 
			
		||||
#include "framework/RESTAPI_PartHandler.h"
 | 
			
		||||
 | 
			
		||||
#include "Daemon.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
#if defined(TIP_CERT_SERVICE)
 | 
			
		||||
	bool ProcessExternalActionLinks(RESTAPIHandler &handler, const std::string &Id,
 | 
			
		||||
									const std::string &Action);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_action_links::DoGet() {
 | 
			
		||||
 | 
			
		||||
        auto Action = GetParameter("action","");
 | 
			
		||||
        auto Id = GetParameter("id","");
 | 
			
		||||
		auto Action = GetParameter("action", "");
 | 
			
		||||
		auto Id = GetParameter("id", "");
 | 
			
		||||
 | 
			
		||||
#if defined(TIP_CERT_SERVICE)
 | 
			
		||||
		if (!OpenWifi::ProcessExternalActionLinks(*this, Id, Action)) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::ActionLink Link;
 | 
			
		||||
        if(!StorageService()->ActionLinksDB().GetActionLink(Id,Link))
 | 
			
		||||
		if (!StorageService()->ActionLinksDB().GetActionLink(Id, Link))
 | 
			
		||||
			return DoReturnA404();
 | 
			
		||||
 | 
			
		||||
        if(Action=="password_reset")
 | 
			
		||||
		if (Action == "password_reset")
 | 
			
		||||
			return RequestResetPassword(Link);
 | 
			
		||||
        else if(Action=="sub_password_reset")
 | 
			
		||||
            return RequestSubResetPassword(Link);
 | 
			
		||||
        else if(Action=="email_verification")
 | 
			
		||||
		else if (Action == "sub_password_reset")
 | 
			
		||||
			return RequestResetPassword(Link);
 | 
			
		||||
		else if (Action == "email_verification")
 | 
			
		||||
			return DoEmailVerification(Link);
 | 
			
		||||
        else if(Action=="sub_email_verification")
 | 
			
		||||
            return DoSubEmailVerification(Link);
 | 
			
		||||
        else if(Action=="signup_verification")
 | 
			
		||||
		else if (Action == "sub_email_verification")
 | 
			
		||||
			return DoEmailVerification(Link);
 | 
			
		||||
		else if (Action == "signup_verification")
 | 
			
		||||
			return DoNewSubVerification(Link);
 | 
			
		||||
		else
 | 
			
		||||
			return DoReturnA404();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_action_links::DoPost() {
 | 
			
		||||
        auto Action = GetParameter("action","");
 | 
			
		||||
		auto Action = GetParameter("action", "");
 | 
			
		||||
 | 
			
		||||
        if(Action=="password_reset")
 | 
			
		||||
		if (Action == "password_reset")
 | 
			
		||||
			return CompleteResetPassword();
 | 
			
		||||
        else if(Action=="sub_password_reset")
 | 
			
		||||
		else if (Action == "sub_password_reset")
 | 
			
		||||
			return CompleteResetPassword();
 | 
			
		||||
        else if(Action=="signup_completion")
 | 
			
		||||
		else if (Action == "signup_completion")
 | 
			
		||||
			return CompleteSubVerification();
 | 
			
		||||
        else if(Action=="email_invitation")
 | 
			
		||||
		else if (Action == "email_invitation")
 | 
			
		||||
			return CompleteEmailInvitation();
 | 
			
		||||
		else
 | 
			
		||||
			return DoReturnA404();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_action_links::AddGlobalVars(Types::StringPairVec &Vars) {
 | 
			
		||||
		Vars.push_back(std::make_pair("USER_HELPER_EMAIL", AuthService()->HelperEmail()));
 | 
			
		||||
		Vars.push_back(std::make_pair("SUB_HELPER_EMAIL", AuthService()->SubHelperEmail()));
 | 
			
		||||
		Vars.push_back(
 | 
			
		||||
			std::make_pair("GLOBAL_USER_HELPER_EMAIL", AuthService()->GlobalHelperEmail()));
 | 
			
		||||
		Vars.push_back(
 | 
			
		||||
			std::make_pair("GLOBAL_SUB_HELPER_EMAIL", AuthService()->GlobalSubHelperEmail()));
 | 
			
		||||
		Vars.push_back(std::make_pair("USER_HELPER_SITE", AuthService()->HelperSite()));
 | 
			
		||||
		Vars.push_back(std::make_pair("SUB_HELPER_SITE", AuthService()->SubHelperSite()));
 | 
			
		||||
		Vars.push_back(std::make_pair("USER_SYSTEM_LOGIN", AuthService()->SystemLoginSite()));
 | 
			
		||||
		Vars.push_back(std::make_pair("SUB_SYSTEM_LOGIN", AuthService()->SubSystemLoginSite()));
 | 
			
		||||
		Vars.push_back(std::make_pair("USER_SIGNATURE", AuthService()->UserSignature()));
 | 
			
		||||
		Vars.push_back(std::make_pair("SUB_SIGNATURE", AuthService()->SubSignature()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_action_links::RequestResetPassword(SecurityObjects::ActionLink &Link) {
 | 
			
		||||
        Logger_.information(fmt::format("REQUEST-PASSWORD-RESET({}): For ID={}", Request->clientAddress().toString(), Link.userId));
 | 
			
		||||
        Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset.html"};
 | 
			
		||||
        Types::StringPairVec    FormVars{ {"UUID", Link.id},
 | 
			
		||||
		Logger_.information(fmt::format("REQUEST-PASSWORD-RESET({}): For ID={}",
 | 
			
		||||
										Request->clientAddress().toString(), Link.userId));
 | 
			
		||||
		Poco::File FormFile{Daemon()->AssetDir() + "/password_reset.html"};
 | 
			
		||||
		Types::StringPairVec FormVars{
 | 
			
		||||
			{"UUID", Link.id},
 | 
			
		||||
			{"PASSWORD_VALIDATION", AuthService()->PasswordValidationExpression()}};
 | 
			
		||||
        SendHTMLFileBack(FormFile,FormVars);
 | 
			
		||||
		AddGlobalVars(FormVars);
 | 
			
		||||
		SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_action_links::DoNewSubVerification(SecurityObjects::ActionLink &Link) {
 | 
			
		||||
        Logger_.information(fmt::format("REQUEST-SUB-SIGNUP({}): For ID={}", Request->clientAddress().toString(), Link.userId));
 | 
			
		||||
        Poco::File  FormFile{ Daemon()->AssetDir() + "/sub_signup_verification.html"};
 | 
			
		||||
        Types::StringPairVec    FormVars{ {"UUID", Link.id},
 | 
			
		||||
		Logger_.information(fmt::format("REQUEST-SUB-SIGNUP({}): For ID={}",
 | 
			
		||||
										Request->clientAddress().toString(), Link.userId));
 | 
			
		||||
		Poco::File FormFile{Daemon()->AssetDir() + "/sub_signup_verification.html"};
 | 
			
		||||
		Types::StringPairVec FormVars{
 | 
			
		||||
			{"UUID", Link.id},
 | 
			
		||||
			{"PASSWORD_VALIDATION", AuthService()->PasswordValidationExpression()}};
 | 
			
		||||
        SendHTMLFileBack(FormFile,FormVars);
 | 
			
		||||
		AddGlobalVars(FormVars);
 | 
			
		||||
		SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_action_links::CompleteResetPassword() {
 | 
			
		||||
        //  form has been posted...
 | 
			
		||||
		RESTAPI_PartHandler PartHandler;
 | 
			
		||||
		Poco::Net::HTMLForm Form(*Request, Request->stream(), PartHandler);
 | 
			
		||||
		if (!Form.empty()) {
 | 
			
		||||
 | 
			
		||||
            auto Password1 = Form.get("password1","bla");
 | 
			
		||||
            auto Password2 = Form.get("password2","blu");
 | 
			
		||||
            auto Id = Form.get("id","");
 | 
			
		||||
			auto Password1 = Form.get("password1", "bla");
 | 
			
		||||
			auto Password2 = Form.get("password2", "blu");
 | 
			
		||||
			auto Id = Form.get("id", "");
 | 
			
		||||
			auto now = OpenWifi::Now();
 | 
			
		||||
 | 
			
		||||
			SecurityObjects::ActionLink Link;
 | 
			
		||||
            if(!StorageService()->ActionLinksDB().GetActionLink(Id,Link))
 | 
			
		||||
			if (!StorageService()->ActionLinksDB().GetActionLink(Id, Link))
 | 
			
		||||
				return DoReturnA404();
 | 
			
		||||
 | 
			
		||||
            if(now > Link.expires) {
 | 
			
		||||
			if (now > Link.expires) {
 | 
			
		||||
				StorageService()->ActionLinksDB().CancelAction(Id);
 | 
			
		||||
				return DoReturnA404();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            if(Password1!=Password2 || !AuthService()->ValidatePassword(Password2) || !AuthService()->ValidatePassword(Password1)) {
 | 
			
		||||
                Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_error.html"};
 | 
			
		||||
                Types::StringPairVec    FormVars{ {"UUID", Id},
 | 
			
		||||
                                                  {"ERROR_TEXT", "For some reason, the passwords entered do not match or they do not comply with"
 | 
			
		||||
			if (Password1 != Password2 || !AuthService()->ValidatePassword(Password2) ||
 | 
			
		||||
				!AuthService()->ValidatePassword(Password1)) {
 | 
			
		||||
				Poco::File FormFile{Daemon()->AssetDir() + "/password_reset_error.html"};
 | 
			
		||||
				Types::StringPairVec FormVars{
 | 
			
		||||
					{"UUID", Id},
 | 
			
		||||
					{"ERROR_TEXT",
 | 
			
		||||
					 "For some reason, the passwords entered do not match or they do not comply "
 | 
			
		||||
					 "with"
 | 
			
		||||
					 " accepted password creation restrictions. Please consult our on-line help"
 | 
			
		||||
                                                                 " to look at the our password policy. If you would like to contact us, please mention"
 | 
			
		||||
                                                                 " id(" + Id + ")"}};
 | 
			
		||||
                return SendHTMLFileBack(FormFile,FormVars);
 | 
			
		||||
					 " to look at the our password policy. If you would like to contact us, please "
 | 
			
		||||
					 "mention"
 | 
			
		||||
					 " id(" +
 | 
			
		||||
						 Id + ")"}};
 | 
			
		||||
				AddGlobalVars(FormVars);
 | 
			
		||||
				return SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			SecurityObjects::UserInfo UInfo;
 | 
			
		||||
 | 
			
		||||
            bool Found = Link.userAction ? StorageService()->UserDB().GetUserById(Link.userId,UInfo) : StorageService()->SubDB().GetUserById(Link.userId,UInfo);
 | 
			
		||||
            if(!Found) {
 | 
			
		||||
                Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_error.html"};
 | 
			
		||||
                Types::StringPairVec    FormVars{ {"UUID", Id},
 | 
			
		||||
                                                  {"ERROR_TEXT", "This request does not contain a valid user ID. Please contact your system administrator."}};
 | 
			
		||||
                return SendHTMLFileBack(FormFile,FormVars);
 | 
			
		||||
			bool Found = Link.userAction
 | 
			
		||||
							 ? StorageService()->UserDB().GetUserById(Link.userId, UInfo)
 | 
			
		||||
							 : StorageService()->SubDB().GetUserById(Link.userId, UInfo);
 | 
			
		||||
			if (!Found) {
 | 
			
		||||
				Poco::File FormFile{Daemon()->AssetDir() + "/password_reset_error.html"};
 | 
			
		||||
				Types::StringPairVec FormVars{
 | 
			
		||||
					{"UUID", Id},
 | 
			
		||||
					{"ERROR_TEXT", "This request does not contain a valid user ID. Please contact "
 | 
			
		||||
								   "your system administrator."}};
 | 
			
		||||
				AddGlobalVars(FormVars);
 | 
			
		||||
				return SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            if(UInfo.blackListed || UInfo.suspended) {
 | 
			
		||||
                Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_error.html"};
 | 
			
		||||
                Types::StringPairVec    FormVars{ {"UUID", Id},
 | 
			
		||||
                                                  {"ERROR_TEXT", "Please contact our system administrators. We have identified an error in your account that must be resolved first."}};
 | 
			
		||||
                return SendHTMLFileBack(FormFile,FormVars);
 | 
			
		||||
			if (UInfo.blackListed || UInfo.suspended) {
 | 
			
		||||
				Poco::File FormFile{Daemon()->AssetDir() + "/password_reset_error.html"};
 | 
			
		||||
				Types::StringPairVec FormVars{
 | 
			
		||||
					{"UUID", Id},
 | 
			
		||||
					{"ERROR_TEXT", "Please contact our system administrators. We have identified "
 | 
			
		||||
								   "an error in your account that must be resolved first."}};
 | 
			
		||||
				AddGlobalVars(FormVars);
 | 
			
		||||
				return SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            bool GoodPassword = Link.userAction ? AuthService()->SetPassword(Password1,UInfo) : AuthService()->SetSubPassword(Password1,UInfo);
 | 
			
		||||
            if(!GoodPassword) {
 | 
			
		||||
                Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_error.html"};
 | 
			
		||||
                Types::StringPairVec    FormVars{ {"UUID", Id},
 | 
			
		||||
                                                  {"ERROR_TEXT", "You cannot reuse one of your recent passwords."}};
 | 
			
		||||
                return SendHTMLFileBack(FormFile,FormVars);
 | 
			
		||||
			bool GoodPassword = Link.userAction ? AuthService()->SetPassword(Password1, UInfo)
 | 
			
		||||
												: AuthService()->SetSubPassword(Password1, UInfo);
 | 
			
		||||
			if (!GoodPassword) {
 | 
			
		||||
				Poco::File FormFile{Daemon()->AssetDir() + "/password_reset_error.html"};
 | 
			
		||||
				Types::StringPairVec FormVars{
 | 
			
		||||
					{"UUID", Id}, {"ERROR_TEXT", "You cannot reuse one of your recent passwords."}};
 | 
			
		||||
				AddGlobalVars(FormVars);
 | 
			
		||||
				return SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			UInfo.modified = OpenWifi::Now();
 | 
			
		||||
            if(Link.userAction)
 | 
			
		||||
                StorageService()->UserDB().UpdateUserInfo(UInfo.email,Link.userId,UInfo);
 | 
			
		||||
			if (Link.userAction)
 | 
			
		||||
				StorageService()->UserDB().UpdateUserInfo(UInfo.email, Link.userId, UInfo);
 | 
			
		||||
			else
 | 
			
		||||
                StorageService()->SubDB().UpdateUserInfo(UInfo.email,Link.userId,UInfo);
 | 
			
		||||
				StorageService()->SubDB().UpdateUserInfo(UInfo.email, Link.userId, UInfo);
 | 
			
		||||
 | 
			
		||||
            Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_success.html"};
 | 
			
		||||
            Types::StringPairVec    FormVars{ {"UUID", Id},
 | 
			
		||||
			Poco::File FormFile{Daemon()->AssetDir() + "/password_reset_success.html"};
 | 
			
		||||
			Types::StringPairVec FormVars{{"UUID", Id},
 | 
			
		||||
										  {"USERNAME", UInfo.email},
 | 
			
		||||
                                              {"ACTION_LINK",MicroService::instance().GetUIURI()}};
 | 
			
		||||
										  {"ACTION_LINK", MicroService::instance().GetUIURI()}};
 | 
			
		||||
			AddGlobalVars(FormVars);
 | 
			
		||||
			StorageService()->ActionLinksDB().CompleteAction(Id);
 | 
			
		||||
            SendHTMLFileBack(FormFile,FormVars);
 | 
			
		||||
			SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
		} else {
 | 
			
		||||
			DoReturnA404();
 | 
			
		||||
		}
 | 
			
		||||
@@ -145,53 +194,66 @@ namespace OpenWifi {
 | 
			
		||||
		Poco::Net::HTMLForm Form(*Request, Request->stream(), PartHandler);
 | 
			
		||||
 | 
			
		||||
		if (!Form.empty()) {
 | 
			
		||||
            auto Password1 = Form.get("password1","bla");
 | 
			
		||||
            auto Password2 = Form.get("password2","blu");
 | 
			
		||||
            auto Id = Form.get("id","");
 | 
			
		||||
			auto Password1 = Form.get("password1", "bla");
 | 
			
		||||
			auto Password2 = Form.get("password2", "blu");
 | 
			
		||||
			auto Id = Form.get("id", "");
 | 
			
		||||
			auto now = OpenWifi::Now();
 | 
			
		||||
 | 
			
		||||
			SecurityObjects::ActionLink Link;
 | 
			
		||||
            if(!StorageService()->ActionLinksDB().GetActionLink(Id,Link)) {
 | 
			
		||||
			if (!StorageService()->ActionLinksDB().GetActionLink(Id, Link)) {
 | 
			
		||||
				return DoReturnA404();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            if(now > Link.expires) {
 | 
			
		||||
			if (now > Link.expires) {
 | 
			
		||||
				StorageService()->ActionLinksDB().CancelAction(Id);
 | 
			
		||||
				return DoReturnA404();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            if(Password1!=Password2 || !AuthService()->ValidateSubPassword(Password1)) {
 | 
			
		||||
                Poco::File  FormFile{ Daemon()->AssetDir() + "/sub_password_reset_error.html"};
 | 
			
		||||
                Types::StringPairVec    FormVars{ {"UUID", Id},
 | 
			
		||||
                                                  {"ERROR_TEXT", "For some reason, the passwords entered do not match or they do not comply with"
 | 
			
		||||
			if (Password1 != Password2 || !AuthService()->ValidateSubPassword(Password1)) {
 | 
			
		||||
				Poco::File FormFile{Daemon()->AssetDir() + "/sub_password_reset_error.html"};
 | 
			
		||||
				Types::StringPairVec FormVars{
 | 
			
		||||
					{"UUID", Id},
 | 
			
		||||
					{"ERROR_TEXT",
 | 
			
		||||
					 "For some reason, the passwords entered do not match or they do not comply "
 | 
			
		||||
					 "with"
 | 
			
		||||
					 " accepted password creation restrictions. Please consult our on-line help"
 | 
			
		||||
                                                                 " to look at the our password policy. If you would like to contact us, please mention"
 | 
			
		||||
                                                                 " id(" + Id + ")"}};
 | 
			
		||||
                return SendHTMLFileBack(FormFile,FormVars);
 | 
			
		||||
					 " to look at the our password policy. If you would like to contact us, please "
 | 
			
		||||
					 "mention"
 | 
			
		||||
					 " id(" +
 | 
			
		||||
						 Id + ")"}};
 | 
			
		||||
				AddGlobalVars(FormVars);
 | 
			
		||||
				return SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			SecurityObjects::UserInfo UInfo;
 | 
			
		||||
            bool Found = StorageService()->SubDB().GetUserById(Link.userId,UInfo);
 | 
			
		||||
            if(!Found) {
 | 
			
		||||
                Poco::File  FormFile{ Daemon()->AssetDir() + "/sub_signup_verification_error.html"};
 | 
			
		||||
                Types::StringPairVec    FormVars{ {"UUID", Id},
 | 
			
		||||
                                                  {"ERROR_TEXT", "This request does not contain a valid user ID. Please contact your system administrator."}};
 | 
			
		||||
                return SendHTMLFileBack(FormFile,FormVars);
 | 
			
		||||
			bool Found = StorageService()->SubDB().GetUserById(Link.userId, UInfo);
 | 
			
		||||
			if (!Found) {
 | 
			
		||||
				Poco::File FormFile{Daemon()->AssetDir() + "/sub_signup_verification_error.html"};
 | 
			
		||||
				Types::StringPairVec FormVars{
 | 
			
		||||
					{"UUID", Id},
 | 
			
		||||
					{"ERROR_TEXT", "This request does not contain a valid user ID. Please contact "
 | 
			
		||||
								   "your system administrator."}};
 | 
			
		||||
				AddGlobalVars(FormVars);
 | 
			
		||||
				return SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            if(UInfo.blackListed || UInfo.suspended) {
 | 
			
		||||
                Poco::File  FormFile{ Daemon()->AssetDir() + "/sub_signup_verification_error.html"};
 | 
			
		||||
                Types::StringPairVec    FormVars{ {"UUID", Id},
 | 
			
		||||
                                                  {"ERROR_TEXT", "Please contact our system administrators. We have identified an error in your account that must be resolved first."}};
 | 
			
		||||
                return SendHTMLFileBack(FormFile,FormVars);
 | 
			
		||||
			if (UInfo.blackListed || UInfo.suspended) {
 | 
			
		||||
				Poco::File FormFile{Daemon()->AssetDir() + "/sub_signup_verification_error.html"};
 | 
			
		||||
				Types::StringPairVec FormVars{
 | 
			
		||||
					{"UUID", Id},
 | 
			
		||||
					{"ERROR_TEXT", "Please contact our system administrators. We have identified "
 | 
			
		||||
								   "an error in your account that must be resolved first."}};
 | 
			
		||||
				AddGlobalVars(FormVars);
 | 
			
		||||
				return SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            bool GoodPassword = AuthService()->SetSubPassword(Password1,UInfo);
 | 
			
		||||
            if(!GoodPassword) {
 | 
			
		||||
                Poco::File  FormFile{ Daemon()->AssetDir() + "/sub_signup_verification_error.html"};
 | 
			
		||||
                Types::StringPairVec    FormVars{ {"UUID", Id},
 | 
			
		||||
                                                  {"ERROR_TEXT", "You cannot reuse one of your recent passwords."}};
 | 
			
		||||
                return SendHTMLFileBack(FormFile,FormVars);
 | 
			
		||||
			bool GoodPassword = AuthService()->SetSubPassword(Password1, UInfo);
 | 
			
		||||
			if (!GoodPassword) {
 | 
			
		||||
				Poco::File FormFile{Daemon()->AssetDir() + "/sub_signup_verification_error.html"};
 | 
			
		||||
				Types::StringPairVec FormVars{
 | 
			
		||||
					{"UUID", Id}, {"ERROR_TEXT", "You cannot reuse one of your recent passwords."}};
 | 
			
		||||
				AddGlobalVars(FormVars);
 | 
			
		||||
				return SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			UInfo.modified = OpenWifi::Now();
 | 
			
		||||
@@ -200,33 +262,35 @@ namespace OpenWifi {
 | 
			
		||||
			UInfo.waitingForEmailCheck = false;
 | 
			
		||||
			UInfo.validated = OpenWifi::Now();
 | 
			
		||||
 | 
			
		||||
            StorageService()->SubDB().UpdateUserInfo(UInfo.email,Link.userId,UInfo);
 | 
			
		||||
			StorageService()->SubDB().UpdateUserInfo(UInfo.email, Link.userId, UInfo);
 | 
			
		||||
 | 
			
		||||
            Poco::File  FormFile{ Daemon()->AssetDir() + "/sub_signup_verification_success.html"};
 | 
			
		||||
            Types::StringPairVec    FormVars{ {"UUID", Id},
 | 
			
		||||
                                              {"USERNAME", UInfo.email} };
 | 
			
		||||
			Poco::File FormFile{Daemon()->AssetDir() + "/sub_signup_verification_success.html"};
 | 
			
		||||
			Types::StringPairVec FormVars{{"UUID", Id}, {"USERNAME", UInfo.email}};
 | 
			
		||||
			StorageService()->ActionLinksDB().CompleteAction(Id);
 | 
			
		||||
 | 
			
		||||
			//  Send the update to the provisioning service
 | 
			
		||||
			Poco::JSON::Object Body;
 | 
			
		||||
            auto RawSignup = Poco::StringTokenizer(UInfo.signingUp,":");
 | 
			
		||||
            Body.set("signupUUID", RawSignup.count()==1 ? UInfo.signingUp : RawSignup[1]);
 | 
			
		||||
            OpenAPIRequestPut   ProvRequest(uSERVICE_PROVISIONING,"/api/v1/signup",
 | 
			
		||||
                                            {
 | 
			
		||||
                                                {"signupUUID", RawSignup.count()==1 ? UInfo.signingUp : RawSignup[1]} ,
 | 
			
		||||
                                                {"operation", "emailVerified"}
 | 
			
		||||
                                            },
 | 
			
		||||
                                            Body,30000);
 | 
			
		||||
            Logger().information(fmt::format("({}): Completed subscriber e-mail verification and password.",UInfo.email));
 | 
			
		||||
			auto RawSignup = Poco::StringTokenizer(UInfo.signingUp, ":");
 | 
			
		||||
			Body.set("signupUUID", RawSignup.count() == 1 ? UInfo.signingUp : RawSignup[1]);
 | 
			
		||||
			OpenAPIRequestPut ProvRequest(
 | 
			
		||||
				uSERVICE_PROVISIONING, "/api/v1/signup",
 | 
			
		||||
				{{"signupUUID", RawSignup.count() == 1 ? UInfo.signingUp : RawSignup[1]},
 | 
			
		||||
				 {"operation", "emailVerified"}},
 | 
			
		||||
				Body, 30000);
 | 
			
		||||
			Logger().information(fmt::format(
 | 
			
		||||
				"({}): Completed subscriber e-mail verification and password.", UInfo.email));
 | 
			
		||||
			Poco::JSON::Object::Ptr Response;
 | 
			
		||||
			auto Status = ProvRequest.Do(Response);
 | 
			
		||||
			std::stringstream ooo;
 | 
			
		||||
            if(Response!= nullptr)
 | 
			
		||||
			if (Response != nullptr)
 | 
			
		||||
				Response->stringify(ooo);
 | 
			
		||||
            Logger().information(fmt::format("({}): Completed subscriber e-mail verification. Provisioning notified, Error={}.",
 | 
			
		||||
			Logger().information(fmt::format(
 | 
			
		||||
				"({}): Completed subscriber e-mail verification. Provisioning notified, Error={}.",
 | 
			
		||||
				UInfo.email, Status));
 | 
			
		||||
            SendHTMLFileBack(FormFile,FormVars);
 | 
			
		||||
            Logger().information(fmt::format("({}): Completed subscriber e-mail verification. FORM notified.",UInfo.email));
 | 
			
		||||
			AddGlobalVars(FormVars);
 | 
			
		||||
			SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
			Logger().information(fmt::format(
 | 
			
		||||
				"({}): Completed subscriber e-mail verification. FORM notified.", UInfo.email));
 | 
			
		||||
		} else {
 | 
			
		||||
			DoReturnA404();
 | 
			
		||||
		}
 | 
			
		||||
@@ -235,35 +299,39 @@ namespace OpenWifi {
 | 
			
		||||
	void RESTAPI_action_links::DoEmailVerification(SecurityObjects::ActionLink &Link) {
 | 
			
		||||
		auto now = OpenWifi::Now();
 | 
			
		||||
 | 
			
		||||
        if(now > Link.expires) {
 | 
			
		||||
		if (now > Link.expires) {
 | 
			
		||||
			StorageService()->ActionLinksDB().CancelAction(Link.id);
 | 
			
		||||
			return DoReturnA404();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::UserInfo UInfo;
 | 
			
		||||
        bool Found = Link.userAction ? StorageService()->UserDB().GetUserById(Link.userId,UInfo) : StorageService()->SubDB().GetUserById(Link.userId,UInfo);
 | 
			
		||||
		bool Found = Link.userAction ? StorageService()->UserDB().GetUserById(Link.userId, UInfo)
 | 
			
		||||
									 : StorageService()->SubDB().GetUserById(Link.userId, UInfo);
 | 
			
		||||
		if (!Found) {
 | 
			
		||||
            Types::StringPairVec FormVars{{"UUID",       Link.id},
 | 
			
		||||
			Types::StringPairVec FormVars{
 | 
			
		||||
				{"UUID", Link.id},
 | 
			
		||||
				{"ERROR_TEXT", "This does not appear to be a valid email verification link.."}};
 | 
			
		||||
			Poco::File FormFile{Daemon()->AssetDir() + "/email_verification_error.html"};
 | 
			
		||||
			AddGlobalVars(FormVars);
 | 
			
		||||
			return SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        Logger_.information(fmt::format("EMAIL-VERIFICATION(%s): For ID={}", Request->clientAddress().toString(),
 | 
			
		||||
                                        UInfo.email));
 | 
			
		||||
		Logger_.information(fmt::format("EMAIL-VERIFICATION(%s): For ID={}",
 | 
			
		||||
										Request->clientAddress().toString(), UInfo.email));
 | 
			
		||||
		UInfo.waitingForEmailCheck = false;
 | 
			
		||||
		UInfo.validated = true;
 | 
			
		||||
		UInfo.lastEmailCheck = OpenWifi::Now();
 | 
			
		||||
		UInfo.validationDate = OpenWifi::Now();
 | 
			
		||||
		UInfo.modified = OpenWifi::Now();
 | 
			
		||||
        if(Link.userAction)
 | 
			
		||||
		if (Link.userAction)
 | 
			
		||||
			StorageService()->UserDB().UpdateUserInfo(UInfo.email, Link.userId, UInfo);
 | 
			
		||||
		else
 | 
			
		||||
			StorageService()->SubDB().UpdateUserInfo(UInfo.email, Link.userId, UInfo);
 | 
			
		||||
		Types::StringPairVec FormVars{{"UUID", Link.id},
 | 
			
		||||
									  {"USERNAME", UInfo.email},
 | 
			
		||||
                                      {"ACTION_LINK",MicroService::instance().GetUIURI()}};
 | 
			
		||||
									  {"ACTION_LINK", MicroService::instance().GetUIURI()}};
 | 
			
		||||
		Poco::File FormFile{Daemon()->AssetDir() + "/email_verification_success.html"};
 | 
			
		||||
		AddGlobalVars(FormVars);
 | 
			
		||||
		StorageService()->ActionLinksDB().CompleteAction(Link.id);
 | 
			
		||||
		SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
	}
 | 
			
		||||
@@ -271,6 +339,7 @@ namespace OpenWifi {
 | 
			
		||||
	void RESTAPI_action_links::DoReturnA404() {
 | 
			
		||||
		Types::StringPairVec FormVars;
 | 
			
		||||
		Poco::File FormFile{Daemon()->AssetDir() + "/404_error.html"};
 | 
			
		||||
		AddGlobalVars(FormVars);
 | 
			
		||||
		SendHTMLFileBack(FormFile, FormVars);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -278,12 +347,10 @@ namespace OpenWifi {
 | 
			
		||||
		/// TODO:
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    void RESTAPI_action_links::RequestSubResetPassword([[maybe_unused]] SecurityObjects::ActionLink &Link) {
 | 
			
		||||
	void RESTAPI_action_links::RequestSubResetPassword(
 | 
			
		||||
		[[maybe_unused]] SecurityObjects::ActionLink &Link) {}
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
	void RESTAPI_action_links::DoSubEmailVerification(
 | 
			
		||||
		[[maybe_unused]] SecurityObjects::ActionLink &Link) {}
 | 
			
		||||
 | 
			
		||||
    void RESTAPI_action_links::DoSubEmailVerification([[maybe_unused]] SecurityObjects::ActionLink &Link) {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -9,17 +9,15 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_action_links : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_action_links(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_action_links(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							 RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							 bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
             std::vector<std::string>{
 | 
			
		||||
                                        Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                                        Server,
 | 
			
		||||
                                        TransactionId,
 | 
			
		||||
                                        Internal,
 | 
			
		||||
                                        false,
 | 
			
		||||
                                        true, RateLimit{.Interval=1000,.MaxCalls=10}) {}
 | 
			
		||||
							 Server, TransactionId, Internal, false, true,
 | 
			
		||||
							 RateLimit{.Interval = 1000, .MaxCalls = 10}) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/actionLink"}; };
 | 
			
		||||
		void RequestResetPassword(SecurityObjects::ActionLink &Link);
 | 
			
		||||
		void RequestSubResetPassword(SecurityObjects::ActionLink &Link);
 | 
			
		||||
@@ -30,10 +28,11 @@ namespace OpenWifi {
 | 
			
		||||
		void DoReturnA404();
 | 
			
		||||
		void DoNewSubVerification(SecurityObjects::ActionLink &Link);
 | 
			
		||||
		void CompleteEmailInvitation();
 | 
			
		||||
		static void AddGlobalVars(Types::StringPairVec &Vars);
 | 
			
		||||
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoPost() final;
 | 
			
		||||
        void DoDelete() final {};
 | 
			
		||||
        void DoPut() final {};
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
		void DoPut() final{};
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -8,17 +8,18 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_apiKey_handler::DoGet() {
 | 
			
		||||
        std::string     user_uuid = GetBinding("uuid","");
 | 
			
		||||
        if(user_uuid.empty()) {
 | 
			
		||||
		std::string user_uuid = GetBinding("uuid", "");
 | 
			
		||||
		if (user_uuid.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
        if(user_uuid!=UserInfo_.userinfo.id && UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) {
 | 
			
		||||
		if (user_uuid != UserInfo_.userinfo.id &&
 | 
			
		||||
			UserInfo_.userinfo.userRole != SecurityObjects::ROOT) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::ApiKeyEntryList List;
 | 
			
		||||
        if(DB_.GetRecords(0,500, List.apiKeys, fmt::format(" userUuid='{}' ", user_uuid))) {
 | 
			
		||||
            for(auto &key:List.apiKeys) {
 | 
			
		||||
		if (DB_.GetRecords(0, 500, List.apiKeys, fmt::format(" userUuid='{}' ", user_uuid))) {
 | 
			
		||||
			for (auto &key : List.apiKeys) {
 | 
			
		||||
				Sanitize(UserInfo_, key);
 | 
			
		||||
			}
 | 
			
		||||
			Poco::JSON::Object Answer;
 | 
			
		||||
@@ -29,29 +30,30 @@ namespace OpenWifi {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_apiKey_handler::DoDelete() {
 | 
			
		||||
        std::string     user_uuid = GetBinding("uuid","");
 | 
			
		||||
        if(user_uuid.empty()) {
 | 
			
		||||
		std::string user_uuid = GetBinding("uuid", "");
 | 
			
		||||
		if (user_uuid.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(user_uuid!=UserInfo_.userinfo.id && UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) {
 | 
			
		||||
		if (user_uuid != UserInfo_.userinfo.id &&
 | 
			
		||||
			UserInfo_.userinfo.userRole != SecurityObjects::ROOT) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(user_uuid!=UserInfo_.userinfo.id) {
 | 
			
		||||
            if(!StorageService()->UserDB().Exists("id",user_uuid)) {
 | 
			
		||||
		if (user_uuid != UserInfo_.userinfo.id) {
 | 
			
		||||
			if (!StorageService()->UserDB().Exists("id", user_uuid)) {
 | 
			
		||||
				return NotFound();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        std::string ApiKeyId= GetParameter("keyUuid","");
 | 
			
		||||
        if(ApiKeyId.empty()) {
 | 
			
		||||
		std::string ApiKeyId = GetParameter("keyUuid", "");
 | 
			
		||||
		if (ApiKeyId.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::ApiKeyEntry ApiKey;
 | 
			
		||||
        if(StorageService()->ApiKeyDB().GetRecord("id",ApiKeyId,ApiKey)) {
 | 
			
		||||
            if(ApiKey.userUuid==user_uuid) {
 | 
			
		||||
		if (StorageService()->ApiKeyDB().GetRecord("id", ApiKeyId, ApiKey)) {
 | 
			
		||||
			if (ApiKey.userUuid == user_uuid) {
 | 
			
		||||
				AuthService()->RemoveTokenSystemWide(ApiKey.apiKey);
 | 
			
		||||
				DB_.DeleteRecord("id", ApiKeyId);
 | 
			
		||||
				return OK();
 | 
			
		||||
@@ -62,60 +64,64 @@ namespace OpenWifi {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_apiKey_handler::DoPost() {
 | 
			
		||||
        std::string     user_uuid = GetBinding("uuid","");
 | 
			
		||||
		std::string user_uuid = GetBinding("uuid", "");
 | 
			
		||||
 | 
			
		||||
        if(user_uuid.empty()) {
 | 
			
		||||
		if (user_uuid.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(user_uuid!=UserInfo_.userinfo.id && UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) {
 | 
			
		||||
		if (user_uuid != UserInfo_.userinfo.id &&
 | 
			
		||||
			UserInfo_.userinfo.userRole != SecurityObjects::ROOT) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(user_uuid!=UserInfo_.userinfo.id) {
 | 
			
		||||
		if (user_uuid != UserInfo_.userinfo.id) {
 | 
			
		||||
			//  Must verify if the user exists
 | 
			
		||||
            if(!StorageService()->UserDB().Exists("id",user_uuid)) {
 | 
			
		||||
			if (!StorageService()->UserDB().Exists("id", user_uuid)) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::UserMustExist);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::ApiKeyEntry NewKey;
 | 
			
		||||
        if(!NewKey.from_json(ParsedBody_)) {
 | 
			
		||||
		if (!NewKey.from_json(ParsedBody_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
 | 
			
		||||
		}
 | 
			
		||||
        NewKey.lastUse = 0 ;
 | 
			
		||||
		NewKey.lastUse = 0;
 | 
			
		||||
 | 
			
		||||
        if(!Utils::IsAlphaNumeric(NewKey.name) || NewKey.name.empty()) {
 | 
			
		||||
		if (!Utils::IsAlphaNumeric(NewKey.name) || NewKey.name.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Poco::toLowerInPlace(NewKey.name);
 | 
			
		||||
		NewKey.userUuid = user_uuid;
 | 
			
		||||
        if(NewKey.expiresOn < Utils::Now()) {
 | 
			
		||||
		if (NewKey.expiresOn < Utils::Now()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//  does a key of that name already exit for this user?
 | 
			
		||||
		SecurityObjects::ApiKeyEntryList ExistingList;
 | 
			
		||||
        if(DB_.GetRecords(0,500, ExistingList.apiKeys, fmt::format(" userUuid='{}' ", user_uuid))) {
 | 
			
		||||
            if(std::find_if(ExistingList.apiKeys.begin(),ExistingList.apiKeys.end(), [NewKey](const SecurityObjects::ApiKeyEntry &E) -> bool {
 | 
			
		||||
                return E.name==NewKey.name;
 | 
			
		||||
            })!=ExistingList.apiKeys.end()) {
 | 
			
		||||
		if (DB_.GetRecords(0, 500, ExistingList.apiKeys,
 | 
			
		||||
						   fmt::format(" userUuid='{}' ", user_uuid))) {
 | 
			
		||||
			if (std::find_if(ExistingList.apiKeys.begin(), ExistingList.apiKeys.end(),
 | 
			
		||||
							 [NewKey](const SecurityObjects::ApiKeyEntry &E) -> bool {
 | 
			
		||||
								 return E.name == NewKey.name;
 | 
			
		||||
							 }) != ExistingList.apiKeys.end()) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::ApiKeyNameAlreadyExists);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(ExistingList.apiKeys.size()>=10) {
 | 
			
		||||
		if (ExistingList.apiKeys.size() >= 10) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::TooManyApiKeys);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		NewKey.id = MicroServiceCreateUUID();
 | 
			
		||||
		NewKey.userUuid = user_uuid;
 | 
			
		||||
		NewKey.salt = std::to_string(Utils::Now());
 | 
			
		||||
        NewKey.apiKey = Utils::ComputeHash(NewKey.salt, UserInfo_.userinfo.id, UserInfo_.webtoken.access_token_ );
 | 
			
		||||
		NewKey.apiKey = Utils::ComputeHash(NewKey.salt, UserInfo_.userinfo.id,
 | 
			
		||||
										   UserInfo_.webtoken.access_token_);
 | 
			
		||||
		NewKey.created = Utils::Now();
 | 
			
		||||
 | 
			
		||||
        if(DB_.CreateRecord(NewKey)) {
 | 
			
		||||
		if (DB_.CreateRecord(NewKey)) {
 | 
			
		||||
			Poco::JSON::Object Answer;
 | 
			
		||||
			NewKey.to_json(Answer);
 | 
			
		||||
			return ReturnObject(Answer);
 | 
			
		||||
@@ -124,30 +130,31 @@ namespace OpenWifi {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_apiKey_handler::DoPut() {
 | 
			
		||||
        std::string     user_uuid = GetBinding("uuid","");
 | 
			
		||||
        if(user_uuid.empty()) {
 | 
			
		||||
		std::string user_uuid = GetBinding("uuid", "");
 | 
			
		||||
		if (user_uuid.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
        if(user_uuid!=UserInfo_.userinfo.id && UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) {
 | 
			
		||||
		if (user_uuid != UserInfo_.userinfo.id &&
 | 
			
		||||
			UserInfo_.userinfo.userRole != SecurityObjects::ROOT) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
		SecurityObjects::ApiKeyEntry NewKey;
 | 
			
		||||
        if(!NewKey.from_json(ParsedBody_)) {
 | 
			
		||||
		if (!NewKey.from_json(ParsedBody_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::ApiKeyEntry ExistingKey;
 | 
			
		||||
        if(!DB_.GetRecord("id",NewKey.id,ExistingKey)) {
 | 
			
		||||
		if (!DB_.GetRecord("id", NewKey.id, ExistingKey)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::ApiKeyDoesNotExist);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(ExistingKey.userUuid!=user_uuid) {
 | 
			
		||||
		if (ExistingKey.userUuid != user_uuid) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingUserID);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        AssignIfPresent(ParsedBody_,"description",ExistingKey.description);
 | 
			
		||||
		AssignIfPresent(ParsedBody_, "description", ExistingKey.description);
 | 
			
		||||
 | 
			
		||||
        if(DB_.UpdateRecord("id",ExistingKey.id,ExistingKey)) {
 | 
			
		||||
		if (DB_.UpdateRecord("id", ExistingKey.id, ExistingKey)) {
 | 
			
		||||
			Poco::JSON::Object Answer;
 | 
			
		||||
			ExistingKey.to_json(Answer);
 | 
			
		||||
			return ReturnObject(Answer);
 | 
			
		||||
@@ -155,4 +162,4 @@ namespace OpenWifi {
 | 
			
		||||
		BadRequest(RESTAPI::Errors::RecordNotUpdated);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -4,31 +4,29 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "framework/RESTAPI_Handler.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "framework/RESTAPI_Handler.h"
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_apiKey_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_apiKey_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_apiKey_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							   RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							   bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
                                 std::vector<std::string>{
 | 
			
		||||
                                         Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_PUT,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_DELETE,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                                 Server,
 | 
			
		||||
                                 TransactionId,
 | 
			
		||||
                                 Internal) {}
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/apiKey/{uuid}"}; };
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
        ApiKeyDB     &DB_=StorageService()->ApiKeyDB();
 | 
			
		||||
		ApiKeyDB &DB_ = StorageService()->ApiKeyDB();
 | 
			
		||||
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoPut() final;
 | 
			
		||||
		void DoPost() final;
 | 
			
		||||
		void DoDelete() final;
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -3,23 +3,23 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_asset_server.h"
 | 
			
		||||
#include "Daemon.h"
 | 
			
		||||
#include "Poco/File.h"
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
#include "Daemon.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	void RESTAPI_asset_server::DoGet() {
 | 
			
		||||
		Poco::File AssetFile;
 | 
			
		||||
 | 
			
		||||
        if(Request->getURI().find("/favicon.ico") != std::string::npos) {
 | 
			
		||||
		if (Request->getURI().find("/favicon.ico") != std::string::npos) {
 | 
			
		||||
			AssetFile = Daemon()->AssetDir() + "/favicon.ico";
 | 
			
		||||
		} else {
 | 
			
		||||
			std::string AssetName = GetBinding(RESTAPI::Protocol::ID, "");
 | 
			
		||||
			AssetFile = Daemon()->AssetDir() + "/" + AssetName;
 | 
			
		||||
		}
 | 
			
		||||
        if(!AssetFile.isFile()) {
 | 
			
		||||
		if (!AssetFile.isFile()) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
		SendFile(AssetFile);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -9,26 +9,24 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_asset_server : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_asset_server(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_asset_server(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							 RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							 bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
                                 std::vector<std::string>
 | 
			
		||||
                                         {Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_PUT,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_DELETE,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                                          Server,
 | 
			
		||||
                                          TransactionId,
 | 
			
		||||
                                          Internal, false) {}
 | 
			
		||||
        static auto PathName() { return std::list<std::string>{"/wwwassets/{id}" ,
 | 
			
		||||
                                                                                         "/favicon.ico"}; };
 | 
			
		||||
							 Server, TransactionId, Internal, false) {}
 | 
			
		||||
		static auto PathName() {
 | 
			
		||||
			return std::list<std::string>{"/wwwassets/{id}", "/favicon.ico"};
 | 
			
		||||
		};
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
        void DoPost() final {};
 | 
			
		||||
        void DoDelete() final {};
 | 
			
		||||
        void DoPut() final {};
 | 
			
		||||
		void DoPost() final{};
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
		void DoPut() final{};
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -5,20 +5,22 @@
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
#include "Poco/CountingStream.h"
 | 
			
		||||
#include "Poco/Net/HTMLForm.h"
 | 
			
		||||
#include "RESTAPI_avatar_handler.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "Poco/Net/HTMLForm.h"
 | 
			
		||||
#include "Poco/CountingStream.h"
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
    void AvatarPartHandler::handlePart(const Poco::Net::MessageHeader &Header, std::istream &Stream) {
 | 
			
		||||
	void AvatarPartHandler::handlePart(const Poco::Net::MessageHeader &Header,
 | 
			
		||||
									   std::istream &Stream) {
 | 
			
		||||
		FileType_ = Header.get(RESTAPI::Protocol::CONTENTTYPE, RESTAPI::Protocol::UNSPECIFIED);
 | 
			
		||||
		if (Header.has(RESTAPI::Protocol::CONTENTDISPOSITION)) {
 | 
			
		||||
			std::string Disposition;
 | 
			
		||||
			Poco::Net::NameValueCollection Parameters;
 | 
			
		||||
            Poco::Net::MessageHeader::splitParameters(Header[RESTAPI::Protocol::CONTENTDISPOSITION], Disposition, Parameters);
 | 
			
		||||
			Poco::Net::MessageHeader::splitParameters(Header[RESTAPI::Protocol::CONTENTDISPOSITION],
 | 
			
		||||
													  Disposition, Parameters);
 | 
			
		||||
			Name_ = Parameters.get(RESTAPI::Protocol::NAME, RESTAPI::Protocol::UNNAMED);
 | 
			
		||||
		}
 | 
			
		||||
		Poco::CountingInputStream InputStream(Stream);
 | 
			
		||||
@@ -35,14 +37,16 @@ namespace OpenWifi {
 | 
			
		||||
		Poco::Net::HTMLForm form(*Request, Request->stream(), partHandler);
 | 
			
		||||
		Poco::JSON::Object Answer;
 | 
			
		||||
 | 
			
		||||
        if (!partHandler.Name().empty() && partHandler.Length()< MicroServiceConfigGetInt("openwifi.avatar.maxsize",2000000)) {
 | 
			
		||||
		if (!partHandler.Name().empty() &&
 | 
			
		||||
			partHandler.Length() < MicroServiceConfigGetInt("openwifi.avatar.maxsize", 2000000)) {
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::AVATARID, Id);
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::ERRORCODE, 0);
 | 
			
		||||
            Logger_.information(fmt::format("Uploaded avatar: {} Type: {}", partHandler.Name(), partHandler.ContentType()));
 | 
			
		||||
            StorageService()->AvatarDB().SetAvatar(UserInfo_.userinfo.email,
 | 
			
		||||
                                 Id, SS.str(), partHandler.ContentType(), partHandler.Name());
 | 
			
		||||
            StorageService()->UserDB().SetAvatar(Id,"1");
 | 
			
		||||
            Logger().information(fmt::format("Adding avatar for {}",UserInfo_.userinfo.email));
 | 
			
		||||
			Logger_.information(fmt::format("Uploaded avatar: {} Type: {}", partHandler.Name(),
 | 
			
		||||
											partHandler.ContentType()));
 | 
			
		||||
			StorageService()->AvatarDB().SetAvatar(UserInfo_.userinfo.email, Id, SS.str(),
 | 
			
		||||
												   partHandler.ContentType(), partHandler.Name());
 | 
			
		||||
			StorageService()->UserDB().SetAvatar(Id, "1");
 | 
			
		||||
			Logger().information(fmt::format("Adding avatar for {}", UserInfo_.userinfo.email));
 | 
			
		||||
		} else {
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::AVATARID, Id);
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::ERRORCODE, 13);
 | 
			
		||||
@@ -58,17 +62,19 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::string Type, Name, AvatarContent;
 | 
			
		||||
        if (!StorageService()->AvatarDB().GetAvatar(UserInfo_.userinfo.email, Id, AvatarContent, Type, Name)) {
 | 
			
		||||
		if (!StorageService()->AvatarDB().GetAvatar(UserInfo_.userinfo.email, Id, AvatarContent,
 | 
			
		||||
													Type, Name)) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
        Logger().information(fmt::format("Retrieving avatar for {}, size:{}",UserInfo_.userinfo.email,AvatarContent.size()));
 | 
			
		||||
		Logger().information(fmt::format("Retrieving avatar for {}, size:{}",
 | 
			
		||||
										 UserInfo_.userinfo.email, AvatarContent.size()));
 | 
			
		||||
		return SendFileContent(AvatarContent, Type, Name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_avatar_handler::DoDelete() {
 | 
			
		||||
		std::string Id = GetBinding(RESTAPI::Protocol::ID, "");
 | 
			
		||||
 | 
			
		||||
        if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && Id!=UserInfo_.userinfo.id) {
 | 
			
		||||
		if (UserInfo_.userinfo.userRole != SecurityObjects::ROOT && Id != UserInfo_.userinfo.id) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -76,8 +82,8 @@ namespace OpenWifi {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        Logger().information(fmt::format("Deleted avatar for {}",UserInfo_.userinfo.email));
 | 
			
		||||
        StorageService()->UserDB().SetAvatar(Id,"");
 | 
			
		||||
		Logger().information(fmt::format("Deleted avatar for {}", UserInfo_.userinfo.email));
 | 
			
		||||
		StorageService()->UserDB().SetAvatar(Id, "");
 | 
			
		||||
		OK();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -3,18 +3,15 @@
 | 
			
		||||
//
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "framework/RESTAPI_Handler.h"
 | 
			
		||||
#include "Poco/Net/PartHandler.h"
 | 
			
		||||
#include "framework/RESTAPI_Handler.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	class AvatarPartHandler : public Poco::Net::PartHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        AvatarPartHandler(std::string Id, Poco::Logger &Logger, std::stringstream & ofs) :
 | 
			
		||||
                Id_(std::move(Id)),
 | 
			
		||||
                Logger_(Logger),
 | 
			
		||||
                OutputStream_(ofs){
 | 
			
		||||
        }
 | 
			
		||||
		AvatarPartHandler(std::string Id, Poco::Logger &Logger, std::stringstream &ofs)
 | 
			
		||||
			: Id_(std::move(Id)), Logger_(Logger), OutputStream_(ofs) {}
 | 
			
		||||
		void handlePart(const Poco::Net::MessageHeader &Header, std::istream &Stream);
 | 
			
		||||
		[[nodiscard]] uint64_t Length() const { return Length_; }
 | 
			
		||||
		[[nodiscard]] std::string &Name() { return Name_; }
 | 
			
		||||
@@ -28,26 +25,25 @@ namespace OpenWifi {
 | 
			
		||||
		Poco::Logger &Logger_;
 | 
			
		||||
		std::stringstream &OutputStream_;
 | 
			
		||||
 | 
			
		||||
        inline Poco::Logger & Logger() { return Logger_; };
 | 
			
		||||
		inline Poco::Logger &Logger() { return Logger_; };
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class RESTAPI_avatar_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_avatar_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_avatar_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							   RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							   bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
                                 std::vector<std::string>{
 | 
			
		||||
                                         Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_DELETE,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                                         Server,
 | 
			
		||||
                                         TransactionId,
 | 
			
		||||
                                         Internal) {}
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/avatar/{id}"}; };
 | 
			
		||||
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoPost() final;
 | 
			
		||||
		void DoDelete() final;
 | 
			
		||||
        void DoPut() final {};
 | 
			
		||||
		void DoPut() final{};
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -8,13 +8,15 @@
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
    inline void Sanitize([[maybe_unused]] const SecurityObjects::UserInfoAndPolicy &User, SecurityObjects::UserInfo & U) {
 | 
			
		||||
	inline void Sanitize([[maybe_unused]] const SecurityObjects::UserInfoAndPolicy &User,
 | 
			
		||||
						 SecurityObjects::UserInfo &U) {
 | 
			
		||||
		U.currentPassword.clear();
 | 
			
		||||
		U.lastPasswords.clear();
 | 
			
		||||
		U.oauthType.clear();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    inline void Sanitize([[maybe_unused]] const SecurityObjects::UserInfoAndPolicy &User, SecurityObjects::ApiKeyEntry & U) {
 | 
			
		||||
	inline void Sanitize([[maybe_unused]] const SecurityObjects::UserInfoAndPolicy &User,
 | 
			
		||||
						 SecurityObjects::ApiKeyEntry &U) {
 | 
			
		||||
		U.salt.clear();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -10,11 +10,8 @@
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	void RESTAPI_email_handler::DoPost() {
 | 
			
		||||
        const auto & Obj = ParsedBody_;
 | 
			
		||||
        if (Obj->has("subject") &&
 | 
			
		||||
            Obj->has("from") &&
 | 
			
		||||
            Obj->has("text") &&
 | 
			
		||||
            Obj->has("recipients") &&
 | 
			
		||||
		const auto &Obj = ParsedBody_;
 | 
			
		||||
		if (Obj->has("subject") && Obj->has("from") && Obj->has("text") && Obj->has("recipients") &&
 | 
			
		||||
			Obj->isArray("recipients")) {
 | 
			
		||||
 | 
			
		||||
			Poco::JSON::Array::Ptr Recipients = Obj->getArray("recipients");
 | 
			
		||||
@@ -24,11 +21,11 @@ namespace OpenWifi {
 | 
			
		||||
			Attrs[SUBJECT] = Obj->get("subject").toString();
 | 
			
		||||
			Attrs[TEXT] = Obj->get("text").toString();
 | 
			
		||||
			Attrs[SENDER] = Obj->get("from").toString();
 | 
			
		||||
            if(SMTPMailerService()->SendMessage(Recipient, "password_reset.txt", Attrs, false)) {
 | 
			
		||||
			if (SMTPMailerService()->SendMessage(Recipient, "password_reset.txt", Attrs, false)) {
 | 
			
		||||
				return OK();
 | 
			
		||||
			}
 | 
			
		||||
			return ReturnStatus(Poco::Net::HTTPResponse::HTTP_SERVICE_UNAVAILABLE);
 | 
			
		||||
		}
 | 
			
		||||
		BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -9,17 +9,17 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_email_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_email_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_email_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							  RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							  bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                                                  Server,
 | 
			
		||||
                                                  TransactionId,
 | 
			
		||||
                                                  Internal) {}
 | 
			
		||||
        static auto PathName() { return std::list<std::string>{"/api/v1/email"};}
 | 
			
		||||
        void DoGet() final {};
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/email"}; }
 | 
			
		||||
		void DoGet() final{};
 | 
			
		||||
		void DoPost() final;
 | 
			
		||||
        void DoDelete() final {};
 | 
			
		||||
        void DoPut() final {};
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
		void DoPut() final{};
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -9,13 +9,12 @@
 | 
			
		||||
#include "Poco/JSON/Parser.h"
 | 
			
		||||
 | 
			
		||||
#include "AuthService.h"
 | 
			
		||||
#include "RESTAPI_oauth2_handler.h"
 | 
			
		||||
#include "MFAServer.h"
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "RESTAPI_db_helpers.h"
 | 
			
		||||
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
#include "RESTAPI_oauth2_handler.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "framework/MicroService.h"
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
@@ -27,7 +26,8 @@ namespace OpenWifi {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::INVALID_TOKEN);
 | 
			
		||||
		}
 | 
			
		||||
		if (GetBoolParameter(RESTAPI::Protocol::ME)) {
 | 
			
		||||
            Logger_.information(fmt::format("REQUEST-ME({}): Request for {}", Request->clientAddress().toString(),
 | 
			
		||||
			Logger_.information(fmt::format("REQUEST-ME({}): Request for {}",
 | 
			
		||||
											Request->clientAddress().toString(),
 | 
			
		||||
											UserInfo_.userinfo.email));
 | 
			
		||||
			Poco::JSON::Object Me;
 | 
			
		||||
			SecurityObjects::UserInfo ReturnedUser = UserInfo_.userinfo;
 | 
			
		||||
@@ -59,8 +59,8 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_oauth2_handler::DoPost() {
 | 
			
		||||
 | 
			
		||||
        const auto & Obj = ParsedBody_;
 | 
			
		||||
        if(Obj == nullptr) {
 | 
			
		||||
		const auto &Obj = ParsedBody_;
 | 
			
		||||
		if (Obj == nullptr) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -72,9 +72,9 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		Poco::toLowerInPlace(userId);
 | 
			
		||||
 | 
			
		||||
        if(!refreshToken.empty() && grant_type == "refresh_token") {
 | 
			
		||||
		if (!refreshToken.empty() && grant_type == "refresh_token") {
 | 
			
		||||
			SecurityObjects::UserInfoAndPolicy UInfo;
 | 
			
		||||
            if(AuthService()->RefreshUserToken(*Request, refreshToken, UInfo)) {
 | 
			
		||||
			if (AuthService()->RefreshUserToken(*Request, refreshToken, UInfo)) {
 | 
			
		||||
				Poco::JSON::Object Answer;
 | 
			
		||||
				UInfo.webtoken.to_json(Answer);
 | 
			
		||||
				return ReturnObject(Answer);
 | 
			
		||||
@@ -83,27 +83,30 @@ namespace OpenWifi {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter(RESTAPI::Protocol::REQUIREMENTS)) {
 | 
			
		||||
            Logger_.information(fmt::format("POLICY-REQUEST({}): Request.", Request->clientAddress().toString()));
 | 
			
		||||
		if (GetBoolParameter(RESTAPI::Protocol::REQUIREMENTS)) {
 | 
			
		||||
			Logger_.information(
 | 
			
		||||
				fmt::format("POLICY-REQUEST({}): Request.", Request->clientAddress().toString()));
 | 
			
		||||
			Poco::JSON::Object Answer;
 | 
			
		||||
            Answer.set(RESTAPI::Protocol::PASSWORDPATTERN, AuthService()->PasswordValidationExpression());
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::PASSWORDPATTERN,
 | 
			
		||||
					   AuthService()->PasswordValidationExpression());
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::ACCESSPOLICY, AuthService()->GetAccessPolicy());
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::PASSWORDPOLICY, AuthService()->GetPasswordPolicy());
 | 
			
		||||
			return ReturnObject(Answer);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter(RESTAPI::Protocol::FORGOTPASSWORD)) {
 | 
			
		||||
		if (GetBoolParameter(RESTAPI::Protocol::FORGOTPASSWORD)) {
 | 
			
		||||
			SecurityObjects::UserInfo UInfo1;
 | 
			
		||||
            auto UserExists = StorageService()->UserDB().GetUserByEmail(userId,UInfo1);
 | 
			
		||||
            if(UserExists) {
 | 
			
		||||
                Logger_.information(fmt::format("FORGOTTEN-PASSWORD({}): Request for {}", Request->clientAddress().toString(), userId));
 | 
			
		||||
			auto UserExists = StorageService()->UserDB().GetUserByEmail(userId, UInfo1);
 | 
			
		||||
			if (UserExists) {
 | 
			
		||||
				Logger_.information(fmt::format("FORGOTTEN-PASSWORD({}): Request for {}",
 | 
			
		||||
												Request->clientAddress().toString(), userId));
 | 
			
		||||
				SecurityObjects::ActionLink NewLink;
 | 
			
		||||
 | 
			
		||||
				NewLink.action = OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD;
 | 
			
		||||
                NewLink.id = MicroServiceCreateUUID();
 | 
			
		||||
				NewLink.id = MicroService::CreateUUID();
 | 
			
		||||
				NewLink.userId = UInfo1.id;
 | 
			
		||||
				NewLink.created = OpenWifi::Now();
 | 
			
		||||
                NewLink.expires = NewLink.created + (24*60*60);
 | 
			
		||||
				NewLink.expires = NewLink.created + (24 * 60 * 60);
 | 
			
		||||
				NewLink.userAction = true;
 | 
			
		||||
				StorageService()->ActionLinksDB().CreateAction(NewLink);
 | 
			
		||||
 | 
			
		||||
@@ -121,21 +124,23 @@ namespace OpenWifi {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter(RESTAPI::Protocol::RESENDMFACODE)) {
 | 
			
		||||
            Logger_.information(fmt::format("RESEND-MFA-CODE({}): Request for {}", Request->clientAddress().toString(), userId));
 | 
			
		||||
            if(Obj->has("uuid")) {
 | 
			
		||||
		if (GetBoolParameter(RESTAPI::Protocol::RESENDMFACODE)) {
 | 
			
		||||
			Logger_.information(fmt::format("RESEND-MFA-CODE({}): Request for {}",
 | 
			
		||||
											Request->clientAddress().toString(), userId));
 | 
			
		||||
			if (Obj->has("uuid")) {
 | 
			
		||||
				auto uuid = Obj->get("uuid").toString();
 | 
			
		||||
                if(MFAServer()->ResendCode(uuid))
 | 
			
		||||
				if (MFAServer()->ResendCode(uuid))
 | 
			
		||||
					return OK();
 | 
			
		||||
			}
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::BAD_MFA_TRANSACTION);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter(RESTAPI::Protocol::COMPLETEMFACHALLENGE,false)) {
 | 
			
		||||
            Logger_.information(fmt::format("COMPLETE-MFA-CHALLENGE({}): Request for {}", Request->clientAddress().toString(), userId));
 | 
			
		||||
            if(Obj->has("uuid")) {
 | 
			
		||||
		if (GetBoolParameter(RESTAPI::Protocol::COMPLETEMFACHALLENGE, false)) {
 | 
			
		||||
			Logger_.information(fmt::format("COMPLETE-MFA-CHALLENGE({}): Request for {}",
 | 
			
		||||
											Request->clientAddress().toString(), userId));
 | 
			
		||||
			if (Obj->has("uuid")) {
 | 
			
		||||
				SecurityObjects::UserInfoAndPolicy UInfo;
 | 
			
		||||
                if(MFAServer()->CompleteMFAChallenge(Obj,UInfo)) {
 | 
			
		||||
				if (MFAServer()->CompleteMFAChallenge(Obj, UInfo)) {
 | 
			
		||||
					Poco::JSON::Object ReturnObj;
 | 
			
		||||
					UInfo.webtoken.to_json(ReturnObj);
 | 
			
		||||
					return ReturnObject(ReturnObj);
 | 
			
		||||
@@ -145,21 +150,21 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::UserInfoAndPolicy UInfo;
 | 
			
		||||
        bool Expired=false;
 | 
			
		||||
        auto Code=AuthService()->Authorize(userId, password, newPassword, UInfo, Expired);
 | 
			
		||||
        if (Code==SUCCESS) {
 | 
			
		||||
		bool Expired = false;
 | 
			
		||||
		auto Code = AuthService()->Authorize(userId, password, newPassword, UInfo, Expired);
 | 
			
		||||
		switch (Code) {
 | 
			
		||||
		case SUCCESS: {
 | 
			
		||||
			Poco::JSON::Object ReturnObj;
 | 
			
		||||
            if(AuthService()->RequiresMFA(UInfo)) {
 | 
			
		||||
                if(MFAServer()->StartMFAChallenge(UInfo, ReturnObj)) {
 | 
			
		||||
			if (AuthService()->RequiresMFA(UInfo)) {
 | 
			
		||||
				if (MFAServer()->StartMFAChallenge(UInfo, ReturnObj)) {
 | 
			
		||||
					return ReturnObject(ReturnObj);
 | 
			
		||||
				}
 | 
			
		||||
                Logger_.warning("MFA Seems to be broken. Please fix. Disabling MFA checking for now.");
 | 
			
		||||
				Logger_.warning(
 | 
			
		||||
					"MFA Seems to be broken. Please fix. Disabling MFA checking for now.");
 | 
			
		||||
			}
 | 
			
		||||
			UInfo.webtoken.to_json(ReturnObj);
 | 
			
		||||
			return ReturnObject(ReturnObj);
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            switch(Code) {
 | 
			
		||||
		}
 | 
			
		||||
		case INVALID_CREDENTIALS:
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::INVALID_CREDENTIALS);
 | 
			
		||||
		case PASSWORD_INVALID:
 | 
			
		||||
@@ -170,10 +175,10 @@ namespace OpenWifi {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::USERNAME_PENDING_VERIFICATION);
 | 
			
		||||
		case PASSWORD_CHANGE_REQUIRED:
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::PASSWORD_CHANGE_REQUIRED);
 | 
			
		||||
		case ACCOUNT_SUSPENDED:
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCOUNT_SUSPENDED);
 | 
			
		||||
		default:
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::INVALID_CREDENTIALS);
 | 
			
		||||
		}
 | 
			
		||||
            return;
 | 
			
		||||
	}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -12,21 +12,22 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_oauth2_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
	    RESTAPI_oauth2_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_oauth2_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							   RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							   bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_DELETE,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
													  Server,
 | 
			
		||||
                                                      TransactionId,
 | 
			
		||||
													  Internal, false, true , RateLimit{.Interval=1000,.MaxCalls=10}) {}
 | 
			
		||||
        static auto PathName() { return std::list<std::string>{"/api/v1/oauth2/{token}","/api/v1/oauth2"}; };
 | 
			
		||||
							 Server, TransactionId, Internal, false, true,
 | 
			
		||||
							 RateLimit{.Interval = 1000, .MaxCalls = 10}) {}
 | 
			
		||||
		static auto PathName() {
 | 
			
		||||
			return std::list<std::string>{"/api/v1/oauth2/{token}", "/api/v1/oauth2"};
 | 
			
		||||
		};
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoPost() final;
 | 
			
		||||
		void DoDelete() final;
 | 
			
		||||
		void DoPut() final {};
 | 
			
		||||
		void DoPut() final{};
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -19,8 +19,8 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::Preferences P;
 | 
			
		||||
 | 
			
		||||
        const auto & RawObject = ParsedBody_;
 | 
			
		||||
        if(!P.from_json(RawObject)) {
 | 
			
		||||
		const auto &RawObject = ParsedBody_;
 | 
			
		||||
		if (!P.from_json(RawObject)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -33,4 +33,4 @@ namespace OpenWifi {
 | 
			
		||||
		ReturnObject(Answer);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -9,19 +9,18 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_preferences : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_preferences(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_preferences(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
                         std::vector<std::string>{
 | 
			
		||||
            Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_PUT,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
            Server,
 | 
			
		||||
            TransactionId,
 | 
			
		||||
            Internal) {}
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/preferences"}; };
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoPut() final;
 | 
			
		||||
        void DoPost() final {};
 | 
			
		||||
        void DoDelete() final {};
 | 
			
		||||
		void DoPost() final{};
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -1,95 +1,67 @@
 | 
			
		||||
//
 | 
			
		||||
 //
 | 
			
		||||
// Created by stephane bourque on 2021-10-23.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI/RESTAPI_oauth2_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_user_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_users_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_action_links.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_system_endpoints_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_apiKey_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_asset_server.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_avatar_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_subavatar_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_email_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_sms_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_validate_token_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_oauth2_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_preferences.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_subpreferences.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_signup_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_sms_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_subavatar_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_submfa_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_suboauth2_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_subpreferences.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_subtotp_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_subuser_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_subusers_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_validate_sub_token_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_submfa_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_system_endpoints_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_totp_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_subtotp_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_signup_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_apiKey_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_user_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_users_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_validate_apikey.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_validate_sub_token_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_validate_token_handler.h"
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_systemSecret_handler.h"
 | 
			
		||||
#include "framework/RESTAPI_SystemCommand.h"
 | 
			
		||||
#include "framework/RESTAPI_WebSocketServer.h"
 | 
			
		||||
#include "framework/RESTAPI_SystemConfiguration.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
    Poco::Net::HTTPRequestHandler * RESTAPI_ExtRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings,
 | 
			
		||||
                                                            Poco::Logger & L, RESTAPI_GenericServerAccounting & S,
 | 
			
		||||
                                                            uint64_t TransactionId) {
 | 
			
		||||
	Poco::Net::HTTPRequestHandler *
 | 
			
		||||
	RESTAPI_ExtRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings,
 | 
			
		||||
					  Poco::Logger &L, RESTAPI_GenericServerAccounting &S, uint64_t TransactionId) {
 | 
			
		||||
		return RESTAPI_Router<
 | 
			
		||||
            RESTAPI_oauth2_handler,
 | 
			
		||||
            RESTAPI_user_handler,
 | 
			
		||||
            RESTAPI_users_handler,
 | 
			
		||||
            RESTAPI_system_command,
 | 
			
		||||
            RESTAPI_asset_server,
 | 
			
		||||
            RESTAPI_system_endpoints_handler,
 | 
			
		||||
            RESTAPI_action_links,
 | 
			
		||||
            RESTAPI_avatar_handler,
 | 
			
		||||
            RESTAPI_subavatar_handler,
 | 
			
		||||
            RESTAPI_email_handler,
 | 
			
		||||
            RESTAPI_sms_handler,
 | 
			
		||||
            RESTAPI_preferences,
 | 
			
		||||
            RESTAPI_subpreferences,
 | 
			
		||||
            RESTAPI_suboauth2_handler,
 | 
			
		||||
            RESTAPI_subuser_handler,
 | 
			
		||||
            RESTAPI_subusers_handler,
 | 
			
		||||
            RESTAPI_submfa_handler,
 | 
			
		||||
            RESTAPI_totp_handler,
 | 
			
		||||
            RESTAPI_subtotp_handler,
 | 
			
		||||
            RESTAPI_signup_handler,
 | 
			
		||||
            RESTAPI_validate_sub_token_handler,
 | 
			
		||||
            RESTAPI_validate_token_handler,
 | 
			
		||||
            RESTAPI_validate_apikey,
 | 
			
		||||
            RESTAPI_webSocketServer,
 | 
			
		||||
            RESTAPI_apiKey_handler
 | 
			
		||||
        >(Path, Bindings, L, S,TransactionId);
 | 
			
		||||
			RESTAPI_oauth2_handler, RESTAPI_user_handler, RESTAPI_users_handler,
 | 
			
		||||
			RESTAPI_system_command, RESTAPI_system_configuration, RESTAPI_asset_server, RESTAPI_system_endpoints_handler,
 | 
			
		||||
			RESTAPI_action_links, RESTAPI_avatar_handler, RESTAPI_subavatar_handler,
 | 
			
		||||
			RESTAPI_email_handler, RESTAPI_sms_handler, RESTAPI_preferences, RESTAPI_subpreferences,
 | 
			
		||||
			RESTAPI_suboauth2_handler, RESTAPI_subuser_handler, RESTAPI_subusers_handler,
 | 
			
		||||
			RESTAPI_submfa_handler, RESTAPI_totp_handler, RESTAPI_subtotp_handler,
 | 
			
		||||
			RESTAPI_signup_handler, RESTAPI_validate_sub_token_handler,
 | 
			
		||||
			RESTAPI_validate_token_handler, RESTAPI_validate_apikey, RESTAPI_webSocketServer,
 | 
			
		||||
			RESTAPI_apiKey_handler, RESTAPI_systemSecret_handler>(Path, Bindings, L, S,
 | 
			
		||||
																  TransactionId);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    Poco::Net::HTTPRequestHandler * RESTAPI_IntRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings,
 | 
			
		||||
                                                            Poco::Logger & L, RESTAPI_GenericServerAccounting & S, uint64_t TransactionId) {
 | 
			
		||||
	Poco::Net::HTTPRequestHandler *
 | 
			
		||||
	RESTAPI_IntRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings,
 | 
			
		||||
					  Poco::Logger &L, RESTAPI_GenericServerAccounting &S, uint64_t TransactionId) {
 | 
			
		||||
 | 
			
		||||
		return RESTAPI_Router_I<
 | 
			
		||||
            RESTAPI_oauth2_handler,
 | 
			
		||||
            RESTAPI_user_handler,
 | 
			
		||||
            RESTAPI_users_handler,
 | 
			
		||||
            RESTAPI_system_command,
 | 
			
		||||
            RESTAPI_asset_server,
 | 
			
		||||
            RESTAPI_system_endpoints_handler,
 | 
			
		||||
            RESTAPI_action_links,
 | 
			
		||||
            RESTAPI_avatar_handler,
 | 
			
		||||
            RESTAPI_subavatar_handler,
 | 
			
		||||
            RESTAPI_email_handler,
 | 
			
		||||
            RESTAPI_sms_handler,
 | 
			
		||||
            RESTAPI_preferences,
 | 
			
		||||
            RESTAPI_subpreferences,
 | 
			
		||||
            RESTAPI_suboauth2_handler,
 | 
			
		||||
            RESTAPI_subuser_handler,
 | 
			
		||||
            RESTAPI_subusers_handler,
 | 
			
		||||
            RESTAPI_submfa_handler,
 | 
			
		||||
            RESTAPI_totp_handler,
 | 
			
		||||
            RESTAPI_subtotp_handler,
 | 
			
		||||
            RESTAPI_validate_sub_token_handler,
 | 
			
		||||
            RESTAPI_validate_token_handler,
 | 
			
		||||
            RESTAPI_validate_apikey,
 | 
			
		||||
            RESTAPI_signup_handler
 | 
			
		||||
        >(Path, Bindings, L, S, TransactionId);
 | 
			
		||||
			RESTAPI_oauth2_handler, RESTAPI_user_handler, RESTAPI_users_handler,
 | 
			
		||||
			RESTAPI_system_command, RESTAPI_system_configuration, RESTAPI_asset_server, RESTAPI_system_endpoints_handler,
 | 
			
		||||
			RESTAPI_action_links, RESTAPI_avatar_handler, RESTAPI_subavatar_handler,
 | 
			
		||||
			RESTAPI_email_handler, RESTAPI_sms_handler, RESTAPI_preferences, RESTAPI_subpreferences,
 | 
			
		||||
			RESTAPI_suboauth2_handler, RESTAPI_subuser_handler, RESTAPI_subusers_handler,
 | 
			
		||||
			RESTAPI_submfa_handler, RESTAPI_totp_handler, RESTAPI_subtotp_handler,
 | 
			
		||||
			RESTAPI_validate_sub_token_handler, RESTAPI_validate_token_handler,
 | 
			
		||||
			RESTAPI_validate_apikey, RESTAPI_signup_handler, RESTAPI_systemSecret_handler>(
 | 
			
		||||
			Path, Bindings, L, S, TransactionId);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -3,8 +3,8 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_signup_handler.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
 | 
			
		||||
#define __DBG__ std::cout << __LINE__ << std::endl;
 | 
			
		||||
@@ -15,23 +15,23 @@ namespace OpenWifi {
 | 
			
		||||
		auto signupUUID = GetParameter("signupUUID");
 | 
			
		||||
		auto owner = GetParameter("owner");
 | 
			
		||||
		auto operatorName = GetParameter("operatorName");
 | 
			
		||||
        if(UserName.empty() || signupUUID.empty() || owner.empty() || operatorName.empty()) {
 | 
			
		||||
		if (UserName.empty() || signupUUID.empty() || owner.empty() || operatorName.empty()) {
 | 
			
		||||
			Logger().error("Signup requires: email, signupUUID, operatorName, and owner.");
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!Utils::ValidEMailAddress(UserName)) {
 | 
			
		||||
		if (!Utils::ValidEMailAddress(UserName)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidEmailAddress);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Do we already exist? Can only signup once...
 | 
			
		||||
		SecurityObjects::UserInfo Existing;
 | 
			
		||||
        if(StorageService()->SubDB().GetUserByEmail(UserName,Existing)) {
 | 
			
		||||
            if(Existing.signingUp.empty()) {
 | 
			
		||||
		if (StorageService()->SubDB().GetUserByEmail(UserName, Existing)) {
 | 
			
		||||
			if (Existing.signingUp.empty()) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::SignupAlreadySigned);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            if(Existing.waitingForEmailCheck) {
 | 
			
		||||
			if (Existing.waitingForEmailCheck) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::SignupEmailCheck);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -52,14 +52,15 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		StorageService()->SubDB().CreateRecord(NewSub);
 | 
			
		||||
 | 
			
		||||
        Logger_.information(fmt::format("SIGNUP-PASSWORD({}): Request for {}", Request->clientAddress().toString(), UserName));
 | 
			
		||||
		Logger_.information(fmt::format("SIGNUP-PASSWORD({}): Request for {}",
 | 
			
		||||
										Request->clientAddress().toString(), UserName));
 | 
			
		||||
		SecurityObjects::ActionLink NewLink;
 | 
			
		||||
 | 
			
		||||
		NewLink.action = OpenWifi::SecurityObjects::LinkActions::SUB_SIGNUP;
 | 
			
		||||
		NewLink.id = MicroServiceCreateUUID();
 | 
			
		||||
		NewLink.userId = NewSub.id;
 | 
			
		||||
		NewLink.created = OpenWifi::Now();
 | 
			
		||||
        NewLink.expires = NewLink.created + (1*60*60);  // 1 hour
 | 
			
		||||
		NewLink.expires = NewLink.created + (1 * 60 * 60); // 1 hour
 | 
			
		||||
		NewLink.userAction = false;
 | 
			
		||||
		StorageService()->ActionLinksDB().CreateAction(NewLink);
 | 
			
		||||
 | 
			
		||||
@@ -72,4 +73,4 @@ namespace OpenWifi {
 | 
			
		||||
		// TODO
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -9,31 +9,30 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_signup_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_signup_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_signup_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							   RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							   bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
                                 std::vector<std::string>{
 | 
			
		||||
                                         Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_PUT},
 | 
			
		||||
                                 Server,
 | 
			
		||||
                                 TransactionId,
 | 
			
		||||
                                 Internal, false, true ){}
 | 
			
		||||
							 Server, TransactionId, Internal, false, true) {}
 | 
			
		||||
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/signup"}; };
 | 
			
		||||
 | 
			
		||||
/*        inline bool RoleIsAuthorized(std::string & Reason) {
 | 
			
		||||
		/*        inline bool RoleIsAuthorized(std::string & Reason) {
 | 
			
		||||
					if(UserInfo_.userinfo.userRole != SecurityObjects::USER_ROLE::SUBSCRIBER) {
 | 
			
		||||
						Reason = "User must be a subscriber";
 | 
			
		||||
						return false;
 | 
			
		||||
					}
 | 
			
		||||
					return true;
 | 
			
		||||
				}
 | 
			
		||||
*/
 | 
			
		||||
        void DoGet() final {};
 | 
			
		||||
		*/
 | 
			
		||||
		void DoGet() final{};
 | 
			
		||||
		void DoPost() final;
 | 
			
		||||
        void DoPut() final ;
 | 
			
		||||
        void DoDelete() final {};
 | 
			
		||||
    private:
 | 
			
		||||
		void DoPut() final;
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -11,43 +11,42 @@ namespace OpenWifi {
 | 
			
		||||
	void OpenWifi::RESTAPI_sms_handler::DoPost() {
 | 
			
		||||
		const auto &Obj = ParsedBody_;
 | 
			
		||||
 | 
			
		||||
        if(!SMSSender()->Enabled()) {
 | 
			
		||||
		if (!SMSSender()->Enabled()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SMSMFANotEnabled);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::string Arg;
 | 
			
		||||
        if(HasParameter("validateNumber",Arg) && Arg=="true" && Obj->has("to")) {
 | 
			
		||||
		if (HasParameter("validateNumber", Arg) && Arg == "true" && Obj->has("to")) {
 | 
			
		||||
			auto Number = Obj->get("to").toString();
 | 
			
		||||
            if(SMSSender()->StartValidation(Number, UserInfo_.userinfo.email)) {
 | 
			
		||||
			if (SMSSender()->StartValidation(Number, UserInfo_.userinfo.email)) {
 | 
			
		||||
				return OK();
 | 
			
		||||
			}
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SMSCouldNotBeSentRetry);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::string Code;
 | 
			
		||||
        if( HasParameter("completeValidation",Arg) &&
 | 
			
		||||
            Arg=="true" &&
 | 
			
		||||
            HasParameter("validationCode", Code) &&
 | 
			
		||||
            Obj->has("to")) {
 | 
			
		||||
		if (HasParameter("completeValidation", Arg) && Arg == "true" &&
 | 
			
		||||
			HasParameter("validationCode", Code) && Obj->has("to")) {
 | 
			
		||||
			auto Number = Obj->get("to").toString();
 | 
			
		||||
            if(SMSSender()->CompleteValidation(Number, Code, UserInfo_.userinfo.email)) {
 | 
			
		||||
			if (SMSSender()->CompleteValidation(Number, Code, UserInfo_.userinfo.email)) {
 | 
			
		||||
				return OK();
 | 
			
		||||
			}
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SMSCouldNotValidate);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if( UserInfo_.userinfo.userRole!=SecurityObjects::ROOT &&
 | 
			
		||||
            UserInfo_.userinfo.userRole!=SecurityObjects::PARTNER &&
 | 
			
		||||
            UserInfo_.userinfo.userRole!=SecurityObjects::ADMIN) {
 | 
			
		||||
        if(Internal_) {
 | 
			
		||||
            poco_information(Logger(),fmt::format("Internal SMS request: TID={}", TransactionId_));
 | 
			
		||||
        } else if (UserInfo_.userinfo.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
			UserInfo_.userinfo.userRole != SecurityObjects::PARTNER &&
 | 
			
		||||
			UserInfo_.userinfo.userRole != SecurityObjects::ADMIN) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if (Obj->has("to") &&
 | 
			
		||||
            Obj->has("text")) {
 | 
			
		||||
		if (Obj->has("to") && Obj->has("text")) {
 | 
			
		||||
 | 
			
		||||
			std::string PhoneNumber = Obj->get("to").toString();
 | 
			
		||||
			std::string Text = Obj->get("text").toString();
 | 
			
		||||
            if(SMSSender()->Send(PhoneNumber, Text))
 | 
			
		||||
			if (SMSSender()->Send(PhoneNumber, Text))
 | 
			
		||||
				return OK();
 | 
			
		||||
 | 
			
		||||
			return InternalError(RESTAPI::Errors::SMSCouldNotBeSentRetry);
 | 
			
		||||
@@ -55,4 +54,4 @@ namespace OpenWifi {
 | 
			
		||||
		BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -9,17 +9,17 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_sms_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_sms_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_sms_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                                                  Server,
 | 
			
		||||
                                                  TransactionId,
 | 
			
		||||
                                                  Internal) {}
 | 
			
		||||
        static auto PathName() { return std::list<std::string>{"/api/v1/sms"};}
 | 
			
		||||
        void DoGet() final {};
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/sms"}; }
 | 
			
		||||
		void DoGet() final{};
 | 
			
		||||
		void DoPost() final;
 | 
			
		||||
        void DoDelete() final {};
 | 
			
		||||
        void DoPut() final {};
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
		void DoPut() final{};
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -5,20 +5,22 @@
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
#include "Poco/CountingStream.h"
 | 
			
		||||
#include "Poco/Net/HTMLForm.h"
 | 
			
		||||
#include "RESTAPI_subavatar_handler.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "Poco/Net/HTMLForm.h"
 | 
			
		||||
#include "Poco/CountingStream.h"
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
    void SubAvatarPartHandler::handlePart(const Poco::Net::MessageHeader &Header, std::istream &Stream) {
 | 
			
		||||
	void SubAvatarPartHandler::handlePart(const Poco::Net::MessageHeader &Header,
 | 
			
		||||
										  std::istream &Stream) {
 | 
			
		||||
		FileType_ = Header.get(RESTAPI::Protocol::CONTENTTYPE, RESTAPI::Protocol::UNSPECIFIED);
 | 
			
		||||
		if (Header.has(RESTAPI::Protocol::CONTENTDISPOSITION)) {
 | 
			
		||||
			std::string Disposition;
 | 
			
		||||
			Poco::Net::NameValueCollection Parameters;
 | 
			
		||||
            Poco::Net::MessageHeader::splitParameters(Header[RESTAPI::Protocol::CONTENTDISPOSITION], Disposition, Parameters);
 | 
			
		||||
			Poco::Net::MessageHeader::splitParameters(Header[RESTAPI::Protocol::CONTENTDISPOSITION],
 | 
			
		||||
													  Disposition, Parameters);
 | 
			
		||||
			Name_ = Parameters.get(RESTAPI::Protocol::NAME, RESTAPI::Protocol::UNNAMED);
 | 
			
		||||
		}
 | 
			
		||||
		Poco::CountingInputStream InputStream(Stream);
 | 
			
		||||
@@ -35,14 +37,17 @@ namespace OpenWifi {
 | 
			
		||||
		Poco::Net::HTMLForm form(*Request, Request->stream(), partHandler);
 | 
			
		||||
		Poco::JSON::Object Answer;
 | 
			
		||||
 | 
			
		||||
        if (!partHandler.Name().empty() && partHandler.Length()< MicroServiceConfigGetInt("openwifi.avatar.maxsize",2000000)) {
 | 
			
		||||
		if (!partHandler.Name().empty() &&
 | 
			
		||||
			partHandler.Length() < MicroServiceConfigGetInt("openwifi.avatar.maxsize", 2000000)) {
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::AVATARID, Id);
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::ERRORCODE, 0);
 | 
			
		||||
            Logger_.information(fmt::format("Uploaded avatar: {} Type: {}", partHandler.Name(), partHandler.ContentType()));
 | 
			
		||||
            StorageService()->SubAvatarDB().SetAvatar(UserInfo_.userinfo.email,
 | 
			
		||||
                                 Id, SS.str(), partHandler.ContentType(), partHandler.Name());
 | 
			
		||||
            StorageService()->SubDB().SetAvatar(Id,"1");
 | 
			
		||||
            Logger().information(fmt::format("Adding avatar for {}",UserInfo_.userinfo.email));
 | 
			
		||||
			Logger_.information(fmt::format("Uploaded avatar: {} Type: {}", partHandler.Name(),
 | 
			
		||||
											partHandler.ContentType()));
 | 
			
		||||
			StorageService()->SubAvatarDB().SetAvatar(UserInfo_.userinfo.email, Id, SS.str(),
 | 
			
		||||
													  partHandler.ContentType(),
 | 
			
		||||
													  partHandler.Name());
 | 
			
		||||
			StorageService()->SubDB().SetAvatar(Id, "1");
 | 
			
		||||
			Logger().information(fmt::format("Adding avatar for {}", UserInfo_.userinfo.email));
 | 
			
		||||
		} else {
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::AVATARID, Id);
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::ERRORCODE, 13);
 | 
			
		||||
@@ -58,25 +63,26 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::string Type, Name, AvatarContent;
 | 
			
		||||
        if (!StorageService()->SubAvatarDB().GetAvatar(UserInfo_.userinfo.email, Id, AvatarContent, Type, Name)) {
 | 
			
		||||
		if (!StorageService()->SubAvatarDB().GetAvatar(UserInfo_.userinfo.email, Id, AvatarContent,
 | 
			
		||||
													   Type, Name)) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
        Logger().information(fmt::format("Retrieving avatar for {}",UserInfo_.userinfo.email));
 | 
			
		||||
		Logger().information(fmt::format("Retrieving avatar for {}", UserInfo_.userinfo.email));
 | 
			
		||||
		return SendFileContent(AvatarContent, Type, Name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_subavatar_handler::DoDelete() {
 | 
			
		||||
		std::string Id = GetBinding(RESTAPI::Protocol::ID, "");
 | 
			
		||||
 | 
			
		||||
        if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && Id!=UserInfo_.userinfo.id) {
 | 
			
		||||
		if (UserInfo_.userinfo.userRole != SecurityObjects::ROOT && Id != UserInfo_.userinfo.id) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!StorageService()->SubAvatarDB().DeleteAvatar(UserInfo_.userinfo.email, Id)) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
        Logger().information(fmt::format("Deleted avatar for {}",UserInfo_.userinfo.email));
 | 
			
		||||
        StorageService()->SubDB().SetAvatar(Id,"");
 | 
			
		||||
		Logger().information(fmt::format("Deleted avatar for {}", UserInfo_.userinfo.email));
 | 
			
		||||
		StorageService()->SubDB().SetAvatar(Id, "");
 | 
			
		||||
		OK();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -3,18 +3,15 @@
 | 
			
		||||
//
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "framework/RESTAPI_Handler.h"
 | 
			
		||||
#include "Poco/Net/PartHandler.h"
 | 
			
		||||
#include "framework/RESTAPI_Handler.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	class SubAvatarPartHandler : public Poco::Net::PartHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        SubAvatarPartHandler(std::string Id, Poco::Logger &Logger, std::stringstream & ofs) :
 | 
			
		||||
                Id_(std::move(Id)),
 | 
			
		||||
                Logger_(Logger),
 | 
			
		||||
                OutputStream_(ofs){
 | 
			
		||||
        }
 | 
			
		||||
		SubAvatarPartHandler(std::string Id, Poco::Logger &Logger, std::stringstream &ofs)
 | 
			
		||||
			: Id_(std::move(Id)), Logger_(Logger), OutputStream_(ofs) {}
 | 
			
		||||
		void handlePart(const Poco::Net::MessageHeader &Header, std::istream &Stream);
 | 
			
		||||
		[[nodiscard]] uint64_t Length() const { return Length_; }
 | 
			
		||||
		[[nodiscard]] std::string &Name() { return Name_; }
 | 
			
		||||
@@ -28,27 +25,25 @@ namespace OpenWifi {
 | 
			
		||||
		Poco::Logger &Logger_;
 | 
			
		||||
		std::stringstream &OutputStream_;
 | 
			
		||||
 | 
			
		||||
        inline Poco::Logger & Logger() { return Logger_; }
 | 
			
		||||
		inline Poco::Logger &Logger() { return Logger_; }
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class RESTAPI_subavatar_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_subavatar_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_subavatar_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
								  RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
								  bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
                                 std::vector<std::string>{
 | 
			
		||||
                                         Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_DELETE,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                                         Server,
 | 
			
		||||
                                         TransactionId,
 | 
			
		||||
                                         Internal) {}
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/subavatar/{id}"}; };
 | 
			
		||||
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoPost() final;
 | 
			
		||||
		void DoDelete() final;
 | 
			
		||||
        void DoPut() final {};
 | 
			
		||||
 | 
			
		||||
		void DoPut() final{};
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,8 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_submfa_handler.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "SMSSender.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
@@ -12,16 +12,16 @@ namespace OpenWifi {
 | 
			
		||||
	void RESTAPI_submfa_handler::DoGet() {
 | 
			
		||||
		SecurityObjects::UserInfo User;
 | 
			
		||||
 | 
			
		||||
        if (StorageService()->SubDB().GetUserById(UserInfo_.userinfo.id,User)) {
 | 
			
		||||
		if (StorageService()->SubDB().GetUserById(UserInfo_.userinfo.id, User)) {
 | 
			
		||||
			Poco::JSON::Object Answer;
 | 
			
		||||
			SecurityObjects::SubMfaConfig MFC;
 | 
			
		||||
 | 
			
		||||
			MFC.id = User.id;
 | 
			
		||||
            if(User.userTypeProprietaryInfo.mfa.enabled) {
 | 
			
		||||
                if(User.userTypeProprietaryInfo.mfa.method == "sms") {
 | 
			
		||||
			if (User.userTypeProprietaryInfo.mfa.enabled) {
 | 
			
		||||
				if (User.userTypeProprietaryInfo.mfa.method == "sms") {
 | 
			
		||||
					MFC.sms = User.userTypeProprietaryInfo.mobiles[0].number;
 | 
			
		||||
					MFC.type = "sms";
 | 
			
		||||
                } else if(User.userTypeProprietaryInfo.mfa.method == "email") {
 | 
			
		||||
				} else if (User.userTypeProprietaryInfo.mfa.method == "email") {
 | 
			
		||||
					MFC.email = User.email;
 | 
			
		||||
					MFC.type = "email";
 | 
			
		||||
				}
 | 
			
		||||
@@ -37,7 +37,7 @@ namespace OpenWifi {
 | 
			
		||||
	void RESTAPI_submfa_handler::DoPut() {
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
            const auto & Body = ParsedBody_;
 | 
			
		||||
			const auto &Body = ParsedBody_;
 | 
			
		||||
 | 
			
		||||
			SecurityObjects::SubMfaConfig MFC;
 | 
			
		||||
 | 
			
		||||
@@ -49,7 +49,8 @@ namespace OpenWifi {
 | 
			
		||||
				SecurityObjects::UserInfo User;
 | 
			
		||||
				StorageService()->SubDB().GetUserById(UserInfo_.userinfo.id, User);
 | 
			
		||||
				User.userTypeProprietaryInfo.mfa.enabled = false;
 | 
			
		||||
                StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email, UserInfo_.userinfo.id, User);
 | 
			
		||||
				StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email,
 | 
			
		||||
														 UserInfo_.userinfo.id, User);
 | 
			
		||||
 | 
			
		||||
				Poco::JSON::Object Answer;
 | 
			
		||||
				MFC.to_json(Answer);
 | 
			
		||||
@@ -60,7 +61,8 @@ namespace OpenWifi {
 | 
			
		||||
				StorageService()->SubDB().GetUserById(UserInfo_.userinfo.id, User);
 | 
			
		||||
				User.userTypeProprietaryInfo.mfa.enabled = true;
 | 
			
		||||
				User.userTypeProprietaryInfo.mfa.method = "email";
 | 
			
		||||
                StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email, UserInfo_.userinfo.id, User);
 | 
			
		||||
				StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email,
 | 
			
		||||
														 UserInfo_.userinfo.id, User);
 | 
			
		||||
 | 
			
		||||
				MFC.sms = MFC.sms;
 | 
			
		||||
				MFC.type = "email";
 | 
			
		||||
@@ -77,7 +79,7 @@ namespace OpenWifi {
 | 
			
		||||
						return BadRequest(RESTAPI::Errors::SMSMissingPhoneNumber);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
                    if(!SMSSender()->Enabled()) {
 | 
			
		||||
					if (!SMSSender()->Enabled()) {
 | 
			
		||||
						return BadRequest(RESTAPI::Errors::SMSMFANotEnabled);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
@@ -88,7 +90,7 @@ namespace OpenWifi {
 | 
			
		||||
					}
 | 
			
		||||
				} else if (GetBoolParameter("completeValidation", false)) {
 | 
			
		||||
 | 
			
		||||
                    if(!SMSSender()->Enabled()) {
 | 
			
		||||
					if (!SMSSender()->Enabled()) {
 | 
			
		||||
						return BadRequest(RESTAPI::Errors::SMSMFANotEnabled);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
@@ -99,7 +101,8 @@ namespace OpenWifi {
 | 
			
		||||
					if (MFC.sms.empty()) {
 | 
			
		||||
						return BadRequest(RESTAPI::Errors::SMSMissingPhoneNumber);
 | 
			
		||||
					}
 | 
			
		||||
                    if (SMSSender()->CompleteValidation(MFC.sms, ChallengeCode, UserInfo_.userinfo.email)) {
 | 
			
		||||
					if (SMSSender()->CompleteValidation(MFC.sms, ChallengeCode,
 | 
			
		||||
														UserInfo_.userinfo.email)) {
 | 
			
		||||
						SecurityObjects::UserInfo User;
 | 
			
		||||
 | 
			
		||||
						StorageService()->SubDB().GetUserById(UserInfo_.userinfo.id, User);
 | 
			
		||||
@@ -112,7 +115,8 @@ namespace OpenWifi {
 | 
			
		||||
						User.userTypeProprietaryInfo.mobiles.clear();
 | 
			
		||||
						User.userTypeProprietaryInfo.mobiles.push_back(PhoneNumber);
 | 
			
		||||
 | 
			
		||||
                        StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email, UserInfo_.userinfo.id, User);
 | 
			
		||||
						StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email,
 | 
			
		||||
																 UserInfo_.userinfo.id, User);
 | 
			
		||||
 | 
			
		||||
						MFC.sms = MFC.sms;
 | 
			
		||||
						MFC.type = "sms";
 | 
			
		||||
@@ -135,4 +139,4 @@ namespace OpenWifi {
 | 
			
		||||
		return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -9,19 +9,19 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_submfa_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_submfa_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_submfa_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							   RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							   bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_PUT,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                                                  Server,
 | 
			
		||||
                                                  TransactionId,
 | 
			
		||||
                                                  Internal, true, false , RateLimit{.Interval=1000,.MaxCalls=10},
 | 
			
		||||
                                                  true) {}
 | 
			
		||||
							 Server, TransactionId, Internal, true, false,
 | 
			
		||||
							 RateLimit{.Interval = 1000, .MaxCalls = 10}, true) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/submfa"}; };
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
        void DoPost() final {};
 | 
			
		||||
        void DoDelete() final {};
 | 
			
		||||
        void DoPut() final ;
 | 
			
		||||
		void DoPost() final{};
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
		void DoPut() final;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -5,21 +5,22 @@
 | 
			
		||||
#include "RESTAPI_suboauth2_handler.h"
 | 
			
		||||
#include "AuthService.h"
 | 
			
		||||
#include "MFAServer.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_db_helpers.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_suboauth2_handler::DoGet() {
 | 
			
		||||
		bool Expired = false, Contacted = false;
 | 
			
		||||
		if (!IsAuthorized(Expired, Contacted, true)) {
 | 
			
		||||
            if(Expired)
 | 
			
		||||
			if (Expired)
 | 
			
		||||
				return UnAuthorized(RESTAPI::Errors::EXPIRED_TOKEN);
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::INVALID_TOKEN);
 | 
			
		||||
		}
 | 
			
		||||
		bool GetMe = GetBoolParameter(RESTAPI::Protocol::ME, false);
 | 
			
		||||
        if(GetMe) {
 | 
			
		||||
            Logger_.information(fmt::format("REQUEST-ME({}): Request for {}", Request->clientAddress().toString(),
 | 
			
		||||
		if (GetMe) {
 | 
			
		||||
			Logger_.information(fmt::format("REQUEST-ME({}): Request for {}",
 | 
			
		||||
											Request->clientAddress().toString(),
 | 
			
		||||
											UserInfo_.userinfo.email));
 | 
			
		||||
			Poco::JSON::Object Me;
 | 
			
		||||
			SecurityObjects::UserInfo ReturnedUser = UserInfo_.userinfo;
 | 
			
		||||
@@ -49,7 +50,7 @@ namespace OpenWifi {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_suboauth2_handler::DoPost() {
 | 
			
		||||
        const auto & Obj = ParsedBody_;
 | 
			
		||||
		const auto &Obj = ParsedBody_;
 | 
			
		||||
		auto userId = GetS(RESTAPI::Protocol::USERID, Obj);
 | 
			
		||||
		auto password = GetS(RESTAPI::Protocol::PASSWORD, Obj);
 | 
			
		||||
		auto newPassword = GetS(RESTAPI::Protocol::NEWPASSWORD, Obj);
 | 
			
		||||
@@ -58,9 +59,9 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		Poco::toLowerInPlace(userId);
 | 
			
		||||
 | 
			
		||||
        if(!refreshToken.empty() && grant_type == "refresh_token") {
 | 
			
		||||
		if (!refreshToken.empty() && grant_type == "refresh_token") {
 | 
			
		||||
			SecurityObjects::UserInfoAndPolicy UInfo;
 | 
			
		||||
            if(AuthService()->RefreshSubToken(*Request, refreshToken, UInfo)) {
 | 
			
		||||
			if (AuthService()->RefreshSubToken(*Request, refreshToken, UInfo)) {
 | 
			
		||||
				Poco::JSON::Object Answer;
 | 
			
		||||
				UInfo.webtoken.to_json(Answer);
 | 
			
		||||
				return ReturnObject(Answer);
 | 
			
		||||
@@ -69,27 +70,30 @@ namespace OpenWifi {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter(RESTAPI::Protocol::REQUIREMENTS)) {
 | 
			
		||||
            Logger_.information(fmt::format("POLICY-REQUEST({}): Request.", Request->clientAddress().toString()));
 | 
			
		||||
		if (GetBoolParameter(RESTAPI::Protocol::REQUIREMENTS)) {
 | 
			
		||||
			Logger_.information(
 | 
			
		||||
				fmt::format("POLICY-REQUEST({}): Request.", Request->clientAddress().toString()));
 | 
			
		||||
			Poco::JSON::Object Answer;
 | 
			
		||||
            Answer.set(RESTAPI::Protocol::PASSWORDPATTERN, AuthService()->SubPasswordValidationExpression());
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::PASSWORDPATTERN,
 | 
			
		||||
					   AuthService()->SubPasswordValidationExpression());
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::ACCESSPOLICY, AuthService()->GetSubAccessPolicy());
 | 
			
		||||
			Answer.set(RESTAPI::Protocol::PASSWORDPOLICY, AuthService()->GetSubPasswordPolicy());
 | 
			
		||||
			return ReturnObject(Answer);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter(RESTAPI::Protocol::FORGOTPASSWORD)) {
 | 
			
		||||
		if (GetBoolParameter(RESTAPI::Protocol::FORGOTPASSWORD)) {
 | 
			
		||||
			SecurityObjects::UserInfo UInfo1;
 | 
			
		||||
            auto UserExists = StorageService()->SubDB().GetUserByEmail(userId,UInfo1);
 | 
			
		||||
            if(UserExists) {
 | 
			
		||||
                Logger_.information(fmt::format("FORGOTTEN-PASSWORD({}): Request for {}", Request->clientAddress().toString(), userId));
 | 
			
		||||
			auto UserExists = StorageService()->SubDB().GetUserByEmail(userId, UInfo1);
 | 
			
		||||
			if (UserExists) {
 | 
			
		||||
				Logger_.information(fmt::format("FORGOTTEN-PASSWORD({}): Request for {}",
 | 
			
		||||
												Request->clientAddress().toString(), userId));
 | 
			
		||||
				SecurityObjects::ActionLink NewLink;
 | 
			
		||||
 | 
			
		||||
				NewLink.action = OpenWifi::SecurityObjects::LinkActions::SUB_FORGOT_PASSWORD;
 | 
			
		||||
				NewLink.id = MicroServiceCreateUUID();
 | 
			
		||||
				NewLink.userId = UInfo1.id;
 | 
			
		||||
				NewLink.created = OpenWifi::Now();
 | 
			
		||||
                NewLink.expires = NewLink.created + (24*60*60);
 | 
			
		||||
				NewLink.expires = NewLink.created + (24 * 60 * 60);
 | 
			
		||||
				NewLink.userAction = false;
 | 
			
		||||
				StorageService()->ActionLinksDB().CreateAction(NewLink);
 | 
			
		||||
 | 
			
		||||
@@ -107,21 +111,23 @@ namespace OpenWifi {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter(RESTAPI::Protocol::RESENDMFACODE)) {
 | 
			
		||||
            Logger_.information(fmt::format("RESEND-MFA-CODE({}): Request for {}", Request->clientAddress().toString(), userId));
 | 
			
		||||
            if(Obj->has("uuid")) {
 | 
			
		||||
		if (GetBoolParameter(RESTAPI::Protocol::RESENDMFACODE)) {
 | 
			
		||||
			Logger_.information(fmt::format("RESEND-MFA-CODE({}): Request for {}",
 | 
			
		||||
											Request->clientAddress().toString(), userId));
 | 
			
		||||
			if (Obj->has("uuid")) {
 | 
			
		||||
				auto uuid = Obj->get("uuid").toString();
 | 
			
		||||
                if(MFAServer()->ResendCode(uuid))
 | 
			
		||||
				if (MFAServer()->ResendCode(uuid))
 | 
			
		||||
					return OK();
 | 
			
		||||
			}
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::BAD_MFA_TRANSACTION);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter(RESTAPI::Protocol::COMPLETEMFACHALLENGE)) {
 | 
			
		||||
            Logger_.information(fmt::format("COMPLETE-MFA-CHALLENGE({}): Request for {}", Request->clientAddress().toString(), userId));
 | 
			
		||||
            if(Obj->has("uuid") && Obj->has("answer")) {
 | 
			
		||||
		if (GetBoolParameter(RESTAPI::Protocol::COMPLETEMFACHALLENGE)) {
 | 
			
		||||
			Logger_.information(fmt::format("COMPLETE-MFA-CHALLENGE({}): Request for {}",
 | 
			
		||||
											Request->clientAddress().toString(), userId));
 | 
			
		||||
			if (Obj->has("uuid") && Obj->has("answer")) {
 | 
			
		||||
				SecurityObjects::UserInfoAndPolicy UInfo;
 | 
			
		||||
                if(MFAServer()->CompleteMFAChallenge(Obj,UInfo)) {
 | 
			
		||||
				if (MFAServer()->CompleteMFAChallenge(Obj, UInfo)) {
 | 
			
		||||
					Poco::JSON::Object ReturnObj;
 | 
			
		||||
					UInfo.webtoken.to_json(ReturnObj);
 | 
			
		||||
					return ReturnObject(ReturnObj);
 | 
			
		||||
@@ -131,20 +137,21 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::UserInfoAndPolicy UInfo;
 | 
			
		||||
        bool Expired=false;
 | 
			
		||||
        auto Code=AuthService()->AuthorizeSub(userId, password, newPassword, UInfo, Expired);
 | 
			
		||||
        if (Code==SUCCESS) {
 | 
			
		||||
		bool Expired = false;
 | 
			
		||||
		auto Code = AuthService()->AuthorizeSub(userId, password, newPassword, UInfo, Expired);
 | 
			
		||||
		switch (Code) {
 | 
			
		||||
		case SUCCESS: {
 | 
			
		||||
			Poco::JSON::Object ReturnObj;
 | 
			
		||||
            if(AuthService()->RequiresMFA(UInfo)) {
 | 
			
		||||
                if(MFAServer()->StartMFAChallenge(UInfo, ReturnObj)) {
 | 
			
		||||
			if (AuthService()->RequiresMFA(UInfo)) {
 | 
			
		||||
				if (MFAServer()->StartMFAChallenge(UInfo, ReturnObj)) {
 | 
			
		||||
					return ReturnObject(ReturnObj);
 | 
			
		||||
				}
 | 
			
		||||
                Logger_.warning("MFA Seems to be broken. Please fix. Disabling MFA checking for now.");
 | 
			
		||||
				Logger_.warning(
 | 
			
		||||
					"MFA Seems to be broken. Please fix. Disabling MFA checking for now.");
 | 
			
		||||
			}
 | 
			
		||||
			UInfo.webtoken.to_json(ReturnObj);
 | 
			
		||||
			return ReturnObject(ReturnObj);
 | 
			
		||||
        } else {
 | 
			
		||||
            switch(Code) {
 | 
			
		||||
		}
 | 
			
		||||
		case INVALID_CREDENTIALS:
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::INVALID_CREDENTIALS);
 | 
			
		||||
		case PASSWORD_INVALID:
 | 
			
		||||
@@ -155,10 +162,10 @@ namespace OpenWifi {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::USERNAME_PENDING_VERIFICATION);
 | 
			
		||||
		case PASSWORD_CHANGE_REQUIRED:
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::PASSWORD_CHANGE_REQUIRED);
 | 
			
		||||
		case ACCOUNT_SUSPENDED:
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCOUNT_SUSPENDED);
 | 
			
		||||
		default:
 | 
			
		||||
                    return UnAuthorized(RESTAPI::Errors::INVALID_CREDENTIALS); break;
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::INVALID_CREDENTIALS);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -8,20 +8,22 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_suboauth2_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_suboauth2_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_suboauth2_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
								  RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
								  bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_DELETE,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                                                  Server,
 | 
			
		||||
                                                  TransactionId,
 | 
			
		||||
                                                  Internal, false, false , RateLimit{.Interval=1000,.MaxCalls=10},
 | 
			
		||||
                                                  false) {}
 | 
			
		||||
        static auto PathName() { return std::list<std::string>{"/api/v1/suboauth2/{token}","/api/v1/suboauth2"}; };
 | 
			
		||||
							 Server, TransactionId, Internal, false, false,
 | 
			
		||||
							 RateLimit{.Interval = 1000, .MaxCalls = 10}, false) {}
 | 
			
		||||
		static auto PathName() {
 | 
			
		||||
			return std::list<std::string>{"/api/v1/suboauth2/{token}", "/api/v1/suboauth2"};
 | 
			
		||||
		};
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoPost() final;
 | 
			
		||||
		void DoDelete() final;
 | 
			
		||||
        void DoPut() final {};
 | 
			
		||||
		void DoPut() final{};
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -19,8 +19,8 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::Preferences P;
 | 
			
		||||
 | 
			
		||||
        const auto & RawObject = ParsedBody_;
 | 
			
		||||
        if(!P.from_json(RawObject)) {
 | 
			
		||||
		const auto &RawObject = ParsedBody_;
 | 
			
		||||
		if (!P.from_json(RawObject)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -33,4 +33,4 @@ namespace OpenWifi {
 | 
			
		||||
		ReturnObject(Answer);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -9,19 +9,18 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_subpreferences : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_subpreferences(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_subpreferences(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							   RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							   bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
                         std::vector<std::string>{
 | 
			
		||||
            Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_PUT,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
            Server,
 | 
			
		||||
            TransactionId,
 | 
			
		||||
            Internal) {}
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/subpreferences"}; };
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoPut() final;
 | 
			
		||||
        void DoPost() final {};
 | 
			
		||||
        void DoDelete() final {};
 | 
			
		||||
		void DoPost() final{};
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -11,22 +11,23 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_subtotp_handler::DoGet() {
 | 
			
		||||
 | 
			
		||||
        auto Reset = GetBoolParameter("reset",false);
 | 
			
		||||
		auto Reset = GetBoolParameter("reset", false);
 | 
			
		||||
		std::string QRCode;
 | 
			
		||||
 | 
			
		||||
        if(TotpCache()->StartValidation(UserInfo_.userinfo,true,QRCode,Reset)) {
 | 
			
		||||
            return SendFileContent(QRCode, "image/svg+xml","qrcode.svg");
 | 
			
		||||
		if (TotpCache()->StartValidation(UserInfo_.userinfo, true, QRCode, Reset)) {
 | 
			
		||||
			return SendFileContent(QRCode, "image/svg+xml", "qrcode.svg");
 | 
			
		||||
		}
 | 
			
		||||
		return BadRequest(RESTAPI::Errors::InvalidCommand);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_subtotp_handler::DoPut() {
 | 
			
		||||
        auto Value = GetParameter("value","");
 | 
			
		||||
        auto nextIndex = GetParameter("index",0);
 | 
			
		||||
        bool moreCodes=false;
 | 
			
		||||
		auto Value = GetParameter("value", "");
 | 
			
		||||
		auto nextIndex = GetParameter("index", 0);
 | 
			
		||||
		bool moreCodes = false;
 | 
			
		||||
 | 
			
		||||
		RESTAPI::Errors::msg Error;
 | 
			
		||||
        if(TotpCache()->ContinueValidation(UserInfo_.userinfo,true,Value,nextIndex,moreCodes, Error )) {
 | 
			
		||||
		if (TotpCache()->ContinueValidation(UserInfo_.userinfo, true, Value, nextIndex, moreCodes,
 | 
			
		||||
											Error)) {
 | 
			
		||||
			Poco::JSON::Object Answer;
 | 
			
		||||
			Answer.set("nextIndex", nextIndex);
 | 
			
		||||
			Answer.set("moreCodes", moreCodes);
 | 
			
		||||
@@ -35,4 +36,4 @@ namespace OpenWifi {
 | 
			
		||||
		return BadRequest(Error);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -7,23 +7,20 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_subtotp_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_subtotp_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_subtotp_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
								RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
								bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
                                 std::vector<std::string>
 | 
			
		||||
                                         {
 | 
			
		||||
                                                 Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_PUT,
 | 
			
		||||
                                                 Poco::Net::HTTPRequest::HTTP_OPTIONS
 | 
			
		||||
                                         },
 | 
			
		||||
                                 Server,
 | 
			
		||||
                                 TransactionId,
 | 
			
		||||
                                 Internal) {}
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/subtotp"}; };
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
        void DoPost() final {};
 | 
			
		||||
        void DoDelete() final {};
 | 
			
		||||
		void DoPost() final{};
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
		void DoPut() final;
 | 
			
		||||
    private:
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -3,15 +3,15 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_subuser_handler.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
#include "SMSSender.h"
 | 
			
		||||
#include "SMTPMailerService.h"
 | 
			
		||||
#include "ACLProcessor.h"
 | 
			
		||||
#include "AuthService.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_db_helpers.h"
 | 
			
		||||
#include "MFAServer.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_db_helpers.h"
 | 
			
		||||
#include "SMSSender.h"
 | 
			
		||||
#include "SMTPMailerService.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "TotpCache.h"
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
 | 
			
		||||
@@ -19,18 +19,18 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_subuser_handler::DoGet() {
 | 
			
		||||
		std::string Id = GetBinding("id", "");
 | 
			
		||||
        if(Id.empty()) {
 | 
			
		||||
		if (Id.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingUserID);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Poco::toLowerInPlace(Id);
 | 
			
		||||
		std::string Arg;
 | 
			
		||||
		SecurityObjects::UserInfo UInfo;
 | 
			
		||||
        if(HasParameter("byEmail",Arg) && Arg=="true") {
 | 
			
		||||
            if(!StorageService()->SubDB().GetUserByEmail(Id,UInfo)) {
 | 
			
		||||
		if (HasParameter("byEmail", Arg) && Arg == "true") {
 | 
			
		||||
			if (!StorageService()->SubDB().GetUserByEmail(Id, UInfo)) {
 | 
			
		||||
				return NotFound();
 | 
			
		||||
			}
 | 
			
		||||
        } else if(!StorageService()->SubDB().GetUserById(Id,UInfo)) {
 | 
			
		||||
		} else if (!StorageService()->SubDB().GetUserById(Id, UInfo)) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -42,24 +42,25 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_subuser_handler::DoDelete() {
 | 
			
		||||
		std::string Id = GetBinding("id", "");
 | 
			
		||||
        if(Id.empty()) {
 | 
			
		||||
		if (Id.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingUserID);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::UserInfo TargetUser;
 | 
			
		||||
        if(!StorageService()->SubDB().GetUserById(Id,TargetUser)) {
 | 
			
		||||
		if (!StorageService()->SubDB().GetUserById(Id, TargetUser)) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(TargetUser.userRole != SecurityObjects::SUBSCRIBER) {
 | 
			
		||||
		if (TargetUser.userRole != SecurityObjects::SUBSCRIBER) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidUserRole);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!Internal_ && !ACLProcessor::Can(UserInfo_.userinfo, TargetUser,ACLProcessor::DELETE)) {
 | 
			
		||||
		if (!Internal_ &&
 | 
			
		||||
			!ACLProcessor::Can(UserInfo_.userinfo, TargetUser, ACLProcessor::DELETE)) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!StorageService()->SubDB().DeleteUser(UserInfo_.userinfo.email,Id)) {
 | 
			
		||||
		if (!StorageService()->SubDB().DeleteUser(UserInfo_.userinfo.email, Id)) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -67,48 +68,50 @@ namespace OpenWifi {
 | 
			
		||||
		StorageService()->SubTokenDB().RevokeAllTokens(TargetUser.email);
 | 
			
		||||
		StorageService()->SubPreferencesDB().DeleteRecord("id", Id);
 | 
			
		||||
		StorageService()->SubAvatarDB().DeleteRecord("id", Id);
 | 
			
		||||
        Logger_.information(fmt::format("User '{}' deleted by '{}'.",Id,UserInfo_.userinfo.email));
 | 
			
		||||
		Logger_.information(
 | 
			
		||||
			fmt::format("User '{}' deleted by '{}'.", Id, UserInfo_.userinfo.email));
 | 
			
		||||
		OK();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_subuser_handler::DoPost() {
 | 
			
		||||
		std::string Id = GetBinding("id", "");
 | 
			
		||||
        if(Id!="0") {
 | 
			
		||||
		if (Id != "0") {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::IdMustBe0);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::UserInfo NewUser;
 | 
			
		||||
        const auto & RawObject = ParsedBody_;
 | 
			
		||||
        if(!NewUser.from_json(RawObject)) {
 | 
			
		||||
		const auto &RawObject = ParsedBody_;
 | 
			
		||||
		if (!NewUser.from_json(RawObject)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(NewUser.userRole == SecurityObjects::UNKNOWN || NewUser.userRole != SecurityObjects::SUBSCRIBER) {
 | 
			
		||||
		if (NewUser.userRole == SecurityObjects::UNKNOWN ||
 | 
			
		||||
			NewUser.userRole != SecurityObjects::SUBSCRIBER) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidUserRole);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Poco::toLowerInPlace(NewUser.email);
 | 
			
		||||
		SecurityObjects::UserInfo Existing;
 | 
			
		||||
        if(StorageService()->SubDB().GetUserByEmail(NewUser.email,Existing)) {
 | 
			
		||||
		if (StorageService()->SubDB().GetUserByEmail(NewUser.email, Existing)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::UserAlreadyExists);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!Internal_ && !ACLProcessor::Can(UserInfo_.userinfo,NewUser,ACLProcessor::CREATE)) {
 | 
			
		||||
		if (!Internal_ && !ACLProcessor::Can(UserInfo_.userinfo, NewUser, ACLProcessor::CREATE)) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Poco::toLowerInPlace(NewUser.email);
 | 
			
		||||
        if(!Utils::ValidEMailAddress(NewUser.email)) {
 | 
			
		||||
		if (!Utils::ValidEMailAddress(NewUser.email)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidEmailAddress);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!NewUser.currentPassword.empty()) {
 | 
			
		||||
            if(!AuthService()->ValidateSubPassword(NewUser.currentPassword)) {
 | 
			
		||||
		if (!NewUser.currentPassword.empty()) {
 | 
			
		||||
			if (!AuthService()->ValidateSubPassword(NewUser.currentPassword)) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::InvalidPassword);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(NewUser.name.empty())
 | 
			
		||||
		if (NewUser.name.empty())
 | 
			
		||||
			NewUser.name = NewUser.email;
 | 
			
		||||
 | 
			
		||||
		//  You cannot enable MFA during user creation
 | 
			
		||||
@@ -117,19 +120,20 @@ namespace OpenWifi {
 | 
			
		||||
		NewUser.userTypeProprietaryInfo.mobiles.clear();
 | 
			
		||||
		NewUser.userTypeProprietaryInfo.authenticatorSecret.clear();
 | 
			
		||||
 | 
			
		||||
        if(!StorageService()->SubDB().CreateUser(UserInfo_.userinfo.email, NewUser)) {
 | 
			
		||||
            Logger_.information(fmt::format("Could not add user '{}'.",NewUser.email));
 | 
			
		||||
		if (!StorageService()->SubDB().CreateUser(UserInfo_.userinfo.email, NewUser)) {
 | 
			
		||||
			Logger_.information(fmt::format("Could not add user '{}'.", NewUser.email));
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::RecordNotCreated);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetParameter("email_verification","false")=="true") {
 | 
			
		||||
            if(AuthService::VerifySubEmail(NewUser))
 | 
			
		||||
                Logger_.information(fmt::format("Verification e-mail requested for {}",NewUser.email));
 | 
			
		||||
            StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email,NewUser.id,NewUser);
 | 
			
		||||
		if (GetParameter("email_verification", "false") == "true") {
 | 
			
		||||
			if (AuthService::VerifySubEmail(NewUser))
 | 
			
		||||
				Logger_.information(
 | 
			
		||||
					fmt::format("Verification e-mail requested for {}", NewUser.email));
 | 
			
		||||
			StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email, NewUser.id, NewUser);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!StorageService()->SubDB().GetUserByEmail(NewUser.email, NewUser)) {
 | 
			
		||||
            Logger_.information(fmt::format("User '{}' but not retrieved.",NewUser.email));
 | 
			
		||||
		if (!StorageService()->SubDB().GetUserByEmail(NewUser.email, NewUser)) {
 | 
			
		||||
			Logger_.information(fmt::format("User '{}' but not retrieved.", NewUser.email));
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -137,39 +141,41 @@ namespace OpenWifi {
 | 
			
		||||
		Sanitize(UserInfo_, NewUser);
 | 
			
		||||
		NewUser.to_json(UserInfoObject);
 | 
			
		||||
		ReturnObject(UserInfoObject);
 | 
			
		||||
        Logger_.information(fmt::format("User '{}' has been added by '{}')",NewUser.email, UserInfo_.userinfo.email));
 | 
			
		||||
		Logger_.information(fmt::format("User '{}' has been added by '{}')", NewUser.email,
 | 
			
		||||
										UserInfo_.userinfo.email));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_subuser_handler::DoPut() {
 | 
			
		||||
		std::string Id = GetBinding("id", "");
 | 
			
		||||
        if(Id.empty()) {
 | 
			
		||||
		if (Id.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingUserID);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::UserInfo Existing;
 | 
			
		||||
        if(!StorageService()->SubDB().GetUserById(Id,Existing)) {
 | 
			
		||||
		if (!StorageService()->SubDB().GetUserById(Id, Existing)) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!Internal_ && !ACLProcessor::Can(UserInfo_.userinfo,Existing,ACLProcessor::MODIFY)) {
 | 
			
		||||
		if (!Internal_ && !ACLProcessor::Can(UserInfo_.userinfo, Existing, ACLProcessor::MODIFY)) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter("resetMFA")) {
 | 
			
		||||
            if( (UserInfo_.userinfo.userRole == SecurityObjects::ROOT) ||
 | 
			
		||||
                (UserInfo_.userinfo.userRole == SecurityObjects::ADMIN && Existing.userRole!=SecurityObjects::ROOT) ||
 | 
			
		||||
		if (GetBoolParameter("resetMFA")) {
 | 
			
		||||
			if ((UserInfo_.userinfo.userRole == SecurityObjects::ROOT) ||
 | 
			
		||||
				(UserInfo_.userinfo.userRole == SecurityObjects::ADMIN &&
 | 
			
		||||
				 Existing.userRole != SecurityObjects::ROOT) ||
 | 
			
		||||
				(UserInfo_.userinfo.id == Id)) {
 | 
			
		||||
				Existing.userTypeProprietaryInfo.mfa.enabled = false;
 | 
			
		||||
				Existing.userTypeProprietaryInfo.mfa.method.clear();
 | 
			
		||||
				Existing.userTypeProprietaryInfo.mobiles.clear();
 | 
			
		||||
				Existing.modified = OpenWifi::Now();
 | 
			
		||||
                Existing.notes.push_back( SecurityObjects::NoteInfo{
 | 
			
		||||
                        .created=OpenWifi::Now(),
 | 
			
		||||
                        .createdBy=UserInfo_.userinfo.email,
 | 
			
		||||
                        .note="MFA Reset by " + UserInfo_.userinfo.email});
 | 
			
		||||
                StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email,Id,Existing);
 | 
			
		||||
				Existing.notes.push_back(
 | 
			
		||||
					SecurityObjects::NoteInfo{.created = OpenWifi::Now(),
 | 
			
		||||
											  .createdBy = UserInfo_.userinfo.email,
 | 
			
		||||
											  .note = "MFA Reset by " + UserInfo_.userinfo.email});
 | 
			
		||||
				StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email, Id, Existing);
 | 
			
		||||
				SecurityObjects::UserInfo NewUserInfo;
 | 
			
		||||
                StorageService()->SubDB().GetUserByEmail(UserInfo_.userinfo.email,NewUserInfo);
 | 
			
		||||
				StorageService()->SubDB().GetUserByEmail(UserInfo_.userinfo.email, NewUserInfo);
 | 
			
		||||
				Poco::JSON::Object ModifiedObject;
 | 
			
		||||
				Sanitize(UserInfo_, NewUserInfo);
 | 
			
		||||
				NewUserInfo.to_json(ModifiedObject);
 | 
			
		||||
@@ -179,16 +185,17 @@ namespace OpenWifi {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter("forgotPassword")) {
 | 
			
		||||
		if (GetBoolParameter("forgotPassword") || GetBoolParameter("resetPassword")) {
 | 
			
		||||
			Existing.changePassword = true;
 | 
			
		||||
            Logger_.information(fmt::format("FORGOTTEN-PASSWORD({}): Request for {}", Request->clientAddress().toString(), Existing.email));
 | 
			
		||||
			Logger_.information(fmt::format("FORGOTTEN-PASSWORD({}): Request for {}",
 | 
			
		||||
											Request->clientAddress().toString(), Existing.email));
 | 
			
		||||
 | 
			
		||||
			SecurityObjects::ActionLink NewLink;
 | 
			
		||||
			NewLink.action = OpenWifi::SecurityObjects::LinkActions::SUB_FORGOT_PASSWORD;
 | 
			
		||||
			NewLink.id = MicroServiceCreateUUID();
 | 
			
		||||
			NewLink.userId = Existing.id;
 | 
			
		||||
			NewLink.created = OpenWifi::Now();
 | 
			
		||||
            NewLink.expires = NewLink.created + (24*60*60);
 | 
			
		||||
			NewLink.expires = NewLink.created + (24 * 60 * 60);
 | 
			
		||||
			NewLink.userAction = false;
 | 
			
		||||
			StorageService()->ActionLinksDB().CreateAction(NewLink);
 | 
			
		||||
 | 
			
		||||
@@ -196,98 +203,113 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::UserInfo NewUser;
 | 
			
		||||
        const auto & RawObject = ParsedBody_;
 | 
			
		||||
        if(!NewUser.from_json(RawObject)) {
 | 
			
		||||
		const auto &RawObject = ParsedBody_;
 | 
			
		||||
		if (!NewUser.from_json(RawObject)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// some basic validations
 | 
			
		||||
        if(RawObject->has("userRole") &&
 | 
			
		||||
            (SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString())==SecurityObjects::UNKNOWN ||
 | 
			
		||||
            SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString())==SecurityObjects::SUBSCRIBER)) {
 | 
			
		||||
		if (RawObject->has("userRole") &&
 | 
			
		||||
			(SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString()) ==
 | 
			
		||||
				 SecurityObjects::UNKNOWN ||
 | 
			
		||||
			 SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString()) ==
 | 
			
		||||
				 SecurityObjects::SUBSCRIBER)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidUserRole);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// The only valid things to change are: changePassword, name,
 | 
			
		||||
        AssignIfPresent(RawObject,"name", Existing.name);
 | 
			
		||||
        AssignIfPresent(RawObject,"description", Existing.description);
 | 
			
		||||
        AssignIfPresent(RawObject,"owner", Existing.owner);
 | 
			
		||||
        AssignIfPresent(RawObject,"location", Existing.location);
 | 
			
		||||
        AssignIfPresent(RawObject,"locale", Existing.locale);
 | 
			
		||||
        AssignIfPresent(RawObject,"changePassword", Existing.changePassword);
 | 
			
		||||
        AssignIfPresent(RawObject,"suspended", Existing.suspended);
 | 
			
		||||
        AssignIfPresent(RawObject,"blackListed", Existing.blackListed);
 | 
			
		||||
		AssignIfPresent(RawObject, "name", Existing.name);
 | 
			
		||||
		AssignIfPresent(RawObject, "description", Existing.description);
 | 
			
		||||
		AssignIfPresent(RawObject, "owner", Existing.owner);
 | 
			
		||||
		AssignIfPresent(RawObject, "location", Existing.location);
 | 
			
		||||
		AssignIfPresent(RawObject, "locale", Existing.locale);
 | 
			
		||||
		AssignIfPresent(RawObject, "changePassword", Existing.changePassword);
 | 
			
		||||
		AssignIfPresent(RawObject, "suspended", Existing.suspended);
 | 
			
		||||
		AssignIfPresent(RawObject, "blackListed", Existing.blackListed);
 | 
			
		||||
 | 
			
		||||
        if(RawObject->has("userRole")) {
 | 
			
		||||
            auto NewRole = SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString());
 | 
			
		||||
            if(NewRole!=Existing.userRole) {
 | 
			
		||||
                if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && NewRole==SecurityObjects::ROOT) {
 | 
			
		||||
		if (RawObject->has("userRole")) {
 | 
			
		||||
			auto NewRole =
 | 
			
		||||
				SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString());
 | 
			
		||||
			if (NewRole != Existing.userRole) {
 | 
			
		||||
				if (UserInfo_.userinfo.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
					NewRole == SecurityObjects::ROOT) {
 | 
			
		||||
					return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
				}
 | 
			
		||||
                if(Id==UserInfo_.userinfo.id) {
 | 
			
		||||
				if (Id == UserInfo_.userinfo.id) {
 | 
			
		||||
					return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
				}
 | 
			
		||||
				Existing.userRole = NewRole;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(RawObject->has("notes")) {
 | 
			
		||||
		if (RawObject->has("notes")) {
 | 
			
		||||
			SecurityObjects::NoteInfoVec NIV;
 | 
			
		||||
            NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(RawObject->get("notes").toString());
 | 
			
		||||
            for(auto const &i:NIV) {
 | 
			
		||||
                SecurityObjects::NoteInfo   ii{.created=(uint64_t)OpenWifi::Now(), .createdBy=UserInfo_.userinfo.email, .note=i.note};
 | 
			
		||||
			NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(
 | 
			
		||||
				RawObject->get("notes").toString());
 | 
			
		||||
			for (auto const &i : NIV) {
 | 
			
		||||
				SecurityObjects::NoteInfo ii{.created = (uint64_t)OpenWifi::Now(),
 | 
			
		||||
											 .createdBy = UserInfo_.userinfo.email,
 | 
			
		||||
											 .note = i.note};
 | 
			
		||||
				Existing.notes.push_back(ii);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
        if(RawObject->has("currentPassword")) {
 | 
			
		||||
            if(!AuthService()->ValidateSubPassword(RawObject->get("currentPassword").toString())) {
 | 
			
		||||
		if (RawObject->has("currentPassword")) {
 | 
			
		||||
			if (!AuthService()->ValidateSubPassword(RawObject->get("currentPassword").toString())) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::InvalidPassword);
 | 
			
		||||
			}
 | 
			
		||||
            if(!AuthService()->SetPassword(RawObject->get("currentPassword").toString(),Existing)) {
 | 
			
		||||
			if (!AuthService()->SetPassword(RawObject->get("currentPassword").toString(),
 | 
			
		||||
											Existing)) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::PasswordRejected);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetParameter("email_verification","false")=="true") {
 | 
			
		||||
            if(AuthService::VerifySubEmail(Existing))
 | 
			
		||||
                Logger_.information(fmt::format("Verification e-mail requested for {}",Existing.email));
 | 
			
		||||
		if (GetParameter("email_verification", "false") == "true") {
 | 
			
		||||
			if (AuthService::VerifySubEmail(Existing))
 | 
			
		||||
				Logger_.information(
 | 
			
		||||
					fmt::format("Verification e-mail requested for {}", Existing.email));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(RawObject->has("userTypeProprietaryInfo")) {
 | 
			
		||||
            if(NewUser.userTypeProprietaryInfo.mfa.enabled) {
 | 
			
		||||
		if (RawObject->has("userTypeProprietaryInfo")) {
 | 
			
		||||
			if (NewUser.userTypeProprietaryInfo.mfa.enabled) {
 | 
			
		||||
				if (!MFAMETHODS::Validate(NewUser.userTypeProprietaryInfo.mfa.method)) {
 | 
			
		||||
					return BadRequest(RESTAPI::Errors::BadMFAMethod);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
                if( NewUser.userTypeProprietaryInfo.mfa.enabled &&
 | 
			
		||||
				if (NewUser.userTypeProprietaryInfo.mfa.enabled &&
 | 
			
		||||
					NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::SMS &&
 | 
			
		||||
					!SMSSender()->Enabled()) {
 | 
			
		||||
					return BadRequest(RESTAPI::Errors::SMSMFANotEnabled);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
                if( NewUser.userTypeProprietaryInfo.mfa.enabled &&
 | 
			
		||||
				if (NewUser.userTypeProprietaryInfo.mfa.enabled &&
 | 
			
		||||
					NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::EMAIL &&
 | 
			
		||||
					!SMTPMailerService()->Enabled()) {
 | 
			
		||||
					return BadRequest(RESTAPI::Errors::EMailMFANotEnabled);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
                Existing.userTypeProprietaryInfo.mfa.method = NewUser.userTypeProprietaryInfo.mfa.method;
 | 
			
		||||
				Existing.userTypeProprietaryInfo.mfa.method =
 | 
			
		||||
					NewUser.userTypeProprietaryInfo.mfa.method;
 | 
			
		||||
				Existing.userTypeProprietaryInfo.mfa.enabled = true;
 | 
			
		||||
 | 
			
		||||
				if (NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::SMS) {
 | 
			
		||||
                    if(NewUser.userTypeProprietaryInfo.mobiles.empty()) {
 | 
			
		||||
					if (NewUser.userTypeProprietaryInfo.mobiles.empty()) {
 | 
			
		||||
						return BadRequest(RESTAPI::Errors::NeedMobileNumber);
 | 
			
		||||
					}
 | 
			
		||||
                    if (!SMSSender()->IsNumberValid(NewUser.userTypeProprietaryInfo.mobiles[0].number,UserInfo_.userinfo.email)) {
 | 
			
		||||
					if (!SMSSender()->IsNumberValid(
 | 
			
		||||
							NewUser.userTypeProprietaryInfo.mobiles[0].number,
 | 
			
		||||
							UserInfo_.userinfo.email)) {
 | 
			
		||||
						return BadRequest(RESTAPI::Errors::NeedMobileNumber);
 | 
			
		||||
					}
 | 
			
		||||
                    Existing.userTypeProprietaryInfo.mobiles = NewUser.userTypeProprietaryInfo.mobiles;
 | 
			
		||||
					Existing.userTypeProprietaryInfo.mobiles =
 | 
			
		||||
						NewUser.userTypeProprietaryInfo.mobiles;
 | 
			
		||||
					Existing.userTypeProprietaryInfo.mobiles[0].verified = true;
 | 
			
		||||
					Existing.userTypeProprietaryInfo.authenticatorSecret.clear();
 | 
			
		||||
                } else if (NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::AUTHENTICATOR) {
 | 
			
		||||
				} else if (NewUser.userTypeProprietaryInfo.mfa.method ==
 | 
			
		||||
						   MFAMETHODS::AUTHENTICATOR) {
 | 
			
		||||
					std::string Secret;
 | 
			
		||||
					Existing.userTypeProprietaryInfo.mobiles.clear();
 | 
			
		||||
                    if(Existing.userTypeProprietaryInfo.authenticatorSecret.empty() && TotpCache()->CompleteValidation(UserInfo_.userinfo,false,Secret)) {
 | 
			
		||||
					if (Existing.userTypeProprietaryInfo.authenticatorSecret.empty() &&
 | 
			
		||||
						TotpCache()->CompleteValidation(UserInfo_.userinfo, false, Secret)) {
 | 
			
		||||
						Existing.userTypeProprietaryInfo.authenticatorSecret = Secret;
 | 
			
		||||
					} else if (!Existing.userTypeProprietaryInfo.authenticatorSecret.empty()) {
 | 
			
		||||
						// we allow someone to use their old secret
 | 
			
		||||
@@ -305,9 +327,9 @@ namespace OpenWifi {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email,Id,Existing)) {
 | 
			
		||||
		if (StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email, Id, Existing)) {
 | 
			
		||||
			SecurityObjects::UserInfo NewUserInfo;
 | 
			
		||||
            StorageService()->SubDB().GetUserById(Id,NewUserInfo);
 | 
			
		||||
			StorageService()->SubDB().GetUserById(Id, NewUserInfo);
 | 
			
		||||
			Poco::JSON::Object ModifiedObject;
 | 
			
		||||
			Sanitize(UserInfo_, NewUserInfo);
 | 
			
		||||
			NewUserInfo.to_json(ModifiedObject);
 | 
			
		||||
@@ -315,4 +337,4 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
		BadRequest(RESTAPI::Errors::RecordNotUpdated);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -9,23 +9,22 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_subuser_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_subuser_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_subuser_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
								RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
								bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
                         std::vector<std::string>
 | 
			
		||||
                         {Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_PUT,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_DELETE,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                          Server,
 | 
			
		||||
                          TransactionId,
 | 
			
		||||
                          Internal) {}
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/subuser/{id}"}; };
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoPost() final;
 | 
			
		||||
		void DoDelete() final;
 | 
			
		||||
		void DoPut() final;
 | 
			
		||||
    private:
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,8 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_subusers_handler.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_db_helpers.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
@@ -15,46 +15,53 @@ namespace OpenWifi {
 | 
			
		||||
		auto emailSearch = GetParameter("emailSearch");
 | 
			
		||||
 | 
			
		||||
		std::string baseQuery;
 | 
			
		||||
        if(!nameSearch.empty() || !emailSearch.empty()) {
 | 
			
		||||
            if(!nameSearch.empty())
 | 
			
		||||
                baseQuery = fmt::format(" Lower(name) like('%{}%') ", Poco::toLower(nameSearch) );
 | 
			
		||||
            if(!emailSearch.empty())
 | 
			
		||||
                baseQuery += baseQuery.empty() ? fmt::format(" Lower(email) like('%{}%') ", Poco::toLower(emailSearch))
 | 
			
		||||
                : fmt::format(" and Lower(email) like('%{}%') ", Poco::toLower(emailSearch));
 | 
			
		||||
		if (!nameSearch.empty() || !emailSearch.empty()) {
 | 
			
		||||
			if (!nameSearch.empty())
 | 
			
		||||
				baseQuery = fmt::format(" Lower(name) like('%{}%') ",
 | 
			
		||||
										ORM::Escape(Poco::toLower(nameSearch)));
 | 
			
		||||
			if (!emailSearch.empty())
 | 
			
		||||
				baseQuery += baseQuery.empty()
 | 
			
		||||
								 ? fmt::format(" Lower(email) like('%{}%') ",
 | 
			
		||||
											   ORM::Escape(Poco::toLower(emailSearch)))
 | 
			
		||||
								 : fmt::format(" and Lower(email) like('%{}%') ",
 | 
			
		||||
											   ORM::Escape(Poco::toLower(emailSearch)));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(QB_.CountOnly) {
 | 
			
		||||
		if (QB_.CountOnly) {
 | 
			
		||||
			std::string whereClause;
 | 
			
		||||
            if(!operatorId.empty()) {
 | 
			
		||||
                whereClause = baseQuery.empty() ? fmt::format(" owner='{}' ", operatorId) :
 | 
			
		||||
                              fmt::format(" owner='{}' and {} ", operatorId, baseQuery);
 | 
			
		||||
			if (!operatorId.empty() && Utils::ValidUUID(operatorId)) {
 | 
			
		||||
				whereClause = baseQuery.empty()
 | 
			
		||||
								  ? fmt::format(" owner='{}' ", operatorId)
 | 
			
		||||
								  : fmt::format(" owner='{}' and {} ", operatorId, baseQuery);
 | 
			
		||||
				auto count = StorageService()->SubDB().Count(whereClause);
 | 
			
		||||
				return ReturnCountOnly(count);
 | 
			
		||||
			}
 | 
			
		||||
			auto count = StorageService()->UserDB().Count();
 | 
			
		||||
			return ReturnCountOnly(count);
 | 
			
		||||
        } else if(QB_.Select.empty()) {
 | 
			
		||||
		} else if (QB_.Select.empty()) {
 | 
			
		||||
			std::string whereClause;
 | 
			
		||||
            if(!operatorId.empty()) {
 | 
			
		||||
                whereClause = baseQuery.empty() ? fmt::format(" owner='{}' ", operatorId) :
 | 
			
		||||
                              fmt::format(" owner='{}' and {} ", operatorId, baseQuery);
 | 
			
		||||
			if (!operatorId.empty() && Utils::ValidUUID(operatorId)) {
 | 
			
		||||
				whereClause = baseQuery.empty()
 | 
			
		||||
								  ? fmt::format(" owner='{}' ", operatorId)
 | 
			
		||||
								  : fmt::format(" owner='{}' and {} ", operatorId, baseQuery);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			SecurityObjects::UserInfoList Users;
 | 
			
		||||
            if (StorageService()->SubDB().GetUsers(QB_.Offset, QB_.Limit, Users.users, whereClause)) {
 | 
			
		||||
			if (StorageService()->SubDB().GetUsers(QB_.Offset, QB_.Limit, Users.users,
 | 
			
		||||
												   whereClause)) {
 | 
			
		||||
				for (auto &i : Users.users) {
 | 
			
		||||
					Sanitize(UserInfo_, i);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            if(IdOnly) {
 | 
			
		||||
			if (IdOnly) {
 | 
			
		||||
				Poco::JSON::Array Arr;
 | 
			
		||||
				Poco::JSON::Object Answer;
 | 
			
		||||
 | 
			
		||||
                for(const auto &i:Users.users) {
 | 
			
		||||
				for (const auto &i : Users.users) {
 | 
			
		||||
					Arr.add(i.id);
 | 
			
		||||
				}
 | 
			
		||||
                Answer.set("users",Arr);
 | 
			
		||||
				Answer.set("users", Arr);
 | 
			
		||||
				return ReturnObject(Answer);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -63,9 +70,9 @@ namespace OpenWifi {
 | 
			
		||||
			return ReturnObject(Answer);
 | 
			
		||||
		} else {
 | 
			
		||||
			SecurityObjects::UserInfoList Users;
 | 
			
		||||
            for(auto &i:SelectedRecords()) {
 | 
			
		||||
			for (auto &i : SelectedRecords()) {
 | 
			
		||||
				SecurityObjects::UserInfo UInfo;
 | 
			
		||||
                if(StorageService()->SubDB().GetUserById(i,UInfo)) {
 | 
			
		||||
				if (StorageService()->SubDB().GetUserById(i, UInfo)) {
 | 
			
		||||
					Poco::JSON::Object Obj;
 | 
			
		||||
					Sanitize(UserInfo_, UInfo);
 | 
			
		||||
					Users.users.emplace_back(UInfo);
 | 
			
		||||
@@ -76,4 +83,4 @@ namespace OpenWifi {
 | 
			
		||||
			return ReturnObject(Answer);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -9,18 +9,17 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_subusers_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_subusers_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_subusers_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
								 RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
								 bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
                         std::vector<std::string>
 | 
			
		||||
                         {Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                          Server,
 | 
			
		||||
                          TransactionId,
 | 
			
		||||
                          Internal) {}
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/subusers"}; };
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
        void DoPost() final {};
 | 
			
		||||
        void DoDelete() final {};
 | 
			
		||||
        void DoPut() final {};
 | 
			
		||||
		void DoPost() final{};
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
		void DoPut() final{};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
}; // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										96
									
								
								src/RESTAPI/RESTAPI_systemSecret_handler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,96 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by stephane bourque on 2023-01-25.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_systemSecret_handler.h"
 | 
			
		||||
#include <SecretStore.h>
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_systemSecret_handler::DoGet() {
 | 
			
		||||
		if (!Internal_ && UserInfo_.userinfo.userRole != SecurityObjects::ROOT) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (GetBoolParameter("all")) {
 | 
			
		||||
			auto Store = SecretStore()->Store();
 | 
			
		||||
			Poco::JSON::Array Entries;
 | 
			
		||||
			Poco::JSON::Object List;
 | 
			
		||||
 | 
			
		||||
			for (const auto &[Key, Value] : Store) {
 | 
			
		||||
				Poco::JSON::Object E;
 | 
			
		||||
				E.set("key", Key);
 | 
			
		||||
				E.set("value", Value);
 | 
			
		||||
				Entries.add(E);
 | 
			
		||||
			}
 | 
			
		||||
			List.set("secrets", Entries);
 | 
			
		||||
			return ReturnObject(List);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (GetBoolParameter("dictionary")) {
 | 
			
		||||
			static std::vector<std::pair<std::string, std::string>> KnownKeys = {
 | 
			
		||||
				{"google.maps.apikey", "A Google Key specific for the Google MAPS API."},
 | 
			
		||||
				{"iptocountry.ipinfo.token", "IPInfo.io service token."},
 | 
			
		||||
				{"iptocountry.ipdata.apikey", "IPData.co API Key."},
 | 
			
		||||
				{"iptocountry.ip2location.apikey", "IP2Location.com API Key"}};
 | 
			
		||||
 | 
			
		||||
			Poco::JSON::Object Answer;
 | 
			
		||||
			Poco::JSON::Array Entries;
 | 
			
		||||
			for (const auto &[key, description] : KnownKeys) {
 | 
			
		||||
				Poco::JSON::Object E;
 | 
			
		||||
				E.set("key", key);
 | 
			
		||||
				E.set("description", description);
 | 
			
		||||
				Entries.add(E);
 | 
			
		||||
			}
 | 
			
		||||
			Answer.set("knownKeys", Entries);
 | 
			
		||||
			return ReturnObject(Answer);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto Key = GetBinding("secret");
 | 
			
		||||
		if (Key.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::string Value;
 | 
			
		||||
		if (SecretStore()->Get(Key, Value, "")) {
 | 
			
		||||
			Poco::JSON::Object Answer;
 | 
			
		||||
			Answer.set("key", Key);
 | 
			
		||||
			Answer.set("value", Value);
 | 
			
		||||
			return ReturnObject(Answer);
 | 
			
		||||
		}
 | 
			
		||||
		return NotFound();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_systemSecret_handler::DoDelete() {
 | 
			
		||||
		if (UserInfo_.userinfo.userRole != SecurityObjects::ROOT) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto Key = GetBinding("secret");
 | 
			
		||||
		if (Key.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecretStore()->Remove(Key);
 | 
			
		||||
		return OK();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_systemSecret_handler::DoPut() {
 | 
			
		||||
		if (UserInfo_.userinfo.userRole != SecurityObjects::ROOT) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto Key = GetBinding("secret");
 | 
			
		||||
		auto Value = GetParameter("value", "_______no_value_____");
 | 
			
		||||
		if (Key.empty() || Value == "_______no_value_____") {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecretStore()->Set(Key, Value);
 | 
			
		||||
		Poco::JSON::Object Answer;
 | 
			
		||||
		Answer.set("key", Key);
 | 
			
		||||
		Answer.set("value", Value);
 | 
			
		||||
		return ReturnObject(Answer);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
							
								
								
									
										29
									
								
								src/RESTAPI/RESTAPI_systemSecret_handler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,29 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by stephane bourque on 2023-01-25.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "framework/RESTAPI_Handler.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_systemSecret_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
		RESTAPI_systemSecret_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
									 RESTAPI_GenericServerAccounting &Server,
 | 
			
		||||
									 uint64_t TransactionId, bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_PUT,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_DELETE,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/systemSecret/{secret}"}; };
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoPost() final{};
 | 
			
		||||
		void DoDelete() final;
 | 
			
		||||
		void DoPut() final;
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
	};
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -11,15 +11,12 @@ namespace OpenWifi {
 | 
			
		||||
	void RESTAPI_system_endpoints_handler::DoGet() {
 | 
			
		||||
		auto Services = MicroServiceGetServices();
 | 
			
		||||
		SecurityObjects::SystemEndpointList L;
 | 
			
		||||
        for(const auto &i:Services) {
 | 
			
		||||
            SecurityObjects::SystemEndpoint S{
 | 
			
		||||
                .type = i.Type,
 | 
			
		||||
                .id = i.Id,
 | 
			
		||||
                .uri = i.PublicEndPoint};
 | 
			
		||||
		for (const auto &i : Services) {
 | 
			
		||||
			SecurityObjects::SystemEndpoint S{.type = i.Type, .id = i.Id, .uri = i.PublicEndPoint};
 | 
			
		||||
			L.endpoints.push_back(S);
 | 
			
		||||
		}
 | 
			
		||||
		Poco::JSON::Object Obj;
 | 
			
		||||
		L.to_json(Obj);
 | 
			
		||||
		ReturnObject(Obj);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -9,17 +9,17 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_system_endpoints_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_system_endpoints_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_system_endpoints_handler(const RESTAPIHandler::BindingMap &bindings,
 | 
			
		||||
										 Poco::Logger &L, RESTAPI_GenericServerAccounting &Server,
 | 
			
		||||
										 uint64_t TransactionId, bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                                                          Server,
 | 
			
		||||
                                                          TransactionId,
 | 
			
		||||
                                                          Internal) {}
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/systemEndpoints"}; };
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
        void DoPost() final {};
 | 
			
		||||
        void DoDelete() final {};
 | 
			
		||||
        void DoPut() final {};
 | 
			
		||||
		void DoPost() final{};
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
		void DoPut() final{};
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -9,21 +9,22 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_totp_handler::DoGet() {
 | 
			
		||||
 | 
			
		||||
        auto Reset = GetBoolParameter("reset",false);
 | 
			
		||||
		auto Reset = GetBoolParameter("reset", false);
 | 
			
		||||
		std::string QRCode;
 | 
			
		||||
        if(TotpCache()->StartValidation(UserInfo_.userinfo,false,QRCode,Reset)) {
 | 
			
		||||
            return SendFileContent(QRCode, "image/svg+xml","qrcode.svg");
 | 
			
		||||
		if (TotpCache()->StartValidation(UserInfo_.userinfo, false, QRCode, Reset)) {
 | 
			
		||||
			return SendFileContent(QRCode, "image/svg+xml", "qrcode.svg");
 | 
			
		||||
		}
 | 
			
		||||
		return BadRequest(RESTAPI::Errors::InvalidCommand);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_totp_handler::DoPut() {
 | 
			
		||||
        auto Value = GetParameter("value","");
 | 
			
		||||
        auto nextIndex = GetParameter("index",0);
 | 
			
		||||
        bool moreCodes=false;
 | 
			
		||||
		auto Value = GetParameter("value", "");
 | 
			
		||||
		auto nextIndex = GetParameter("index", 0);
 | 
			
		||||
		bool moreCodes = false;
 | 
			
		||||
 | 
			
		||||
		RESTAPI::Errors::msg Err;
 | 
			
		||||
        if(TotpCache()->ContinueValidation(UserInfo_.userinfo,false,Value,nextIndex,moreCodes, Err)) {
 | 
			
		||||
		if (TotpCache()->ContinueValidation(UserInfo_.userinfo, false, Value, nextIndex, moreCodes,
 | 
			
		||||
											Err)) {
 | 
			
		||||
			Poco::JSON::Object Answer;
 | 
			
		||||
			Answer.set("nextIndex", nextIndex);
 | 
			
		||||
			Answer.set("moreCodes", moreCodes);
 | 
			
		||||
@@ -32,4 +33,4 @@ namespace OpenWifi {
 | 
			
		||||
		return BadRequest(Err);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -9,23 +9,20 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_totp_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_totp_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_totp_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							 RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							 bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
                                 std::vector<std::string>
 | 
			
		||||
                                         {
 | 
			
		||||
                                              Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_PUT,
 | 
			
		||||
                                              Poco::Net::HTTPRequest::HTTP_OPTIONS
 | 
			
		||||
                                          },
 | 
			
		||||
                                 Server,
 | 
			
		||||
                                 TransactionId,
 | 
			
		||||
                                 Internal) {}
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/totp"}; };
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
        void DoPost() final {};
 | 
			
		||||
        void DoDelete() final {};
 | 
			
		||||
		void DoPost() final{};
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
		void DoPut() final;
 | 
			
		||||
    private:
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -3,37 +3,37 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_user_handler.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
#include "SMSSender.h"
 | 
			
		||||
#include "SMTPMailerService.h"
 | 
			
		||||
#include "ACLProcessor.h"
 | 
			
		||||
#include "AuthService.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_db_helpers.h"
 | 
			
		||||
#include "MFAServer.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_db_helpers.h"
 | 
			
		||||
#include "SMSSender.h"
 | 
			
		||||
#include "SMTPMailerService.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "TotpCache.h"
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_user_handler::DoGet() {
 | 
			
		||||
		std::string Id = GetBinding("id", "");
 | 
			
		||||
        if(Id.empty()) {
 | 
			
		||||
		if (Id.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingUserID);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Poco::toLowerInPlace(Id);
 | 
			
		||||
		std::string Arg;
 | 
			
		||||
		SecurityObjects::UserInfo UInfo;
 | 
			
		||||
        if(HasParameter("byEmail",Arg) && Arg=="true") {
 | 
			
		||||
            if(!StorageService()->UserDB().GetUserByEmail(Id,UInfo)) {
 | 
			
		||||
		if (HasParameter("byEmail", Arg) && Arg == "true") {
 | 
			
		||||
			if (!StorageService()->UserDB().GetUserByEmail(Id, UInfo)) {
 | 
			
		||||
				return NotFound();
 | 
			
		||||
			}
 | 
			
		||||
        } else if(!StorageService()->UserDB().GetUserById(Id,UInfo)) {
 | 
			
		||||
		} else if (!StorageService()->UserDB().GetUserById(Id, UInfo)) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!ACLProcessor::Can(UserInfo_.userinfo, UInfo,ACLProcessor::READ)) {
 | 
			
		||||
		if (!Internal_ && !ACLProcessor::Can(UserInfo_.userinfo, UInfo, ACLProcessor::READ)) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -45,76 +45,77 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_user_handler::DoDelete() {
 | 
			
		||||
		std::string Id = GetBinding("id", "");
 | 
			
		||||
        if(Id.empty()) {
 | 
			
		||||
		if (Id.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingUserID);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::UserInfo UInfo;
 | 
			
		||||
        if(!StorageService()->UserDB().GetUserById(Id,UInfo)) {
 | 
			
		||||
		if (!StorageService()->UserDB().GetUserById(Id, UInfo)) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!ACLProcessor::Can(UserInfo_.userinfo, UInfo,ACLProcessor::DELETE)) {
 | 
			
		||||
		if (!ACLProcessor::Can(UserInfo_.userinfo, UInfo, ACLProcessor::DELETE)) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!StorageService()->UserDB().DeleteUser(UserInfo_.userinfo.email,Id)) {
 | 
			
		||||
		if (!StorageService()->UserDB().DeleteUser(UserInfo_.userinfo.email, Id)) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		AuthService()->DeleteUserFromCache(Id);
 | 
			
		||||
        StorageService()->AvatarDB().DeleteAvatar(UserInfo_.userinfo.email,Id);
 | 
			
		||||
        StorageService()->PreferencesDB().DeletePreferences(UserInfo_.userinfo.email,Id);
 | 
			
		||||
		StorageService()->AvatarDB().DeleteAvatar(UserInfo_.userinfo.email, Id);
 | 
			
		||||
		StorageService()->PreferencesDB().DeletePreferences(UserInfo_.userinfo.email, Id);
 | 
			
		||||
		StorageService()->UserTokenDB().RevokeAllTokens(Id);
 | 
			
		||||
		StorageService()->ApiKeyDB().RemoveAllApiKeys(Id);
 | 
			
		||||
        Logger_.information(fmt::format("User '{}' deleted by '{}'.",Id,UserInfo_.userinfo.email));
 | 
			
		||||
		Logger_.information(
 | 
			
		||||
			fmt::format("User '{}' deleted by '{}'.", Id, UserInfo_.userinfo.email));
 | 
			
		||||
		OK();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_user_handler::DoPost() {
 | 
			
		||||
 | 
			
		||||
		std::string Id = GetBinding("id", "");
 | 
			
		||||
        if(Id!="0") {
 | 
			
		||||
		if (Id != "0") {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::IdMustBe0);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::UserInfo NewUser;
 | 
			
		||||
        const auto & RawObject = ParsedBody_;
 | 
			
		||||
        if(!NewUser.from_json(RawObject)) {
 | 
			
		||||
		const auto &RawObject = ParsedBody_;
 | 
			
		||||
		if (!NewUser.from_json(RawObject)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(NewUser.userRole == SecurityObjects::UNKNOWN) {
 | 
			
		||||
		if (NewUser.userRole == SecurityObjects::UNKNOWN) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidUserRole);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(UserInfo_.userinfo.userRole==SecurityObjects::ROOT) {
 | 
			
		||||
            NewUser.owner = GetParameter("entity","");
 | 
			
		||||
		if (UserInfo_.userinfo.userRole == SecurityObjects::ROOT) {
 | 
			
		||||
			NewUser.owner = GetParameter("entity", "");
 | 
			
		||||
		} else {
 | 
			
		||||
			NewUser.owner = UserInfo_.userinfo.owner;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!ACLProcessor::Can(UserInfo_.userinfo,NewUser,ACLProcessor::CREATE)) {
 | 
			
		||||
		if (!ACLProcessor::Can(UserInfo_.userinfo, NewUser, ACLProcessor::CREATE)) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Poco::toLowerInPlace(NewUser.email);
 | 
			
		||||
        if(!Utils::ValidEMailAddress(NewUser.email)) {
 | 
			
		||||
		if (!Utils::ValidEMailAddress(NewUser.email)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidEmailAddress);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::UserInfo Existing;
 | 
			
		||||
        if(StorageService()->SubDB().GetUserByEmail(NewUser.email,Existing)) {
 | 
			
		||||
		if (StorageService()->SubDB().GetUserByEmail(NewUser.email, Existing)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::UserAlreadyExists);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!NewUser.currentPassword.empty()) {
 | 
			
		||||
            if(!AuthService()->ValidatePassword(NewUser.currentPassword)) {
 | 
			
		||||
		if (!NewUser.currentPassword.empty()) {
 | 
			
		||||
			if (!AuthService()->ValidatePassword(NewUser.currentPassword)) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::InvalidPassword);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(NewUser.name.empty())
 | 
			
		||||
		if (NewUser.name.empty())
 | 
			
		||||
			NewUser.name = NewUser.email;
 | 
			
		||||
 | 
			
		||||
		//  You cannot enable MFA during user creation
 | 
			
		||||
@@ -124,19 +125,21 @@ namespace OpenWifi {
 | 
			
		||||
		NewUser.userTypeProprietaryInfo.authenticatorSecret.clear();
 | 
			
		||||
		NewUser.validated = true;
 | 
			
		||||
 | 
			
		||||
        if(!StorageService()->UserDB().CreateUser(NewUser.email,NewUser)) {
 | 
			
		||||
            Logger_.information(fmt::format("Could not add user '{}'.",NewUser.email));
 | 
			
		||||
		if (!StorageService()->UserDB().CreateUser(NewUser.email, NewUser)) {
 | 
			
		||||
			Logger_.information(fmt::format("Could not add user '{}'.", NewUser.email));
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::RecordNotCreated);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter("email_verification")) {
 | 
			
		||||
            if(AuthService::VerifyEmail(NewUser))
 | 
			
		||||
                Logger_.information(fmt::format("Verification e-mail requested for {}",NewUser.email));
 | 
			
		||||
            StorageService()->UserDB().UpdateUserInfo(UserInfo_.userinfo.email,NewUser.id,NewUser);
 | 
			
		||||
		if (GetBoolParameter("email_verification")) {
 | 
			
		||||
			if (AuthService::VerifyEmail(NewUser))
 | 
			
		||||
				Logger_.information(
 | 
			
		||||
					fmt::format("Verification e-mail requested for {}", NewUser.email));
 | 
			
		||||
			StorageService()->UserDB().UpdateUserInfo(UserInfo_.userinfo.email, NewUser.id,
 | 
			
		||||
													  NewUser);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!StorageService()->UserDB().GetUserByEmail(NewUser.email, NewUser)) {
 | 
			
		||||
            Logger_.information(fmt::format("User '{}' but not retrieved.",NewUser.email));
 | 
			
		||||
		if (!StorageService()->UserDB().GetUserByEmail(NewUser.email, NewUser)) {
 | 
			
		||||
			Logger_.information(fmt::format("User '{}' but not retrieved.", NewUser.email));
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -144,40 +147,42 @@ namespace OpenWifi {
 | 
			
		||||
		Sanitize(UserInfo_, NewUser);
 | 
			
		||||
		NewUser.to_json(UserInfoObject);
 | 
			
		||||
		ReturnObject(UserInfoObject);
 | 
			
		||||
        Logger_.information(fmt::format("User '{}' has been added by '{}')",NewUser.email, UserInfo_.userinfo.email));
 | 
			
		||||
		Logger_.information(fmt::format("User '{}' has been added by '{}')", NewUser.email,
 | 
			
		||||
										UserInfo_.userinfo.email));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_user_handler::DoPut() {
 | 
			
		||||
 | 
			
		||||
		std::string Id = GetBinding("id", "");
 | 
			
		||||
        if(Id.empty()) {
 | 
			
		||||
		if (Id.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingUserID);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::UserInfo Existing;
 | 
			
		||||
        if(!StorageService()->UserDB().GetUserById(Id,Existing)) {
 | 
			
		||||
		if (!StorageService()->UserDB().GetUserById(Id, Existing)) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(!ACLProcessor::Can(UserInfo_.userinfo,Existing,ACLProcessor::MODIFY)) {
 | 
			
		||||
		if (!ACLProcessor::Can(UserInfo_.userinfo, Existing, ACLProcessor::MODIFY)) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter("resetMFA")) {
 | 
			
		||||
            if( (UserInfo_.userinfo.userRole == SecurityObjects::ROOT) ||
 | 
			
		||||
                (UserInfo_.userinfo.userRole == SecurityObjects::ADMIN && Existing.userRole!=SecurityObjects::ROOT) ||
 | 
			
		||||
		if (GetBoolParameter("resetMFA")) {
 | 
			
		||||
			if ((UserInfo_.userinfo.userRole == SecurityObjects::ROOT) ||
 | 
			
		||||
				(UserInfo_.userinfo.userRole == SecurityObjects::ADMIN &&
 | 
			
		||||
				 Existing.userRole != SecurityObjects::ROOT) ||
 | 
			
		||||
				(UserInfo_.userinfo.id == Id)) {
 | 
			
		||||
				Existing.userTypeProprietaryInfo.mfa.enabled = false;
 | 
			
		||||
				Existing.userTypeProprietaryInfo.mfa.method.clear();
 | 
			
		||||
				Existing.userTypeProprietaryInfo.mobiles.clear();
 | 
			
		||||
				Existing.modified = OpenWifi::Now();
 | 
			
		||||
                Existing.notes.push_back( SecurityObjects::NoteInfo{
 | 
			
		||||
                            .created=OpenWifi::Now(),
 | 
			
		||||
                            .createdBy=UserInfo_.userinfo.email,
 | 
			
		||||
                            .note="MFA Reset by " + UserInfo_.userinfo.email});
 | 
			
		||||
                StorageService()->UserDB().UpdateUserInfo(UserInfo_.userinfo.email,Id,Existing);
 | 
			
		||||
				Existing.notes.push_back(
 | 
			
		||||
					SecurityObjects::NoteInfo{.created = OpenWifi::Now(),
 | 
			
		||||
											  .createdBy = UserInfo_.userinfo.email,
 | 
			
		||||
											  .note = "MFA Reset by " + UserInfo_.userinfo.email});
 | 
			
		||||
				StorageService()->UserDB().UpdateUserInfo(UserInfo_.userinfo.email, Id, Existing);
 | 
			
		||||
				SecurityObjects::UserInfo NewUserInfo;
 | 
			
		||||
                StorageService()->UserDB().GetUserByEmail(UserInfo_.userinfo.email,NewUserInfo);
 | 
			
		||||
				StorageService()->UserDB().GetUserByEmail(UserInfo_.userinfo.email, NewUserInfo);
 | 
			
		||||
				Poco::JSON::Object ModifiedObject;
 | 
			
		||||
				Sanitize(UserInfo_, NewUserInfo);
 | 
			
		||||
				NewUserInfo.to_json(ModifiedObject);
 | 
			
		||||
@@ -187,117 +192,133 @@ namespace OpenWifi {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter("forgotPassword")) {
 | 
			
		||||
		if (GetBoolParameter("forgotPassword")) {
 | 
			
		||||
			Existing.changePassword = true;
 | 
			
		||||
            Logger_.information(fmt::format("FORGOTTEN-PASSWORD({}): Request for {}", Request->clientAddress().toString(), Existing.email));
 | 
			
		||||
			Logger_.information(fmt::format("FORGOTTEN-PASSWORD({}): Request for {}",
 | 
			
		||||
											Request->clientAddress().toString(), Existing.email));
 | 
			
		||||
			SecurityObjects::ActionLink NewLink;
 | 
			
		||||
 | 
			
		||||
			NewLink.action = OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD;
 | 
			
		||||
			NewLink.id = MicroServiceCreateUUID();
 | 
			
		||||
			NewLink.userId = Existing.id;
 | 
			
		||||
			NewLink.created = OpenWifi::Now();
 | 
			
		||||
            NewLink.expires = NewLink.created + (24*60*60);
 | 
			
		||||
			NewLink.expires = NewLink.created + (24 * 60 * 60);
 | 
			
		||||
			NewLink.userAction = true;
 | 
			
		||||
			StorageService()->ActionLinksDB().CreateAction(NewLink);
 | 
			
		||||
			return OK();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SecurityObjects::UserInfo NewUser;
 | 
			
		||||
        const auto & RawObject = ParsedBody_;
 | 
			
		||||
        if(!NewUser.from_json(RawObject)) {
 | 
			
		||||
		const auto &RawObject = ParsedBody_;
 | 
			
		||||
		if (!NewUser.from_json(RawObject)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// some basic validations
 | 
			
		||||
        if(RawObject->has("userRole") && SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString())==SecurityObjects::UNKNOWN) {
 | 
			
		||||
		if (RawObject->has("userRole") &&
 | 
			
		||||
			SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString()) ==
 | 
			
		||||
				SecurityObjects::UNKNOWN) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidUserRole);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(RawObject->has("owner")) {
 | 
			
		||||
		if (RawObject->has("owner")) {
 | 
			
		||||
			if (UserInfo_.userinfo.userRole == SecurityObjects::ROOT && Existing.owner.empty()) {
 | 
			
		||||
				AssignIfPresent(RawObject, "owner", Existing.owner);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// The only valid things to change are: changePassword, name,
 | 
			
		||||
        AssignIfPresent(RawObject,"name", Existing.name);
 | 
			
		||||
        AssignIfPresent(RawObject,"description", Existing.description);
 | 
			
		||||
        AssignIfPresent(RawObject,"location", Existing.location);
 | 
			
		||||
        AssignIfPresent(RawObject,"locale", Existing.locale);
 | 
			
		||||
        AssignIfPresent(RawObject,"changePassword", Existing.changePassword);
 | 
			
		||||
        AssignIfPresent(RawObject,"suspended", Existing.suspended);
 | 
			
		||||
        AssignIfPresent(RawObject,"blackListed", Existing.blackListed);
 | 
			
		||||
		AssignIfPresent(RawObject, "name", Existing.name);
 | 
			
		||||
		AssignIfPresent(RawObject, "description", Existing.description);
 | 
			
		||||
		AssignIfPresent(RawObject, "location", Existing.location);
 | 
			
		||||
		AssignIfPresent(RawObject, "locale", Existing.locale);
 | 
			
		||||
		AssignIfPresent(RawObject, "changePassword", Existing.changePassword);
 | 
			
		||||
		AssignIfPresent(RawObject, "suspended", Existing.suspended);
 | 
			
		||||
		AssignIfPresent(RawObject, "blackListed", Existing.blackListed);
 | 
			
		||||
 | 
			
		||||
        if(RawObject->has("userRole")) {
 | 
			
		||||
            auto NewRole = SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString());
 | 
			
		||||
            if(NewRole!=Existing.userRole) {
 | 
			
		||||
                if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && NewRole==SecurityObjects::ROOT) {
 | 
			
		||||
		if (RawObject->has("userRole")) {
 | 
			
		||||
			auto NewRole =
 | 
			
		||||
				SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString());
 | 
			
		||||
			if (NewRole != Existing.userRole) {
 | 
			
		||||
				if (UserInfo_.userinfo.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
					NewRole == SecurityObjects::ROOT) {
 | 
			
		||||
					return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
				}
 | 
			
		||||
                if(Id==UserInfo_.userinfo.id) {
 | 
			
		||||
				if (Id == UserInfo_.userinfo.id) {
 | 
			
		||||
					return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
				}
 | 
			
		||||
				Existing.userRole = NewRole;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(RawObject->has("notes")) {
 | 
			
		||||
		if (RawObject->has("notes")) {
 | 
			
		||||
			SecurityObjects::NoteInfoVec NIV;
 | 
			
		||||
            NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(RawObject->get("notes").toString());
 | 
			
		||||
            for(auto const &i:NIV) {
 | 
			
		||||
                SecurityObjects::NoteInfo   ii{.created=(uint64_t)OpenWifi::Now(), .createdBy=UserInfo_.userinfo.email, .note=i.note};
 | 
			
		||||
			NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(
 | 
			
		||||
				RawObject->get("notes").toString());
 | 
			
		||||
			for (auto const &i : NIV) {
 | 
			
		||||
				SecurityObjects::NoteInfo ii{.created = (uint64_t)OpenWifi::Now(),
 | 
			
		||||
											 .createdBy = UserInfo_.userinfo.email,
 | 
			
		||||
											 .note = i.note};
 | 
			
		||||
				Existing.notes.push_back(ii);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
        if(RawObject->has("currentPassword")) {
 | 
			
		||||
            if(!AuthService()->ValidatePassword(RawObject->get("currentPassword").toString())) {
 | 
			
		||||
		if (RawObject->has("currentPassword")) {
 | 
			
		||||
			if (!AuthService()->ValidatePassword(RawObject->get("currentPassword").toString())) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::InvalidPassword);
 | 
			
		||||
			}
 | 
			
		||||
            if(!AuthService()->SetPassword(RawObject->get("currentPassword").toString(),Existing)) {
 | 
			
		||||
			if (!AuthService()->SetPassword(RawObject->get("currentPassword").toString(),
 | 
			
		||||
											Existing)) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::PasswordRejected);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(GetBoolParameter("email_verification")) {
 | 
			
		||||
            if(AuthService::VerifyEmail(Existing))
 | 
			
		||||
                Logger_.information(fmt::format("Verification e-mail requested for {}",Existing.email));
 | 
			
		||||
		if (GetBoolParameter("email_verification")) {
 | 
			
		||||
			if (AuthService::VerifyEmail(Existing))
 | 
			
		||||
				Logger_.information(
 | 
			
		||||
					fmt::format("Verification e-mail requested for {}", Existing.email));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(RawObject->has("userTypeProprietaryInfo")) {
 | 
			
		||||
            if(NewUser.userTypeProprietaryInfo.mfa.enabled) {
 | 
			
		||||
		if (RawObject->has("userTypeProprietaryInfo")) {
 | 
			
		||||
			if (NewUser.userTypeProprietaryInfo.mfa.enabled) {
 | 
			
		||||
				if (!MFAMETHODS::Validate(NewUser.userTypeProprietaryInfo.mfa.method)) {
 | 
			
		||||
					return BadRequest(RESTAPI::Errors::BadMFAMethod);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
                if( NewUser.userTypeProprietaryInfo.mfa.enabled &&
 | 
			
		||||
				if (NewUser.userTypeProprietaryInfo.mfa.enabled &&
 | 
			
		||||
					NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::SMS &&
 | 
			
		||||
					!SMSSender()->Enabled()) {
 | 
			
		||||
					return BadRequest(RESTAPI::Errors::SMSMFANotEnabled);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
                if( NewUser.userTypeProprietaryInfo.mfa.enabled &&
 | 
			
		||||
				if (NewUser.userTypeProprietaryInfo.mfa.enabled &&
 | 
			
		||||
					NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::EMAIL &&
 | 
			
		||||
					!SMTPMailerService()->Enabled()) {
 | 
			
		||||
					return BadRequest(RESTAPI::Errors::EMailMFANotEnabled);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
                Existing.userTypeProprietaryInfo.mfa.method = NewUser.userTypeProprietaryInfo.mfa.method;
 | 
			
		||||
				Existing.userTypeProprietaryInfo.mfa.method =
 | 
			
		||||
					NewUser.userTypeProprietaryInfo.mfa.method;
 | 
			
		||||
				Existing.userTypeProprietaryInfo.mfa.enabled = true;
 | 
			
		||||
 | 
			
		||||
				if (NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::SMS) {
 | 
			
		||||
                    if(NewUser.userTypeProprietaryInfo.mobiles.empty()) {
 | 
			
		||||
					if (NewUser.userTypeProprietaryInfo.mobiles.empty()) {
 | 
			
		||||
						return BadRequest(RESTAPI::Errors::NeedMobileNumber);
 | 
			
		||||
					}
 | 
			
		||||
                    if (!SMSSender()->IsNumberValid(NewUser.userTypeProprietaryInfo.mobiles[0].number,UserInfo_.userinfo.email)) {
 | 
			
		||||
					if (!SMSSender()->IsNumberValid(
 | 
			
		||||
							NewUser.userTypeProprietaryInfo.mobiles[0].number,
 | 
			
		||||
							UserInfo_.userinfo.email)) {
 | 
			
		||||
						return BadRequest(RESTAPI::Errors::NeedMobileNumber);
 | 
			
		||||
					}
 | 
			
		||||
                    Existing.userTypeProprietaryInfo.mobiles = NewUser.userTypeProprietaryInfo.mobiles;
 | 
			
		||||
					Existing.userTypeProprietaryInfo.mobiles =
 | 
			
		||||
						NewUser.userTypeProprietaryInfo.mobiles;
 | 
			
		||||
					Existing.userTypeProprietaryInfo.mobiles[0].verified = true;
 | 
			
		||||
					Existing.userTypeProprietaryInfo.authenticatorSecret.clear();
 | 
			
		||||
                } else if (NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::AUTHENTICATOR) {
 | 
			
		||||
				} else if (NewUser.userTypeProprietaryInfo.mfa.method ==
 | 
			
		||||
						   MFAMETHODS::AUTHENTICATOR) {
 | 
			
		||||
					std::string Secret;
 | 
			
		||||
					Existing.userTypeProprietaryInfo.mobiles.clear();
 | 
			
		||||
                    if(Existing.userTypeProprietaryInfo.authenticatorSecret.empty() && TotpCache()->CompleteValidation(UserInfo_.userinfo,false,Secret)) {
 | 
			
		||||
					if (Existing.userTypeProprietaryInfo.authenticatorSecret.empty() &&
 | 
			
		||||
						TotpCache()->CompleteValidation(UserInfo_.userinfo, false, Secret)) {
 | 
			
		||||
						Existing.userTypeProprietaryInfo.authenticatorSecret = Secret;
 | 
			
		||||
					} else if (!Existing.userTypeProprietaryInfo.authenticatorSecret.empty()) {
 | 
			
		||||
						// we allow someone to use their old secret
 | 
			
		||||
@@ -316,9 +337,9 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Existing.modified = OpenWifi::Now();
 | 
			
		||||
        if(StorageService()->UserDB().UpdateUserInfo(UserInfo_.userinfo.email,Id,Existing)) {
 | 
			
		||||
		if (StorageService()->UserDB().UpdateUserInfo(UserInfo_.userinfo.email, Id, Existing)) {
 | 
			
		||||
			SecurityObjects::UserInfo NewUserInfo;
 | 
			
		||||
            StorageService()->UserDB().GetUserByEmail(UserInfo_.userinfo.email,NewUserInfo);
 | 
			
		||||
			StorageService()->UserDB().GetUserByEmail(UserInfo_.userinfo.email, NewUserInfo);
 | 
			
		||||
			Poco::JSON::Object ModifiedObject;
 | 
			
		||||
			Sanitize(UserInfo_, NewUserInfo);
 | 
			
		||||
			NewUserInfo.to_json(ModifiedObject);
 | 
			
		||||
@@ -326,4 +347,4 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
		BadRequest(RESTAPI::Errors::RecordNotUpdated);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -9,23 +9,22 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_user_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_user_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_user_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							 RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							 bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
                                 std::vector<std::string>
 | 
			
		||||
                                         {Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_PUT,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_DELETE,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                                          Server,
 | 
			
		||||
                                          TransactionId,
 | 
			
		||||
                                          Internal) {}
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/user/{id}"}; };
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoPost() final;
 | 
			
		||||
		void DoDelete() final;
 | 
			
		||||
		void DoPut() final;
 | 
			
		||||
    private:
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -3,33 +3,38 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_users_handler.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_db_helpers.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	void RESTAPI_users_handler::DoGet() {
 | 
			
		||||
        bool IdOnly = (GetParameter("idOnly","false")=="true");
 | 
			
		||||
		bool IdOnly = (GetParameter("idOnly", "false") == "true");
 | 
			
		||||
		auto nameSearch = GetParameter("nameSearch");
 | 
			
		||||
		auto emailSearch = GetParameter("emailSearch");
 | 
			
		||||
 | 
			
		||||
		std::string baseQuery;
 | 
			
		||||
        if(!nameSearch.empty() || !emailSearch.empty()) {
 | 
			
		||||
            if(!nameSearch.empty())
 | 
			
		||||
                baseQuery = fmt::format(" Lower(name) like('%{}%') ", Poco::toLower(nameSearch) );
 | 
			
		||||
            if(!emailSearch.empty())
 | 
			
		||||
                baseQuery += baseQuery.empty() ? fmt::format(" Lower(email) like('%{}%') ", Poco::toLower(emailSearch))
 | 
			
		||||
                                               : fmt::format(" and Lower(email) like('%{}%') ", Poco::toLower(emailSearch));
 | 
			
		||||
		if (!nameSearch.empty() || !emailSearch.empty()) {
 | 
			
		||||
			if (!nameSearch.empty())
 | 
			
		||||
				baseQuery = fmt::format(" Lower(name) like('%{}%') ",
 | 
			
		||||
										ORM::Escape(Poco::toLower(nameSearch)));
 | 
			
		||||
			if (!emailSearch.empty())
 | 
			
		||||
				baseQuery += baseQuery.empty()
 | 
			
		||||
								 ? fmt::format(" Lower(email) like('%{}%') ",
 | 
			
		||||
											   ORM::Escape(Poco::toLower(emailSearch)))
 | 
			
		||||
								 : fmt::format(" and Lower(email) like('%{}%') ",
 | 
			
		||||
											   ORM::Escape(Poco::toLower(emailSearch)));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        if(QB_.Select.empty()) {
 | 
			
		||||
		if (QB_.Select.empty()) {
 | 
			
		||||
			SecurityObjects::UserInfoList Users;
 | 
			
		||||
            if(StorageService()->UserDB().GetUsers(QB_.Offset, QB_.Limit, Users.users, baseQuery)) {
 | 
			
		||||
			if (StorageService()->UserDB().GetUsers(QB_.Offset, QB_.Limit, Users.users,
 | 
			
		||||
													baseQuery)) {
 | 
			
		||||
				for (auto &i : Users.users) {
 | 
			
		||||
					Sanitize(UserInfo_, i);
 | 
			
		||||
				}
 | 
			
		||||
                if(IdOnly) {
 | 
			
		||||
				if (IdOnly) {
 | 
			
		||||
					Poco::JSON::Array Arr;
 | 
			
		||||
                    for(const auto &i:Users.users)
 | 
			
		||||
					for (const auto &i : Users.users)
 | 
			
		||||
						Arr.add(i.id);
 | 
			
		||||
					Poco::JSON::Object Answer;
 | 
			
		||||
					Answer.set("users", Arr);
 | 
			
		||||
@@ -41,9 +46,9 @@ namespace OpenWifi {
 | 
			
		||||
			return ReturnObject(Answer);
 | 
			
		||||
		} else {
 | 
			
		||||
			SecurityObjects::UserInfoList Users;
 | 
			
		||||
            for(auto &i:SelectedRecords()) {
 | 
			
		||||
			for (auto &i : SelectedRecords()) {
 | 
			
		||||
				SecurityObjects::UserInfo UInfo;
 | 
			
		||||
                if(StorageService()->UserDB().GetUserById(i,UInfo)) {
 | 
			
		||||
				if (StorageService()->UserDB().GetUserById(i, UInfo)) {
 | 
			
		||||
					Poco::JSON::Object Obj;
 | 
			
		||||
					Sanitize(UserInfo_, UInfo);
 | 
			
		||||
					Users.users.emplace_back(UInfo);
 | 
			
		||||
@@ -54,4 +59,4 @@ namespace OpenWifi {
 | 
			
		||||
			return ReturnObject(Answer);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -9,19 +9,17 @@
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_users_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
        RESTAPI_users_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
 | 
			
		||||
		RESTAPI_users_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
							  RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
							  bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
                                 std::vector<std::string>
 | 
			
		||||
                                 {Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
                                  Server,
 | 
			
		||||
                                  TransactionId,
 | 
			
		||||
                                  Internal) {}
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/users"}; };
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
        void DoPost() final {};
 | 
			
		||||
        void DoDelete() final {};
 | 
			
		||||
        void DoPut() final {};
 | 
			
		||||
		void DoPost() final{};
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
		void DoPut() final{};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}; // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||