Compare commits
	
		
			134 Commits
		
	
	
		
			v2.7.0-RC4
			...
			v2.11.0-RC
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | d966f53423 | ||
|   | 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 | ||
|   | 0417162858 | ||
|   | 75b2b30b67 | ||
|   | abc06d7953 | ||
|   | 7993e7d345 | ||
|   | be4549fabb | ||
|   | 92c141e511 | ||
|   | 296713e853 | ||
|   | d6dee68880 | ||
|   | aaffa145ad | ||
|   | c8e894bf79 | ||
|   | 766a608e1b | ||
|   | 333316d7a9 | ||
|   | 6527b45f2f | ||
|   | 76ef41aefe | ||
|   | 7e988c5780 | ||
|   | 2080027d7c | ||
|   | b8a14e95d8 | ||
|   | 8966888e6b | ||
|   | 0ad79b8076 | ||
|   | f650a6fde4 | ||
|   | a6b7057c9b | ||
|   | 6a1fa01235 | ||
|   | f554e73b91 | ||
|   | 2316dca6ce | ||
|   | 2395423832 | ||
|   | 43363e6854 | ||
|   | 2ab3d6a53d | ||
|   | 561fc84958 | ||
|   | afbe50b65d | ||
|   | 15b5551cd8 | ||
|   | 717ab7451f | ||
|   | 8afba9650b | ||
|   | 155d6ba319 | ||
|   | 66f4742ca5 | ||
|   | ad1bc551db | ||
|   | 9926b551f5 | ||
|   | 1dfd7969ea | ||
|   | a62e34fdf8 | ||
|   | 45deeaea88 | ||
|   | c5aadffe1d | ||
|   | d10883b60d | ||
|   | d38db8e05b | ||
|   | 8ea43f455c | ||
|   | f653083548 | ||
|   | 66c50b27bf | ||
|   | 351dd650fa | ||
|   | 8550675c04 | ||
|   | 76864c21d7 | ||
|   | 696ee32ef3 | ||
|   | 780d6654fb | ||
|   | b195763518 | ||
|   | 6543f44eab | ||
|   | 9b5aa5dd5d | ||
|   | 3062424816 | ||
|   | 41bd759d03 | ||
|   | a27cd109e8 | ||
|   | ec03bc6710 | ||
|   | f00de63289 | ||
|   | becd374124 | ||
|   | 89256bb900 | ||
|   | a1634770bc | ||
|   | 6db6e51ef3 | ||
|   | 1ada42bdcb | ||
|   | 6bbcca57ae | ||
|   | 447ab2a705 | ||
|   | ae251f9d35 | ||
|   | 729b1e6708 | ||
|   | 514bb3e622 | ||
|   | 087265b8b7 | ||
|   | ccd5498f19 | ||
|   | 1688f5a39d | ||
|   | 1b185515ce | ||
|   | 3c45f07cee | ||
|   | a493c9190e | ||
|   | fda8afd90c | ||
|   | a18cb37671 | ||
|   | 2c85a691bb | ||
|   | e8800782b4 | ||
|   | d0e818805a | ||
|   | 02ad85ca73 | ||
|   | 0ca578e9ec | ||
|   | d351522441 | ||
|   | 401419e060 | ||
|   | a8b0b46b1a | ||
|   | d4fe199b0d | ||
|   | 52bbf884f9 | ||
|   | e398d3cf4b | ||
|   | f53cc82df1 | ||
|   | 3f9edc80e0 | 
							
								
								
									
										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 | ||||
| ... | ||||
|  | ||||
							
								
								
									
										10
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -27,7 +27,7 @@ jobs: | ||||
|       DOCKER_REGISTRY_USERNAME: ucentral | ||||
|     steps: | ||||
|     - name: Checkout actions repo | ||||
|       uses: actions/checkout@v2 | ||||
|       uses: actions/checkout@v3 | ||||
|       with: | ||||
|         repository: Telecominfraproject/.github | ||||
|         path: github | ||||
| @@ -58,11 +58,11 @@ jobs: | ||||
|     - name: Get base branch name and set as output | ||||
|       id: get_base_branch | ||||
|       run: | | ||||
|         echo ::set-output name=branch::$(echo ${GITHUB_BASE_REF##*/}) | ||||
|         echo ::set-output name=owgw_branch::$(echo ${GITHUB_BASE_REF##*/} | sed 's/main/master/g') | ||||
|         echo "branch=$(echo ${GITHUB_BASE_REF##*/})" >> $GITHUB_OUTPUT | ||||
|         echo "owgw_branch=$(echo ${GITHUB_BASE_REF##*/} | sed 's/main/master/g')" >> $GITHUB_OUTPUT | ||||
|  | ||||
|     - name: Checkout actions repo | ||||
|       uses: actions/checkout@v2 | ||||
|       uses: actions/checkout@v3 | ||||
|       with: | ||||
|         repository: Telecominfraproject/.github | ||||
|         path: github | ||||
| @@ -87,7 +87,7 @@ jobs: | ||||
|       - docker | ||||
|     steps: | ||||
|     - name: Checkout actions repo | ||||
|       uses: actions/checkout@v2 | ||||
|       uses: actions/checkout@v3 | ||||
|       with: | ||||
|         repository: Telecominfraproject/.github | ||||
|         path: github | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/enforce-jira-issue-key.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -11,7 +11,7 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout actions repo | ||||
|         uses: actions/checkout@v2 | ||||
|         uses: actions/checkout@v3 | ||||
|         with: | ||||
|           repository: Telecominfraproject/.github | ||||
|           path: github | ||||
|   | ||||
							
								
								
									
										38
									
								
								.github/workflows/openapi-pages.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,38 @@ | ||||
| name: Update OpenAPI docs on GitHub Pages | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     paths: | ||||
|       - 'openapi/**' | ||||
|     branches: | ||||
|       - main | ||||
|   workflow_dispatch: | ||||
|  | ||||
| defaults: | ||||
|   run: | ||||
|     shell: bash | ||||
|  | ||||
| jobs: | ||||
|   docsgen: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|  | ||||
|       - name: Generate static HTML page with docs from OpenAPI definition | ||||
|         run: | | ||||
|           docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli:v6.2.1 generate -i https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentralsec/main/openpapi/owsec.yaml -g html2 --skip-validate-spec -o /local/ | ||||
|  | ||||
|       - name: Update OpenAPI docs | ||||
|         run: | | ||||
|           mkdir -p ~/.ssh | ||||
|           ssh-keyscan -H github.com >> ~/.ssh/known_hosts | ||||
|           echo https://tip-automation:${{ secrets.GIT_PUSH_PAT }}@github.com > ~/.git-credentials | ||||
|           git config --global credential.helper store | ||||
|           git config --global user.email "tip-automation@telecominfraproject.com" | ||||
|           git config --global user.name "TIP Automation User" | ||||
|           git pull | ||||
|           git checkout gh-pages || git checkout -b gh-pages | ||||
|           mv index.html docs/index.html | ||||
|           git add docs | ||||
|           git commit -m'Update OpenAPI docs for GitHub pages' | ||||
|           git push --set-upstream origin gh-pages | ||||
							
								
								
									
										2
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -17,7 +17,7 @@ jobs: | ||||
|       HELM_REPO_USERNAME: ucentral | ||||
|     steps: | ||||
|       - name: Checkout uCentral assembly chart repo | ||||
|         uses: actions/checkout@v2 | ||||
|         uses: actions/checkout@v3 | ||||
|         with: | ||||
|           path: wlan-cloud-ucentralsec | ||||
|  | ||||
|   | ||||
							
								
								
									
										21
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,21 +0,0 @@ | ||||
| # Default ignored files | ||||
| /shelf/ | ||||
| /workspace.xml | ||||
| # Datasource local storage ignored files | ||||
| /dataSources/ | ||||
| /dataSources.local.xml | ||||
| # Editor-based HTTP Client requests | ||||
| /httpRequests/ | ||||
| /certs/ | ||||
| /logs/ | ||||
| *.csr | ||||
| *.db | ||||
| /docker-compose/certs/ | ||||
| /docker-compose/*-data/data/ | ||||
| /docker-compose/*-data/uploads/ | ||||
| /docker-compose/.env | ||||
| /docker-compose/.env_* | ||||
| /cmake-build/ | ||||
| *.pem | ||||
| result.json | ||||
| token.json | ||||
							
								
								
									
										191
									
								
								BUILDING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,191 @@ | ||||
| # Building from source | ||||
|  | ||||
| In order to build the 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/AriliaWireless/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  | ||||
| sudo apt install libpq-dev libaprutil1-dev apache2-dev libboost-all-dev | ||||
| sudo apt install librdkafka-dev default-libmysqlclient-dev | ||||
| sudo apt install nlohmann-json-dev | ||||
|  | ||||
| cd ~ | ||||
| git clone https://github.com/AriliaWireless/poco --branch poco-tip-v1 | ||||
| cd poco | ||||
| mkdir cmake-build | ||||
| cd cmake-build | ||||
| cmake .. | ||||
| cmake --build . --config Release | ||||
| sudo cmake --build . --target install | ||||
|  | ||||
| cd ~ | ||||
| git clone https://github.com/AriliaWireless/cppkafka --branch tip-v1 | ||||
| cd cppkafka | ||||
| mkdir cmake-build | ||||
| cd cmake-build | ||||
| cmake .. | ||||
| cmake --build . --config Release | ||||
| sudo cmake --build . --target install | ||||
|  | ||||
| cd ~ | ||||
| git clone https://github.com/AriliaWireless/valijson --branch tip-v1 | ||||
| cd valijson | ||||
| mkdir cmake-build | ||||
| cd cmake-build | ||||
| cmake .. | ||||
| cmake --build . --config Release | ||||
| sudo cmake --build . --target install | ||||
|  | ||||
| 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 | ||||
| ``` | ||||
|  | ||||
| ## 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 | ||||
| sudo yum install 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/AriliaWireless/poco --branch poco-tip-v1 | ||||
| cd poco | ||||
| mkdir cmake-build | ||||
| cd cmake-build | ||||
| cmake .. | ||||
| cmake --build . --config Release | ||||
| sudo cmake --build . --target install | ||||
|  | ||||
| git clone https://github.com/AriliaWireless/cppkafka --branch tip-v1 | ||||
| cd cppkafka | ||||
| mkdir cmake-build | ||||
| cd cmake-build | ||||
| cmake .. | ||||
| cmake --build . --config Release | ||||
| sudo cmake --build . --target install | ||||
|  | ||||
| cd ~ | ||||
| git clone https://github.com/AriliaWireless/valijson --branch tip-v1 | ||||
| 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 | ||||
| ``` | ||||
|  | ||||
| ## 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/AriliaWireless/poco --branch poco-tip-v1 | ||||
| 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/AriliaWireless/cppkafka --branch tip-v1 | ||||
| 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/AriliaWireless/valijson --branch tip-v1 | ||||
| cd valijson | ||||
| mkdir cmake-build | ||||
| cd 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/stephb9959/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 | ||||
| ``` | ||||
| @@ -1,5 +1,5 @@ | ||||
| cmake_minimum_required(VERSION 3.13) | ||||
| project(owsec VERSION 2.7.0) | ||||
| project(owsec VERSION 2.11.0) | ||||
|  | ||||
| set(CMAKE_CXX_STANDARD 17) | ||||
|  | ||||
| @@ -32,12 +32,12 @@ endif() | ||||
|  | ||||
| find_package(Git QUIET) | ||||
| if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") | ||||
|     execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --tags | ||||
|     execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD | ||||
|             WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} | ||||
|             RESULT_VARIABLE GIT_RESULT | ||||
|             OUTPUT_VARIABLE GIT_HASH) | ||||
|     if(NOT GIT_RESULT EQUAL "0") | ||||
|         message(FATAL_ERROR "git describe --always --tags failed with ${GIT_RESULT}") | ||||
|         message(FATAL_ERROR "git rev-parse --short HEAD failed with ${GIT_RESULT}") | ||||
|     endif() | ||||
|     string(REGEX REPLACE "\n$" "" GIT_HASH "${GIT_HASH}") | ||||
| endif() | ||||
| @@ -75,19 +75,63 @@ add_executable( owsec | ||||
|         src/framework/CountryCodes.h | ||||
|         src/framework/KafkaTopics.h | ||||
|         src/framework/MicroService.h | ||||
|         src/framework/OpenWifiTypes.h | ||||
|         src/framework/orm.h | ||||
|         src/framework/StorageClass.h | ||||
|         src/framework/ow_constants.h | ||||
|         src/framework/MicroServiceErrorHandler.h | ||||
|         src/framework/WebSocketClientNotifications.h | ||||
|         src/framework/UI_WebSocketClientServer.cpp | ||||
|         src/framework/UI_WebSocketClientServer.h | ||||
|         src/framework/UI_WebSocketClientNotifications.cpp | ||||
|         src/framework/UI_WebSocketClientNotifications.h | ||||
|         src/framework/utils.h | ||||
|         src/framework/utils.cpp | ||||
|         src/framework/AppServiceRegistry.h | ||||
|         src/framework/SubSystemServer.cpp | ||||
|         src/framework/SubSystemServer.h | ||||
|         src/framework/RESTAPI_utils.h | ||||
|         src/framework/AuthClient.cpp | ||||
|         src/framework/AuthClient.h | ||||
|         src/framework/MicroServiceNames.h | ||||
|         src/framework/MicroServiceFuncs.h | ||||
|         src/framework/OpenAPIRequests.cpp | ||||
|         src/framework/OpenAPIRequests.h | ||||
|         src/framework/MicroServiceFuncs.cpp | ||||
|         src/framework/ALBserver.cpp | ||||
|         src/framework/ALBserver.h | ||||
|         src/framework/KafkaManager.cpp | ||||
|         src/framework/KafkaManager.h | ||||
|         src/framework/RESTAPI_RateLimiter.h | ||||
|         src/framework/WebSocketLogger.h | ||||
|         src/framework/RESTAPI_GenericServerAccounting.h | ||||
|         src/framework/RESTAPI_SystemConfiguration.h | ||||
|         src/framework/CIDR.h | ||||
|         src/framework/RESTAPI_Handler.cpp | ||||
|         src/framework/RESTAPI_Handler.h | ||||
|         src/framework/RESTAPI_ExtServer.h | ||||
|         src/framework/RESTAPI_ExtServer.cpp | ||||
|         src/framework/RESTAPI_IntServer.cpp | ||||
|         src/framework/RESTAPI_IntServer.h | ||||
|         src/framework/RESTAPI_SystemCommand.h | ||||
|         src/framework/RESTAPI_WebSocketServer.h | ||||
|         src/framework/EventBusManager.cpp | ||||
|         src/framework/EventBusManager.h | ||||
|         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 | ||||
|         src/RESTObjects/RESTAPI_CertObjects.cpp src/RESTObjects/RESTAPI_CertObjects.h | ||||
|         src/RESTObjects/RESTAPI_OWLSobjects.cpp src/RESTObjects/RESTAPI_OWLSobjects.h | ||||
|         src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h | ||||
|         src/RESTObjects/RESTAPI_AnalyticsObjects.cpp src/RESTObjects/RESTAPI_AnalyticsObjects.h | ||||
|         src/RESTObjects/RESTAPI_SubObjects.cpp src/RESTObjects/RESTAPI_SubObjects.h | ||||
|  | ||||
|         src/seclibs/qrcode/qrcodegen.hpp src/seclibs/qrcode/qrcodegen.cpp | ||||
|         src/seclibs/cpptotp/bytes.cpp src/seclibs/cpptotp/bytes.h | ||||
|         src/seclibs/cpptotp/otp.cpp src/seclibs/cpptotp/otp.h | ||||
|         src/seclibs/cpptotp/sha1.cpp src/seclibs/cpptotp/sha1.h | ||||
|         src/RESTObjects/RESTAPI_SecurityObjects.h src/RESTObjects/RESTAPI_SecurityObjects.cpp | ||||
|         src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h | ||||
|         src/RESTObjects/RESTAPI_GWobjects.h src/RESTObjects/RESTAPI_GWobjects.cpp | ||||
|         src/RESTObjects/RESTAPI_FMSObjects.h src/RESTObjects/RESTAPI_FMSObjects.cpp | ||||
|         src/RESTAPI/RESTAPI_oauth2_handler.h src/RESTAPI/RESTAPI_oauth2_handler.cpp | ||||
|         src/RESTAPI/RESTAPI_users_handler.cpp src/RESTAPI/RESTAPI_users_handler.h | ||||
|         src/RESTAPI/RESTAPI_user_handler.cpp src/RESTAPI/RESTAPI_user_handler.h | ||||
| @@ -119,14 +163,19 @@ add_executable( owsec | ||||
|         src/SMS_provider_twilio.cpp src/SMS_provider_twilio.h | ||||
|         src/ActionLinkManager.cpp src/ActionLinkManager.h | ||||
|         src/ACLProcessor.h | ||||
|         src/framework/OpenWifiTypes.h | ||||
|         src/storage/orm_users.cpp src/storage/orm_users.h | ||||
|         src/storage/orm_tokens.cpp src/storage/orm_tokens.h | ||||
|         src/storage/orm_preferences.cpp src/storage/orm_preferences.h | ||||
|         src/storage/orm_actionLinks.cpp src/storage/orm_actionLinks.h | ||||
|         src/storage/orm_avatar.cpp src/storage/orm_avatar.h | ||||
|         src/SpecialUserHelpers.h | ||||
|         src/RESTAPI/RESTAPI_db_helpers.h src/storage/orm_logins.cpp src/storage/orm_logins.h src/RESTAPI/RESTAPI_totp_handler.cpp src/RESTAPI/RESTAPI_totp_handler.h 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_db_helpers.h src/storage/orm_logins.cpp src/storage/orm_logins.h | ||||
|         src/RESTAPI/RESTAPI_totp_handler.cpp | ||||
|         src/RESTAPI/RESTAPI_totp_handler.h | ||||
|         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.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 | ||||
| @@ -140,4 +189,4 @@ if(NOT SMALL_BUILD) | ||||
|     if(UNIX AND NOT APPLE) | ||||
|         target_link_libraries(owsec PUBLIC PocoJSON) | ||||
|     endif() | ||||
| endif() | ||||
| endif() | ||||
|   | ||||
							
								
								
									
										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.  | ||||
							
								
								
									
										84
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						| @@ -1,17 +1,15 @@ | ||||
| ARG DEBIAN_VERSION=11.4-slim | ||||
| ARG POCO_VERSION=poco-tip-v1 | ||||
| ARG FMTLIB_VERSION=9.0.0 | ||||
| ARG DEBIAN_VERSION=11.5-slim | ||||
| ARG POCO_VERSION=poco-tip-v2 | ||||
| ARG CPPKAFKA_VERSION=tip-v1 | ||||
| ARG JSON_VALIDATOR_VERSION=2.1.0 | ||||
| ARG AWS_SDK_VERSION=1.9.315 | ||||
| ARG VALIJASON_VERSION=tip-v1 | ||||
|  | ||||
| FROM debian:$DEBIAN_VERSION AS build-base | ||||
|  | ||||
| RUN apt-get update && apt-get install --no-install-recommends -y \ | ||||
|     make cmake g++ git \ | ||||
|     make cmake g++ git curl zip unzip pkg-config \ | ||||
|     libpq-dev libmariadb-dev libmariadbclient-dev-compat \ | ||||
|     librdkafka-dev libboost-all-dev libssl-dev \ | ||||
|     zlib1g-dev nlohmann-json3-dev ca-certificates libcurl4-openssl-dev | ||||
|     zlib1g-dev ca-certificates libcurl4-openssl-dev libfmt-dev | ||||
|  | ||||
| FROM build-base AS poco-build | ||||
|  | ||||
| @@ -27,20 +25,6 @@ RUN cmake .. | ||||
| RUN cmake --build . --config Release -j8 | ||||
| RUN cmake --build . --target install | ||||
|  | ||||
| FROM build-base AS fmtlib-build | ||||
|  | ||||
| ARG FMTLIB_VERSION | ||||
|  | ||||
| ADD https://api.github.com/repos/fmtlib/fmt/git/refs/tags/${FMTLIB_VERSION} version.json | ||||
| RUN git clone https://github.com/fmtlib/fmt --branch ${FMTLIB_VERSION} /fmtlib | ||||
|  | ||||
| WORKDIR /fmtlib | ||||
| RUN mkdir cmake-build | ||||
| WORKDIR cmake-build | ||||
| RUN cmake .. | ||||
| RUN make | ||||
| RUN make install | ||||
|  | ||||
| FROM build-base AS cppkafka-build | ||||
|  | ||||
| ARG CPPKAFKA_VERSION | ||||
| @@ -55,63 +39,44 @@ RUN cmake .. | ||||
| RUN cmake --build . --config Release -j8 | ||||
| RUN cmake --build . --target install | ||||
|  | ||||
| FROM build-base AS json-schema-validator-build | ||||
| FROM build-base AS valijson-build | ||||
|  | ||||
| ARG JSON_VALIDATOR_VERSION | ||||
| ARG VALIJASON_VERSION | ||||
|  | ||||
| ADD https://api.github.com/repos/pboettch/json-schema-validator/git/refs/tags/${JSON_VALIDATOR_VERSION} version.json | ||||
| RUN git clone https://github.com/pboettch/json-schema-validator --branch ${JSON_VALIDATOR_VERSION} /json-schema-validator | ||||
| ADD https://api.github.com/repos/AriliaWireless/valijson/git/refs/tags/${VALIJASON_VERSION} version.json | ||||
| RUN git clone https://github.com/AriliaWireless/valijson --branch ${VALIJASON_VERSION} /valijson | ||||
|  | ||||
| WORKDIR /json-schema-validator | ||||
| WORKDIR /valijson | ||||
| RUN mkdir cmake-build | ||||
| WORKDIR cmake-build | ||||
| RUN cmake .. | ||||
| RUN make | ||||
| RUN make install | ||||
|  | ||||
| FROM build-base AS aws-sdk-cpp-build | ||||
|  | ||||
| ARG AWS_SDK_VERSION | ||||
|  | ||||
| ADD https://api.github.com/repos/aws/aws-sdk-cpp/git/refs/tags/${AWS_SDK_VERSION} version.json | ||||
| RUN git clone --recurse-submodules https://github.com/aws/aws-sdk-cpp --branch ${AWS_SDK_VERSION} /aws-sdk-cpp | ||||
|  | ||||
| WORKDIR /aws-sdk-cpp | ||||
| RUN mkdir cmake-build | ||||
| WORKDIR cmake-build | ||||
| RUN cmake .. -DBUILD_ONLY="sns;s3" \ | ||||
|              -DCMAKE_BUILD_TYPE=Release \ | ||||
|              -DUSE_OPENSSL=ON \ | ||||
|              -DCPP_STANDARD=17 \ | ||||
|              -DBUILD_SHARED_LIBS=ON \ | ||||
|              -DCMAKE_CXX_FLAGS="-Wno-error=stringop-overflow -Wno-error=uninitialized" \ | ||||
|              -DAUTORUN_UNIT_TESTS=OFF | ||||
| RUN cmake --build . --config Release -j8 | ||||
| RUN cmake --build . --target install | ||||
|  | ||||
| FROM build-base AS owsec-build | ||||
|  | ||||
| ADD CMakeLists.txt build /owsec/ | ||||
| ADD overlays /owsec/overlays | ||||
| ADD cmake /owsec/cmake | ||||
| ADD src /owsec/src | ||||
| ADD .git /owsec/.git | ||||
| ARG VCPKG_VERSION=2022.11.14 | ||||
| RUN git clone --depth 1 --branch ${VCPKG_VERSION} https://github.com/microsoft/vcpkg && \ | ||||
|     ./vcpkg/bootstrap-vcpkg.sh && \ | ||||
|     mkdir /vcpkg/custom-triplets && \ | ||||
|     cp /vcpkg/triplets/x64-linux.cmake /vcpkg/custom-triplets/x64-linux.cmake && \ | ||||
|     sed -i 's/set(VCPKG_LIBRARY.*/set(VCPKG_LIBRARY_LINKAGE dynamic)/g' /vcpkg/custom-triplets/x64-linux.cmake && \ | ||||
|     ./vcpkg/vcpkg install aws-sdk-cpp[sns]:x64-linux json-schema-validator:x64-linux --overlay-triplets=/vcpkg/custom-triplets --overlay-ports=/owsec/overlays | ||||
|  | ||||
| COPY --from=poco-build /usr/local/include /usr/local/include | ||||
| COPY --from=poco-build /usr/local/lib /usr/local/lib | ||||
| COPY --from=cppkafka-build /usr/local/include /usr/local/include | ||||
| COPY --from=cppkafka-build /usr/local/lib /usr/local/lib | ||||
| COPY --from=json-schema-validator-build /usr/local/include /usr/local/include | ||||
| COPY --from=json-schema-validator-build /usr/local/lib /usr/local/lib | ||||
| COPY --from=aws-sdk-cpp-build /usr/local/include /usr/local/include | ||||
| COPY --from=aws-sdk-cpp-build /usr/local/lib /usr/local/lib | ||||
|  | ||||
| COPY --from=fmtlib-build /usr/local/include /usr/local/include | ||||
| COPY --from=fmtlib-build /usr/local/lib /usr/local/lib | ||||
|  | ||||
| WORKDIR /owsec | ||||
| RUN mkdir cmake-build | ||||
| WORKDIR /owsec/cmake-build | ||||
| RUN cmake .. | ||||
| RUN cmake -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake .. | ||||
| RUN cmake --build . --config Release -j8 | ||||
|  | ||||
| FROM debian:$DEBIAN_VERSION | ||||
| @@ -128,7 +93,7 @@ RUN mkdir -p "$OWSEC_ROOT" "$OWSEC_CONFIG" && \ | ||||
|  | ||||
| RUN apt-get update && apt-get install --no-install-recommends -y \ | ||||
|     librdkafka++1 gosu gettext ca-certificates bash jq curl wget \ | ||||
|     libmariadb-dev-compat libpq5 unixodbc postgresql-client | ||||
|     libmariadb-dev-compat libpq5 postgresql-client libfmt7 | ||||
|  | ||||
| COPY readiness_check /readiness_check | ||||
| COPY test_scripts/curl/cli /cli | ||||
| @@ -142,11 +107,10 @@ RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentr | ||||
|     -O /usr/local/share/ca-certificates/restapi-ca-selfsigned.crt | ||||
|  | ||||
| COPY --from=owsec-build /owsec/cmake-build/owsec /openwifi/owsec | ||||
| 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=aws-sdk-cpp-build /aws-sdk-cpp/cmake-build/aws-cpp-sdk-core/libaws-cpp-sdk-core.so /usr/local/lib | ||||
| COPY --from=aws-sdk-cpp-build /aws-sdk-cpp/cmake-build/aws-cpp-sdk-s3/libaws-cpp-sdk-s3.so /usr/local/lib | ||||
| COPY --from=aws-sdk-cpp-build /aws-sdk-cpp/cmake-build/aws-cpp-sdk-sns/libaws-cpp-sdk-sns.so /usr/local/lib | ||||
| 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 | ||||
|  | ||||
|   | ||||
							
								
								
									
										306
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -1,66 +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. | ||||
|  | ||||
| #### 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 | ||||
| ``` | ||||
| @@ -75,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' | ||||
| @@ -115,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 | ||||
| @@ -132,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. | ||||
| 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) | ||||
|  | ||||
| ``` | ||||
| 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 | ||||
| ``` | ||||
| ## 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. | ||||
|  | ||||
| #### `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. | ||||
| ## 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. | ||||
|  | ||||
| #### `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. | ||||
| ## 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 | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ fullnameOverride: "" | ||||
| images: | ||||
|   owsec: | ||||
|     repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owsec | ||||
|     tag: main | ||||
|     tag: v2.11.0-RC1 | ||||
|     pullPolicy: Always | ||||
| #    regcred: | ||||
| #      registry: tip-tip-wlan-cloud-ucentral.jfrog.io | ||||
|   | ||||
							
								
								
									
										
											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 | 
| Before Width: | Height: | Size: 4.8 KiB 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 | 
| @@ -17,6 +17,7 @@ servers: | ||||
| security: | ||||
|   - bearerAuth: [] | ||||
|   - ApiKeyAuth: [] | ||||
|   - ApiToken: [] | ||||
|  | ||||
| components: | ||||
|   securitySchemes: | ||||
| @@ -28,6 +29,10 @@ components: | ||||
|       type: http | ||||
|       scheme: bearer | ||||
|       bearerFormat: JWT | ||||
|     ApiToken: | ||||
|       type: apiKey | ||||
|       in: header | ||||
|       name: X-API-TOKEN | ||||
|  | ||||
|   responses: | ||||
|     NotFound: | ||||
| @@ -66,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: | ||||
| @@ -164,18 +170,61 @@ components: | ||||
|         aclTemplate: | ||||
|           $ref: '#/components/schemas/AclTemplate' | ||||
|  | ||||
|     ApiKeyCreationRequest: | ||||
|     ApiKeyAccessRight: | ||||
|       type: object | ||||
|       properties: | ||||
|         service: | ||||
|           type: string | ||||
|         access: | ||||
|           type: string | ||||
|           enum: | ||||
|             - read | ||||
|             - modify | ||||
|             - create | ||||
|             - delete | ||||
|             - noaccess | ||||
|  | ||||
|     ApiKeyAccessRightList: | ||||
|       type: object | ||||
|       properties: | ||||
|         acls: | ||||
|           type: array | ||||
|           items: | ||||
|             $ref: '#/components/schemas/ApiKeyAccessRight' | ||||
|  | ||||
|     ApiKeyEntry: | ||||
|       type: object | ||||
|       properties: | ||||
|         id: | ||||
|           type: string | ||||
|           format: uuid | ||||
|         userUuid: | ||||
|           type: string | ||||
|           format: uuid | ||||
|         name: | ||||
|           type: string | ||||
|         description: | ||||
|           type: string | ||||
|         apiKey: | ||||
|           type: string | ||||
|         salt: | ||||
|           type: string | ||||
|         expiresOn: | ||||
|           type: integer | ||||
|           format: int64 | ||||
|         lastUse: | ||||
|           type: integer | ||||
|           format: int64 | ||||
|         rights: | ||||
|           $ref: '#/components/schemas/AclTemplate' | ||||
|           $ref: '#/components/schemas/ApiKeyAccessRightList' | ||||
|  | ||||
|     ApiKeyEntryList: | ||||
|       type: object | ||||
|       properties: | ||||
|         apiKeys: | ||||
|           type: array | ||||
|           items: | ||||
|             $ref: '#/components/schemas/ApiKeyEntry' | ||||
|  | ||||
|     ApiKeyCreationAnswer: | ||||
|       type: object | ||||
| @@ -194,7 +243,7 @@ components: | ||||
|         apiKey: | ||||
|           type: string | ||||
|         rights: | ||||
|           $ref: '#/components/schemas/AclTemplate' | ||||
|           $ref: '#/components/schemas/ApiKeyAccessRightList' | ||||
|  | ||||
|     AclTemplate: | ||||
|       type: object | ||||
| @@ -444,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 | ||||
| @@ -506,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: | ||||
| @@ -540,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: | ||||
| @@ -704,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 | ||||
| @@ -894,7 +991,7 @@ paths: | ||||
|   /systemEndpoints: | ||||
|     get: | ||||
|       tags: | ||||
|         - Authentication | ||||
|         - System Commands | ||||
|       summary: Retrieve the system layout. | ||||
|       operationId: getSystemInfo | ||||
|       responses: | ||||
| @@ -1034,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' | ||||
| @@ -1150,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' | ||||
| @@ -1348,7 +1457,7 @@ paths: | ||||
|   /email: | ||||
|     post: | ||||
|       tags: | ||||
|         - Email | ||||
|         - Messaging | ||||
|       summary: Send test email with the system. | ||||
|       operationId: Send a test email | ||||
|       requestBody: | ||||
| @@ -1379,7 +1488,7 @@ paths: | ||||
|   /sms: | ||||
|     post: | ||||
|       tags: | ||||
|         - Email | ||||
|         - Messaging | ||||
|       summary: Send test email with the system. | ||||
|       operationId: Send a test SMS | ||||
|       parameters: | ||||
| @@ -1634,7 +1743,103 @@ paths: | ||||
|         404: | ||||
|           $ref: '#/components/responses/NotFound' | ||||
|  | ||||
|  | ||||
|   /apiKey/{uuid}: | ||||
|     get: | ||||
|       tags: | ||||
|         - API Tokens | ||||
|       summary: Retrieve all the APIKeys for a given user UUID | ||||
|       operationId: getApiKeyList | ||||
|       parameters: | ||||
|         - in: path | ||||
|           name: uuid | ||||
|           schema: | ||||
|             type: string | ||||
|             format: uuid | ||||
|           required: true | ||||
|       responses: | ||||
|         200: | ||||
|           $ref: '#/components/schemas/ApiKeyEntryList' | ||||
|         403: | ||||
|           $ref: '#/components/responses/Unauthorized' | ||||
|         404: | ||||
|           $ref: '#/components/responses/NotFound' | ||||
|     delete: | ||||
|       tags: | ||||
|         - API Tokens | ||||
|       summary: Retrieve all the APIKeys for a given user UUID | ||||
|       operationId: deleteApiKey | ||||
|       parameters: | ||||
|         - in: path | ||||
|           name: uuid | ||||
|           schema: | ||||
|             type: string | ||||
|             format: uuid | ||||
|           required: true | ||||
|         - in: query | ||||
|           name: keyUuid | ||||
|           schema: | ||||
|             type: string | ||||
|           required: true | ||||
|       responses: | ||||
|         200: | ||||
|           $ref: '#/components/responses/Success' | ||||
|         403: | ||||
|           $ref: '#/components/responses/Unauthorized' | ||||
|         404: | ||||
|           $ref: '#/components/responses/NotFound' | ||||
|     post: | ||||
|       tags: | ||||
|         - API Tokens | ||||
|       summary: Retrieve all the APIKeys for a given user UUID | ||||
|       operationId: createApiKey | ||||
|       parameters: | ||||
|         - in: path | ||||
|           name: uuid | ||||
|           schema: | ||||
|             type: string | ||||
|             format: uuid | ||||
|           required: true | ||||
|       requestBody: | ||||
|         content: | ||||
|           application/json: | ||||
|             schema: | ||||
|               $ref: '#/components/schemas/ApiKeyEntry' | ||||
|       responses: | ||||
|         200: | ||||
|           $ref: '#/components/schemas/ApiKeyEntry' | ||||
|         403: | ||||
|           $ref: '#/components/responses/Unauthorized' | ||||
|         404: | ||||
|           $ref: '#/components/responses/NotFound' | ||||
|     put: | ||||
|       tags: | ||||
|         - API Tokens | ||||
|       summary: Retrieve all the APIKeys for a given user UUID | ||||
|       operationId: modifyApiKey | ||||
|       parameters: | ||||
|         - in: path | ||||
|           name: uuid | ||||
|           schema: | ||||
|             type: string | ||||
|             format: uuid | ||||
|           required: true | ||||
|         - in: query | ||||
|           name: name | ||||
|           schema: | ||||
|             type: string | ||||
|           required: true | ||||
|       requestBody: | ||||
|         content: | ||||
|           application/json: | ||||
|             schema: | ||||
|               $ref: '#/components/schemas/ApiKeyEntry' | ||||
|       responses: | ||||
|         200: | ||||
|           $ref: '#/components/schemas/ApiKeyEntry' | ||||
|         403: | ||||
|           $ref: '#/components/responses/Unauthorized' | ||||
|         404: | ||||
|           $ref: '#/components/responses/NotFound' | ||||
|  | ||||
|   ######################################################################################### | ||||
|   ## | ||||
| @@ -1677,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: | ||||
| @@ -1732,6 +1924,26 @@ paths: | ||||
|         404: | ||||
|           $ref: '#/components/responses/NotFound' | ||||
|  | ||||
|   /validateApiKey: | ||||
|     get: | ||||
|       tags: | ||||
|         - Security | ||||
|       summary: Allows an application to validate an API Key. | ||||
|       operationId: validateApiKey | ||||
|       parameters: | ||||
|         - in: query | ||||
|           name: token | ||||
|           schema: | ||||
|             type: string | ||||
|           required: true | ||||
|       responses: | ||||
|         200: | ||||
|           $ref: '#/components/schemas/TokenValidationResult' | ||||
|         403: | ||||
|           $ref: '#/components/responses/Unauthorized' | ||||
|         404: | ||||
|           $ref: '#/components/responses/NotFound' | ||||
|  | ||||
|   /system: | ||||
|     post: | ||||
|       tags: | ||||
| @@ -1776,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' | ||||
|  | ||||
| ######################################################################################### | ||||
| ## | ||||
|   | ||||
							
								
								
									
										1
									
								
								overlays/curl/portfile.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| set(VCPKG_POLICY_EMPTY_PACKAGE enabled) | ||||
							
								
								
									
										4
									
								
								overlays/curl/vcpkg.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|     "name": "curl", | ||||
|     "version-string": "7.74.0-1.3+deb11u3" | ||||
| } | ||||
							
								
								
									
										1
									
								
								overlays/openssl/portfile.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| set(VCPKG_POLICY_EMPTY_PACKAGE enabled) | ||||
							
								
								
									
										4
									
								
								overlays/openssl/vcpkg.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|     "name": "openssl", | ||||
|     "version-string": "1.1.1n-0+deb11u3" | ||||
| } | ||||
							
								
								
									
										1
									
								
								overlays/zlib/portfile.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| set(VCPKG_POLICY_EMPTY_PACKAGE enabled) | ||||
							
								
								
									
										4
									
								
								overlays/zlib/vcpkg.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|     "name": "zlib", | ||||
|     "version-string": "1:1.2.11.dfsg-2+deb11u2" | ||||
| } | ||||
| @@ -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 | ||||
|   | ||||
| @@ -9,82 +9,124 @@ | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     class ACLProcessor { | ||||
|     public: | ||||
|         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 | ||||
| 	class ACLProcessor { | ||||
| 	  public: | ||||
| 		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 | ||||
|  | ||||
|  */ | ||||
|         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) { | ||||
|  | ||||
|             // rule 0 | ||||
|             if(User.id == Target.id && User.userRole == SecurityObjects::SUBSCRIBER && Op == DELETE) | ||||
|                 return true; | ||||
| 			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) { | ||||
| 					return User.userRole != SecurityObjects::ROOT; | ||||
| 				} | ||||
| 				//  Root can delete anyone | ||||
| 				switch (User.userRole) { | ||||
| 				case SecurityObjects::ROOT: | ||||
| 					return true; | ||||
| 				case SecurityObjects::ADMIN: | ||||
| 					return Target.userRole != SecurityObjects::ROOT && | ||||
| 						   Target.userRole != SecurityObjects::PARTNER; | ||||
| 				case SecurityObjects::SUBSCRIBER: | ||||
| 					return User.id == Target.id; | ||||
| 				case SecurityObjects::CSR: | ||||
| 					return false; | ||||
| 				case SecurityObjects::SYSTEM: | ||||
| 					return Target.userRole != SecurityObjects::ROOT && | ||||
| 						   Target.userRole != SecurityObjects::PARTNER; | ||||
| 				case SecurityObjects::INSTALLER: | ||||
| 					return User.id == Target.id; | ||||
| 				case SecurityObjects::NOC: | ||||
| 					return Target.userRole == SecurityObjects::NOC; | ||||
| 				case SecurityObjects::ACCOUNTING: | ||||
| 					return Target.userRole == SecurityObjects::ACCOUNTING; | ||||
| 				case SecurityObjects::PARTNER: | ||||
| 					return Target.userRole != SecurityObjects::ROOT; | ||||
| 				default: | ||||
| 					return false; | ||||
| 				} | ||||
| 			} break; | ||||
|  | ||||
|             //  rule 1 | ||||
|             if(User.id == Target.id && Op==DELETE) | ||||
|                 return false; | ||||
| 			case READ: { | ||||
| 				return User.userRole == SecurityObjects::ROOT || | ||||
| 					   User.userRole == SecurityObjects::ADMIN || | ||||
| 					   User.userRole == SecurityObjects::PARTNER; | ||||
| 			} break; | ||||
|  | ||||
|             //  rule 2 | ||||
|             if(User.userRole==SecurityObjects::ROOT) | ||||
|                 return true; | ||||
| 			case CREATE: { | ||||
| 				switch (User.userRole) { | ||||
| 				case SecurityObjects::ROOT: | ||||
| 					return true; | ||||
| 				case SecurityObjects::ADMIN: | ||||
| 					return Target.userRole != SecurityObjects::ROOT && | ||||
| 						   Target.userRole != SecurityObjects::PARTNER; | ||||
| 				case SecurityObjects::SUBSCRIBER: | ||||
| 					return false; | ||||
| 				case SecurityObjects::CSR: | ||||
| 					return Target.userRole == SecurityObjects::CSR; | ||||
| 				case SecurityObjects::SYSTEM: | ||||
| 					return Target.userRole != SecurityObjects::ROOT && | ||||
| 						   Target.userRole != SecurityObjects::PARTNER; | ||||
| 				case SecurityObjects::INSTALLER: | ||||
| 					return Target.userRole == SecurityObjects::INSTALLER; | ||||
| 				case SecurityObjects::NOC: | ||||
| 					return Target.userRole == SecurityObjects::NOC; | ||||
| 				case SecurityObjects::ACCOUNTING: | ||||
| 					return Target.userRole == SecurityObjects::ACCOUNTING; | ||||
| 				case SecurityObjects::PARTNER: | ||||
| 					return Target.userRole != SecurityObjects::ROOT; | ||||
| 				default: | ||||
| 					return false; | ||||
| 				} | ||||
| 			} break; | ||||
|  | ||||
|             //  rule 3 | ||||
|             if(User.id == Target.id) | ||||
|                 return true; | ||||
| 			case MODIFY: { | ||||
| 				switch (User.userRole) { | ||||
| 				case SecurityObjects::ROOT: | ||||
| 					return true; | ||||
| 				case SecurityObjects::ADMIN: | ||||
| 					return Target.userRole != SecurityObjects::ROOT && | ||||
| 						   Target.userRole != SecurityObjects::PARTNER; | ||||
| 				case SecurityObjects::SUBSCRIBER: | ||||
| 					return User.id == Target.id; | ||||
| 				case SecurityObjects::CSR: | ||||
| 					return Target.userRole == SecurityObjects::CSR; | ||||
| 				case SecurityObjects::SYSTEM: | ||||
| 					return Target.userRole != SecurityObjects::ROOT && | ||||
| 						   Target.userRole != SecurityObjects::PARTNER; | ||||
| 				case SecurityObjects::INSTALLER: | ||||
| 					return Target.userRole == SecurityObjects::INSTALLER; | ||||
| 				case SecurityObjects::NOC: | ||||
| 					return Target.userRole == SecurityObjects::NOC; | ||||
| 				case SecurityObjects::ACCOUNTING: | ||||
| 					return Target.userRole == SecurityObjects::ACCOUNTING; | ||||
| 				case SecurityObjects::PARTNER: | ||||
| 					return Target.userRole != SecurityObjects::ROOT; | ||||
| 				default: | ||||
| 					return false; | ||||
| 				} | ||||
| 			} break; | ||||
| 			default: | ||||
| 				return false; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|             //  rule 4 | ||||
|             if(Target.userRole==SecurityObjects::ROOT && Op!=READ) | ||||
|                 return false; | ||||
| 	  private: | ||||
| 	}; | ||||
|  | ||||
|             if(Op==CREATE) { | ||||
|                 if(User.userRole==SecurityObjects::ROOT) | ||||
|                     return true; | ||||
|                 if(User.userRole==SecurityObjects::PARTNER && (Target.userRole==SecurityObjects::ADMIN || | ||||
|                     Target.userRole==SecurityObjects::SUBSCRIBER || | ||||
|                     Target.userRole==SecurityObjects::CSR || | ||||
|                     Target.userRole==SecurityObjects::INSTALLER || | ||||
|                     Target.userRole==SecurityObjects::NOC || | ||||
|                     Target.userRole==SecurityObjects::ACCOUNTING)) | ||||
|                     return true; | ||||
|                 if(User.userRole==SecurityObjects::ADMIN && | ||||
|                     (Target.userRole==SecurityObjects::ADMIN || | ||||
|                     Target.userRole==SecurityObjects::SUBSCRIBER || | ||||
|                     Target.userRole==SecurityObjects::CSR || | ||||
|                     Target.userRole==SecurityObjects::INSTALLER || | ||||
|                     Target.userRole==SecurityObjects::NOC || | ||||
|                     Target.userRole==SecurityObjects::ACCOUNTING)) | ||||
|                     return true; | ||||
|                 if(User.userRole==SecurityObjects::ACCOUNTING && | ||||
|                     (Target.userRole==SecurityObjects::SUBSCRIBER || | ||||
|                     Target.userRole==SecurityObjects::INSTALLER || | ||||
|                     Target.userRole==SecurityObjects::CSR)) | ||||
|                     return true; | ||||
|                 return false; | ||||
|             } | ||||
| } // namespace OpenWifi | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|     private: | ||||
|  | ||||
|     }; | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| #endif //OWSEC_ACLPROCESSOR_H | ||||
| #endif // OWSEC_ACLPROCESSOR_H | ||||
|   | ||||
| @@ -3,124 +3,143 @@ | ||||
| // | ||||
|  | ||||
| #include "ActionLinkManager.h" | ||||
| #include "StorageService.h" | ||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||
| #include "MessagingTemplates.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_) | ||||
|             Thr_.start(*this); | ||||
|         return 0; | ||||
|     } | ||||
| 	int ActionLinkManager::Start() { | ||||
| 		poco_information(Logger(), "Starting..."); | ||||
| 		if (!Running_) | ||||
| 			Thr_.start(*this); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
|     void ActionLinkManager::Stop() { | ||||
|         poco_information(Logger(),"Stopping..."); | ||||
|         if(Running_) { | ||||
|             Running_ = false; | ||||
|             Thr_.wakeUp(); | ||||
|             Thr_.join(); | ||||
|         } | ||||
|         poco_information(Logger(),"Stopped..."); | ||||
|     } | ||||
| 	void ActionLinkManager::Stop() { | ||||
| 		poco_information(Logger(), "Stopping..."); | ||||
| 		if (Running_) { | ||||
| 			Running_ = false; | ||||
| 			Thr_.wakeUp(); | ||||
| 			Thr_.join(); | ||||
| 		} | ||||
| 		poco_information(Logger(), "Stopped..."); | ||||
| 	} | ||||
|  | ||||
|     void ActionLinkManager::run() { | ||||
|         Running_ = true ; | ||||
|         Utils::SetThreadName("action-mgr"); | ||||
| 	void ActionLinkManager::run() { | ||||
| 		Running_ = true; | ||||
| 		Utils::SetThreadName("action-mgr"); | ||||
|  | ||||
|         while(Running_) { | ||||
|             Poco::Thread::trySleep(2000); | ||||
|             if(!Running_) | ||||
|                 break; | ||||
|             std::vector<SecurityObjects::ActionLink>    Links; | ||||
|             { | ||||
|                 std::lock_guard G(Mutex_); | ||||
|                 StorageService()->ActionLinksDB().GetActions(Links); | ||||
|             } | ||||
| 		while (Running_) { | ||||
| 			Poco::Thread::trySleep(2000); | ||||
| 			if (!Running_) | ||||
| 				break; | ||||
| 			std::vector<SecurityObjects::ActionLink> Links; | ||||
| 			{ | ||||
| 				std::lock_guard G(Mutex_); | ||||
| 				StorageService()->ActionLinksDB().GetActions(Links); | ||||
| 			} | ||||
|  | ||||
|             if(Links.empty()) | ||||
|                 continue; | ||||
| 			if (Links.empty()) | ||||
| 				continue; | ||||
|  | ||||
|             for(auto &i:Links) { | ||||
|                 if(!Running_) | ||||
|                     break; | ||||
| 			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)) { | ||||
|                     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)) { | ||||
|                     StorageService()->ActionLinksDB().CancelAction(i.id); | ||||
|                     continue; | ||||
|                 } else if((i.action==OpenWifi::SecurityObjects::LinkActions::EMAIL_INVITATION) && | ||||
|                         (OpenWifi::Now()-i.created)>(24*60*60)) { | ||||
|                     StorageService()->ActionLinksDB().CancelAction(i.id); | ||||
|                     continue; | ||||
|                 } | ||||
| 				SecurityObjects::UserInfo 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)) { | ||||
| 					StorageService()->ActionLinksDB().CancelAction(i.id); | ||||
| 					continue; | ||||
| 				} 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) { | ||||
|                     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)); | ||||
|                             } | ||||
|                             StorageService()->ActionLinksDB().SentAction(i.id); | ||||
|                         } | ||||
|                         break; | ||||
| 				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)); | ||||
| 					} | ||||
| 					StorageService()->ActionLinksDB().SentAction(i.id); | ||||
| 				} 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)); | ||||
|                             } | ||||
|                             StorageService()->ActionLinksDB().SentAction(i.id); | ||||
|                         } | ||||
|                         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)); | ||||
| 					} | ||||
| 					StorageService()->ActionLinksDB().SentAction(i.id); | ||||
| 				} 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)); | ||||
|                             } | ||||
|                             StorageService()->ActionLinksDB().SentAction(i.id); | ||||
|                         } | ||||
|                         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)); | ||||
| 					} | ||||
| 					StorageService()->ActionLinksDB().SentAction(i.id); | ||||
| 				} 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)); | ||||
|                             } | ||||
|                             StorageService()->ActionLinksDB().SentAction(i.id); | ||||
|                         } | ||||
|                         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)); | ||||
| 					} | ||||
| 					StorageService()->ActionLinksDB().SentAction(i.id); | ||||
| 				} 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)); | ||||
|                             } | ||||
|                             StorageService()->ActionLinksDB().SentAction(i.id); | ||||
|                         } | ||||
|                         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)); | ||||
| 					} | ||||
| 					StorageService()->ActionLinksDB().SentAction(i.id); | ||||
| 				} break; | ||||
|  | ||||
|                     case OpenWifi::SecurityObjects::LinkActions::SUB_SIGNUP: { | ||||
|                         auto Signup = Poco::StringTokenizer(UInfo.signingUp,":"); | ||||
|                         if(AuthService::SendEmailToSubUser(i.id, UInfo.email, MessagingTemplates::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; | ||||
| 				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)); | ||||
| 					} | ||||
| 					StorageService()->ActionLinksDB().SentAction(i.id); | ||||
| 				} break; | ||||
|  | ||||
|                     default: { | ||||
|                         StorageService()->ActionLinksDB().SentAction(i.id); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 				default: { | ||||
| 					StorageService()->ActionLinksDB().SentAction(i.id); | ||||
| 				} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
| } // namespace OpenWifi | ||||
| @@ -2,35 +2,29 @@ | ||||
| // Created by stephane bourque on 2021-11-08. | ||||
| // | ||||
|  | ||||
| #ifndef OWSEC_ACTIONLINKMANAGER_H | ||||
| #define OWSEC_ACTIONLINKMANAGER_H | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/SubSystemServer.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     class ActionLinkManager : public SubSystemServer, Poco::Runnable { | ||||
|     public: | ||||
| 	class ActionLinkManager : public SubSystemServer, Poco::Runnable { | ||||
| 	  public: | ||||
| 		static ActionLinkManager *instance() { | ||||
| 			static auto instance_ = new ActionLinkManager; | ||||
| 			return instance_; | ||||
| 		} | ||||
|  | ||||
|         static ActionLinkManager * instance() { | ||||
|             static auto instance_ = new ActionLinkManager; | ||||
|             return instance_; | ||||
|         } | ||||
| 		int Start() final; | ||||
| 		void Stop() final; | ||||
| 		void run() final; | ||||
|  | ||||
|         int Start() final; | ||||
|         void Stop() final; | ||||
|         void run() final; | ||||
| 	  private: | ||||
| 		Poco::Thread Thr_; | ||||
| 		std::atomic_bool Running_ = false; | ||||
|  | ||||
|     private: | ||||
|         Poco::Thread        Thr_; | ||||
|         std::atomic_bool    Running_ = false; | ||||
|  | ||||
|         ActionLinkManager() noexcept: | ||||
|             SubSystemServer("ActionLinkManager", "ACTION-SVR", "action.server") | ||||
|                 { | ||||
|                 } | ||||
|     }; | ||||
|     inline ActionLinkManager * ActionLinkManager() { return ActionLinkManager::instance(); } | ||||
| } | ||||
|  | ||||
| #endif //OWSEC_ACTIONLINKMANAGER_H | ||||
| 		ActionLinkManager() noexcept | ||||
| 			: SubSystemServer("ActionLinkManager", "ACTION-SVR", "action.server") {} | ||||
| 	}; | ||||
| 	inline ActionLinkManager *ActionLinkManager() { return ActionLinkManager::instance(); } | ||||
| } // namespace OpenWifi | ||||
|   | ||||
							
								
								
									
										1441
									
								
								src/AuthService.cpp
									
									
									
									
									
								
							
							
						
						| @@ -6,163 +6,229 @@ | ||||
| //	Arilia Wireless Inc. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRAL_UAUTHSERVICE_H | ||||
| #define UCENTRAL_UAUTHSERVICE_H | ||||
| #pragma once | ||||
|  | ||||
| #include <regex> | ||||
|  | ||||
| #include "framework/MicroService.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"}; | ||||
| 	static const std::string AUTHENTICATION_SYSTEM{"SYSTEM"}; | ||||
|  | ||||
|     class AuthService : public SubSystemServer { | ||||
|     public: | ||||
| 	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); | ||||
|  | ||||
|         static ACCESS_TYPE IntToAccessType(int C); | ||||
|         static int AccessTypeToInt(ACCESS_TYPE T); | ||||
| 		static auto instance() { | ||||
| 			static auto instance_ = new AuthService; | ||||
| 			return instance_; | ||||
| 		} | ||||
|  | ||||
|         static auto instance() { | ||||
|             static auto instance_ = new AuthService; | ||||
|             return instance_; | ||||
|         } | ||||
| 		int Start() override; | ||||
| 		void Stop() override; | ||||
|  | ||||
|         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]] 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 ); | ||||
|         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); | ||||
| 		[[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 RemoveTokenSystemWide(const std::string &token); | ||||
| 		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); | ||||
|  | ||||
|         bool ValidatePassword(const std::string &pwd); | ||||
|         bool ValidateSubPassword(const std::string &pwd); | ||||
| 		void RemoveTokenSystemWide(const std::string &token); | ||||
|  | ||||
|         [[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); | ||||
| 		bool ValidatePassword(const std::string &pwd); | ||||
| 		bool ValidateSubPassword(const std::string &pwd); | ||||
|  | ||||
|         [[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 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 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 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 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 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]] static bool VerifyEmail(SecurityObjects::UserInfo &UInfo); | ||||
|         [[nodiscard]] static bool VerifySubEmail(SecurityObjects::UserInfo &UInfo); | ||||
| 		[[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 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 RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo); | ||||
| 		[[nodiscard]] static bool VerifyEmail(SecurityObjects::UserInfo &UInfo); | ||||
| 		[[nodiscard]] static bool VerifySubEmail(SecurityObjects::UserInfo &UInfo); | ||||
|  | ||||
|         [[nodiscard]] bool SendEmailChallengeCode(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &code); | ||||
| 		[[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); | ||||
|  | ||||
|         bool DeleteUserFromCache(const std::string &UserName); | ||||
|         bool DeleteSubUserFromCache(const std::string &UserName); | ||||
|         void RevokeToken(std::string & Token); | ||||
|         void RevokeSubToken(std::string & Token); | ||||
| 		[[nodiscard]] bool SendEmailChallengeCode(const SecurityObjects::UserInfoAndPolicy &UInfo, | ||||
| 												  const std::string &code); | ||||
|  | ||||
|         [[nodiscard]] static inline const std::string GetLogoAssetURI() { | ||||
|             return MicroService::instance().PublicEndPoint() + "/wwwassets/the_logo.png"; | ||||
|         } | ||||
| 		bool DeleteUserFromCache(const std::string &UserName); | ||||
| 		bool DeleteSubUserFromCache(const std::string &UserName); | ||||
| 		void RevokeToken(std::string &Token); | ||||
| 		void RevokeSubToken(std::string &Token); | ||||
|  | ||||
|         [[nodiscard]] static inline const std::string GetLogoAssetFileName() { | ||||
|             return MicroService::instance().WWWAssetsDir() + "/the_logo.png"; | ||||
|         } | ||||
| 		[[nodiscard]] static inline const std::string GetLogoAssetURI() { | ||||
| 			return MicroServicePublicEndPoint() + "/wwwassets/logo.png"; | ||||
| 		} | ||||
|  | ||||
|         inline const std::string & GetPasswordPolicy() const { return PasswordPolicy_; } | ||||
|         inline const std::string & GetAccessPolicy() const { return AccessPolicy_; } | ||||
| 		[[nodiscard]] static inline const std::string GetLogoAssetFileName() { | ||||
| 			return MicroServiceWWWAssetsDir() + "/logo.png"; | ||||
| 		} | ||||
|  | ||||
|         inline const std::string & GetSubPasswordPolicy() const { return SubPasswordPolicy_; } | ||||
|         inline const std::string & GetSubAccessPolicy() const { return SubAccessPolicy_; } | ||||
| 		[[nodiscard]] static inline const std::string GetSubLogoAssetURI() { | ||||
| 			return MicroServicePublicEndPoint() + "/wwwassets/sub_logo.png"; | ||||
| 		} | ||||
|  | ||||
|         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]] static inline const std::string GetSubLogoAssetFileName() { | ||||
| 			return MicroServiceWWWAssetsDir() + "/sub_logo.png"; | ||||
| 		} | ||||
|  | ||||
|     private: | ||||
| 		Poco::SHA2Engine	SHA2_; | ||||
| 		inline const std::string &GetPasswordPolicy() const { return PasswordPolicy_; } | ||||
| 		inline const std::string &GetAccessPolicy() const { return AccessPolicy_; } | ||||
|  | ||||
| 		std::string         AccessPolicy_; | ||||
| 		std::string         PasswordPolicy_; | ||||
| 		std::string         SubAccessPolicy_; | ||||
| 		std::string         SubPasswordPolicy_; | ||||
| 		std::string         PasswordValidationStr_; | ||||
|         std::string         SubPasswordValidationStr_; | ||||
|         std::regex          PasswordValidation_; | ||||
|         std::regex          SubPasswordValidation_; | ||||
| 		inline const std::string &GetSubPasswordPolicy() const { return SubPasswordPolicy_; } | ||||
| 		inline const std::string &GetSubAccessPolicy() const { return SubAccessPolicy_; } | ||||
|  | ||||
|         uint64_t            TokenAging_ = 15 * 24 * 60 * 60; | ||||
|         uint64_t            HowManyOldPassword_=5; | ||||
|         uint64_t            RefreshTokenLifeSpan_ = 90 * 24 * 60 * 60 ; | ||||
| 		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); | ||||
|  | ||||
|         class SHA256Engine : public Poco::Crypto::DigestEngine | ||||
|                 { | ||||
|                 public: | ||||
|                     enum | ||||
|                     { | ||||
|                         BLOCK_SIZE = 64, | ||||
|                         DIGEST_SIZE = 32 | ||||
|                     }; | ||||
| 		[[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_; }; | ||||
|  | ||||
|                     SHA256Engine() | ||||
|                     : DigestEngine("SHA256") | ||||
|                     { | ||||
|                     } | ||||
| 	  private: | ||||
| 		Poco::SHA2Engine SHA2_; | ||||
|  | ||||
|                 }; | ||||
| 		std::string AccessPolicy_; | ||||
| 		std::string PasswordPolicy_; | ||||
| 		std::string SubAccessPolicy_; | ||||
| 		std::string SubPasswordPolicy_; | ||||
| 		std::string PasswordValidationStr_; | ||||
| 		std::string SubPasswordValidationStr_; | ||||
| 		std::regex PasswordValidation_; | ||||
| 		std::regex SubPasswordValidation_; | ||||
|  | ||||
|         Poco::HMACEngine<SHA256Engine> HMAC_{"tipopenwifi"}; | ||||
| 		uint64_t TokenAging_ = 15 * 24 * 60 * 60; | ||||
| 		uint64_t HowManyOldPassword_ = 5; | ||||
| 		uint64_t RefreshTokenLifeSpan_ = 90 * 24 * 60 * 60; | ||||
|  | ||||
|         AuthService() noexcept: | ||||
|             SubSystemServer("Authentication", "AUTH-SVR", "authentication") | ||||
|         { | ||||
|         } | ||||
|     }; | ||||
| 		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_; | ||||
|  | ||||
|     inline auto AuthService() { return AuthService::instance(); } | ||||
| 		class SHA256Engine : public Poco::Crypto::DigestEngine { | ||||
| 		  public: | ||||
| 			enum { BLOCK_SIZE = 64, DIGEST_SIZE = 32 }; | ||||
|  | ||||
|     [[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 ); | ||||
|     } | ||||
| 			SHA256Engine() : DigestEngine("SHA256") {} | ||||
| 		}; | ||||
|  | ||||
| } // end of namespace | ||||
| 		Poco::HMACEngine<SHA256Engine> HMAC_{"tipopenwifi"}; | ||||
|  | ||||
| #endif //UCENTRAL_UAUTHSERVICE_H | ||||
| 		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); | ||||
| 		else | ||||
| 			return AuthService()->IsAuthorized(Request, SessionToken, UInfo, TID, Expired); | ||||
| 	} | ||||
|  | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -10,73 +10,70 @@ | ||||
| //	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_ = 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() | ||||
|                                    }); | ||||
|         } | ||||
|         return instance_; | ||||
|     } | ||||
| 	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(), SecretStore()}); | ||||
| 		} | ||||
| 		return instance_; | ||||
| 	} | ||||
|  | ||||
|     void Daemon::PostInitialization([[maybe_unused]] Poco::Util::Application &self) { | ||||
|         AssetDir_ = MicroService::instance().ConfigPath("openwifi.restapi.wwwassets"); | ||||
|     } | ||||
| } | ||||
| 	void Daemon::PostInitialization([[maybe_unused]] Poco::Util::Application &self) { | ||||
| 		AssetDir_ = MicroService::instance().ConfigPath("openwifi.restapi.wwwassets"); | ||||
| 	} | ||||
|  | ||||
| 	void DaemonPostInitialization(Poco::Util::Application &self) { | ||||
| 		Daemon()->PostInitialization(self); | ||||
| 	} | ||||
| } // namespace OpenWifi | ||||
|  | ||||
| int main(int argc, char **argv) { | ||||
|     try { | ||||
|         SSL_library_init(); | ||||
|         Aws::SDKOptions AwsOptions; | ||||
|         AwsOptions.memoryManagementOptions.memoryManager = nullptr; | ||||
|         AwsOptions.cryptoOptions.initAndCleanupOpenSSL = false; | ||||
|         AwsOptions.httpOptions.initAndCleanupCurl = true; | ||||
| 	try { | ||||
| 		SSL_library_init(); | ||||
| 		Aws::SDKOptions AwsOptions; | ||||
| 		AwsOptions.memoryManagementOptions.memoryManager = nullptr; | ||||
| 		AwsOptions.cryptoOptions.initAndCleanupOpenSSL = false; | ||||
| 		AwsOptions.httpOptions.initAndCleanupCurl = true; | ||||
|  | ||||
|         Aws::InitAPI(AwsOptions); | ||||
| 		Aws::InitAPI(AwsOptions); | ||||
|  | ||||
|         int ExitCode=0; | ||||
|         { | ||||
|             auto App = OpenWifi::Daemon::instance(); | ||||
|             ExitCode =  App->run(argc, argv); | ||||
|         } | ||||
|         ShutdownAPI(AwsOptions); | ||||
|         return ExitCode; | ||||
|     } catch (Poco::Exception &exc) { | ||||
|         std::cout << exc.displayText() << std::endl; | ||||
|         return Poco::Util::Application::EXIT_SOFTWARE; | ||||
|     } | ||||
| 		int ExitCode = 0; | ||||
| 		{ | ||||
| 			auto App = OpenWifi::Daemon::instance(); | ||||
| 			ExitCode = App->run(argc, argv); | ||||
| 		} | ||||
| 		ShutdownAPI(AwsOptions); | ||||
| 		return ExitCode; | ||||
| 	} catch (Poco::Exception &exc) { | ||||
| 		std::cout << exc.displayText() << std::endl; | ||||
| 		return Poco::Util::Application::EXIT_SOFTWARE; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| // end of namespace | ||||
|   | ||||
							
								
								
									
										69
									
								
								src/Daemon.h
									
									
									
									
									
								
							
							
						
						| @@ -2,57 +2,50 @@ | ||||
| // Created by stephane bourque on 2021-06-10. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRALSEC_DAEMON_H | ||||
| #define UCENTRALSEC_DAEMON_H | ||||
| #pragma once | ||||
|  | ||||
| #include <iostream> | ||||
| #include <cstdlib> | ||||
| #include <vector> | ||||
| #include <iostream> | ||||
| #include <set> | ||||
| #include <vector> | ||||
|  | ||||
| #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 uint64_t vDAEMON_BUS_TIMER = 5000; | ||||
| 	[[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) {}; | ||||
| 	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){}; | ||||
|  | ||||
|         void PostInitialization(Poco::Util::Application &self); | ||||
|         static Daemon *instance(); | ||||
|         inline const std::string & AssetDir() { return AssetDir_; } | ||||
|     private: | ||||
|         static Daemon 		*instance_; | ||||
|         std::string         AssetDir_; | ||||
|     }; | ||||
| 		void PostInitialization(Poco::Util::Application &self); | ||||
| 		static Daemon *instance(); | ||||
| 		inline const std::string &AssetDir() { return AssetDir_; } | ||||
|  | ||||
|     inline Daemon * Daemon() { return Daemon::instance(); } | ||||
|     inline void DaemonPostInitialization(Poco::Util::Application &self) { | ||||
|         Daemon()->PostInitialization(self); | ||||
|     } | ||||
| } | ||||
| 	  private: | ||||
| 		static Daemon *instance_; | ||||
| 		std::string AssetDir_; | ||||
| 	}; | ||||
|  | ||||
| #endif //UCENTRALSEC_DAEMON_H | ||||
| 	inline Daemon *Daemon() { return Daemon::instance(); } | ||||
| 	void DaemonPostInitialization(Poco::Util::Application &self); | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -2,114 +2,125 @@ | ||||
| // Created by stephane bourque on 2021-10-11. | ||||
| // | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| #include "MFAServer.h" | ||||
| #include "AuthService.h" | ||||
| #include "SMSSender.h" | ||||
| #include "SMTPMailerService.h" | ||||
| #include "AuthService.h" | ||||
| #include "TotpCache.h" | ||||
|  | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
| #include "framework/utils.h" | ||||
|  | ||||
| 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) { | ||||
|         std::lock_guard G(Mutex_); | ||||
| 	bool MFAServer::StartMFAChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, | ||||
| 									  Poco::JSON::Object &ChallengeStart) { | ||||
| 		std::lock_guard G(Mutex_); | ||||
|  | ||||
|         CleanCache(); | ||||
| 		CleanCache(); | ||||
|  | ||||
|         if(!MethodEnabled(UInfo.userinfo.userTypeProprietaryInfo.mfa.method)) | ||||
|             return false; | ||||
| 		if (!MethodEnabled(UInfo.userinfo.userTypeProprietaryInfo.mfa.method)) | ||||
| 			return false; | ||||
|  | ||||
|         std::string Challenge = MakeChallenge(); | ||||
|         std::string uuid = MicroService::CreateUUID(); | ||||
|         uint64_t Created = OpenWifi::Now(); | ||||
| 		std::string Challenge = MakeChallenge(); | ||||
| 		std::string uuid = MicroServiceCreateUUID(); | ||||
| 		uint64_t Created = Utils::Now(); | ||||
|  | ||||
|         ChallengeStart.set("uuid",uuid); | ||||
|         ChallengeStart.set("created", Created); | ||||
|         ChallengeStart.set("question", "mfa challenge"); | ||||
|         ChallengeStart.set("method", UInfo.userinfo.userTypeProprietaryInfo.mfa.method); | ||||
| 		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 }; | ||||
|         return SendChallenge(UInfo, UInfo.userinfo.userTypeProprietaryInfo.mfa.method, Challenge); | ||||
|     } | ||||
| 		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()) { | ||||
|             return true; | ||||
|         } | ||||
| 	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; | ||||
| 		} | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
|     bool MFAServer::ResendCode(const std::string &uuid) { | ||||
|         std::lock_guard G(Mutex_); | ||||
|         auto Hint = Cache_.find(uuid); | ||||
|         if(Hint==Cache_.end()) | ||||
|             return false; | ||||
|         return SendChallenge(Hint->second.UInfo, Hint->second.Method, Hint->second.Answer); | ||||
|     } | ||||
| 	bool MFAServer::ResendCode(const std::string &uuid) { | ||||
| 		std::lock_guard G(Mutex_); | ||||
| 		auto Hint = Cache_.find(uuid); | ||||
| 		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) { | ||||
|         std::lock_guard G(Mutex_); | ||||
| 	bool MFAServer::CompleteMFAChallenge(const Poco::JSON::Object::Ptr &ChallengeResponse, | ||||
| 										 SecurityObjects::UserInfoAndPolicy &UInfo) { | ||||
| 		std::lock_guard G(Mutex_); | ||||
|  | ||||
|         if(!ChallengeResponse->has("uuid") || !ChallengeResponse->has("answer")) | ||||
|             return false; | ||||
| 		if (!ChallengeResponse->has("uuid") || !ChallengeResponse->has("answer")) | ||||
| 			return false; | ||||
|  | ||||
|         auto uuid = ChallengeResponse->get("uuid").toString(); | ||||
|         auto Hint = Cache_.find(uuid); | ||||
|         if(Hint == end(Cache_)) { | ||||
|             return false; | ||||
|         } | ||||
| 		auto uuid = ChallengeResponse->get("uuid").toString(); | ||||
| 		auto Hint = Cache_.find(uuid); | ||||
| 		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)) { | ||||
|                 return false; | ||||
|             } | ||||
|         } else if(Hint->second.Answer!=answer) { | ||||
|             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)) { | ||||
| 				return false; | ||||
| 			} | ||||
| 		} else if (Hint->second.Answer != answer) { | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
|         UInfo = Hint->second.UInfo; | ||||
|         Cache_.erase(Hint); | ||||
|         return true; | ||||
|     } | ||||
| 		UInfo = Hint->second.UInfo; | ||||
| 		Cache_.erase(Hint); | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
|     bool MFAServer::MethodEnabled(const std::string &Method) { | ||||
|         if(Method==MFAMETHODS::SMS) | ||||
|             return SMSSender()->Enabled(); | ||||
| 	bool MFAServer::MethodEnabled(const std::string &Method) { | ||||
| 		if (Method == MFAMETHODS::SMS) | ||||
| 			return SMSSender()->Enabled(); | ||||
|  | ||||
|         if(Method==MFAMETHODS::EMAIL) | ||||
|             return SMTPMailerService()->Enabled(); | ||||
| 		if (Method == MFAMETHODS::EMAIL) | ||||
| 			return SMTPMailerService()->Enabled(); | ||||
|  | ||||
|         if(Method==MFAMETHODS::AUTHENTICATOR) | ||||
|             return true; | ||||
| 		if (Method == MFAMETHODS::AUTHENTICATOR) | ||||
| 			return true; | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
|     void MFAServer::CleanCache() { | ||||
|         // it is assumed that you have locked Cache_ at this point. | ||||
|         uint64_t Now = OpenWifi::Now(); | ||||
|         for(auto i=begin(Cache_);i!=end(Cache_);) { | ||||
|             if((Now-i->second.Created)>300) { | ||||
|                 i = Cache_.erase(i); | ||||
|             } else { | ||||
|                 ++i; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 	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) { | ||||
| 				i = Cache_.erase(i); | ||||
| 			} else { | ||||
| 				++i; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } // namespace OpenWifi | ||||
| @@ -4,61 +4,62 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "Poco/JSON/Object.h" | ||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
| #include "framework/SubSystemServer.h" | ||||
|  | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     namespace MFAMETHODS { | ||||
|         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 bool Validate(const std::string &M) { | ||||
|             return std::find(cbegin(Methods), cend(Methods),M)!=Methods.end(); | ||||
|         } | ||||
|     } | ||||
| 	namespace MFAMETHODS { | ||||
| 		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 bool Validate(const std::string &M) { | ||||
| 			return std::find(cbegin(Methods), cend(Methods), M) != Methods.end(); | ||||
| 		} | ||||
| 	} // namespace MFAMETHODS | ||||
|  | ||||
|     struct MFACacheEntry { | ||||
|         SecurityObjects::UserInfoAndPolicy  UInfo; | ||||
|         std::string                         Answer; | ||||
|         uint64_t                            Created; | ||||
|         std::string                         Method; | ||||
|     }; | ||||
| 	struct MFACacheEntry { | ||||
| 		SecurityObjects::UserInfoAndPolicy UInfo; | ||||
| 		std::string Answer; | ||||
| 		uint64_t Created; | ||||
| 		std::string Method; | ||||
| 	}; | ||||
|  | ||||
| 	typedef std::map<std::string, MFACacheEntry> MFAChallengeCache; | ||||
|  | ||||
|     typedef std::map<std::string,MFACacheEntry>     MFAChallengeCache; | ||||
| 	class MFAServer : public SubSystemServer { | ||||
| 	  public: | ||||
| 		int Start() override; | ||||
| 		void Stop() override; | ||||
| 		static auto instance() { | ||||
| 			static auto instance_ = new MFAServer; | ||||
| 			return instance_; | ||||
| 		} | ||||
|  | ||||
|     class MFAServer : public SubSystemServer{ | ||||
|     public: | ||||
|         int Start() override; | ||||
|         void Stop() override; | ||||
|         static auto instance() { | ||||
|             static auto instance_ = new MFAServer; | ||||
|             return instance_; | ||||
|         } | ||||
| 		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); | ||||
|  | ||||
|         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 inline std::string MakeChallenge() { | ||||
| 			return fmt::format("{0:06}", MicroServiceRandom(1, 999999)); | ||||
| 		} | ||||
|  | ||||
|         static inline std::string MakeChallenge() { | ||||
|             return fmt::format("{0:06}" , MicroService::instance().Random(1,999999) ); | ||||
|         } | ||||
| 	  private: | ||||
| 		MFAChallengeCache Cache_; | ||||
| 		MFAServer() noexcept : SubSystemServer("MFServer", "MFA-SVR", "mfa") {} | ||||
|  | ||||
|     private: | ||||
|         MFAChallengeCache   Cache_; | ||||
|         MFAServer() noexcept: | ||||
|             SubSystemServer("MFServer", "MFA-SVR", "mfa") | ||||
|             { | ||||
|             } | ||||
|  | ||||
|         void CleanCache(); | ||||
|     }; | ||||
|  | ||||
|     inline auto MFAServer() { return MFAServer::instance(); } | ||||
| } | ||||
| 		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 | ||||
| @@ -9,67 +9,104 @@ | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     class MessagingTemplates { | ||||
|     public: | ||||
|         static MessagingTemplates & instance() { | ||||
|             static auto instance = new MessagingTemplates; | ||||
|             return *instance; | ||||
|         } | ||||
| 	class MessagingTemplates { | ||||
| 	  public: | ||||
| 		static MessagingTemplates &instance() { | ||||
| 			static auto instance = new MessagingTemplates; | ||||
| 			return *instance; | ||||
| 		} | ||||
|  | ||||
|         enum EMAIL_REASON { | ||||
|             FORGOT_PASSWORD = 0, | ||||
|             EMAIL_VERIFICATION, | ||||
|             SIGNUP_VERIFICATION, | ||||
|             EMAIL_INVITATION, | ||||
|             VERIFICATION_CODE, | ||||
|             SUB_FORGOT_PASSWORD, | ||||
|             SUB_EMAIL_VERIFICATION, | ||||
|             SUB_VERIFICATION_CODE | ||||
|         }; | ||||
| 		enum EMAIL_REASON { | ||||
| 			FORGOT_PASSWORD = 0, | ||||
| 			EMAIL_VERIFICATION, | ||||
| 			SUB_SIGNUP_VERIFICATION, | ||||
| 			EMAIL_INVITATION, | ||||
| 			VERIFICATION_CODE, | ||||
| 			SUB_FORGOT_PASSWORD, | ||||
| 			SUB_EMAIL_VERIFICATION, | ||||
| 			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()) | ||||
|                 return "/" + filename; | ||||
|             return "/" + OperatorName + "/" + filename; | ||||
|         } | ||||
| 		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="") { | ||||
|             switch (r) { | ||||
|                 case FORGOT_PASSWORD: return AddOperator(EmailTemplateNames[FORGOT_PASSWORD],OperatorName); | ||||
|                 case EMAIL_VERIFICATION: return AddOperator(EmailTemplateNames[EMAIL_VERIFICATION],OperatorName); | ||||
|                 case SIGNUP_VERIFICATION: return AddOperator(EmailTemplateNames[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); | ||||
|                 default: | ||||
|                     return ""; | ||||
|             } | ||||
|         } | ||||
| 		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 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 = "" ) { | ||||
|             return AddOperator("logo.jpg", OperatorName); | ||||
|         } | ||||
| 		static std::string Logo(const std::string &OperatorName = "") { | ||||
| 			return AddOperator("logo.png", OperatorName); | ||||
| 		} | ||||
|  | ||||
|         static std::string SubLogo(const std::string &OperatorName = "" ) { | ||||
|             return AddOperator("sub_logo.jpg", OperatorName); | ||||
|         } | ||||
| 		static std::string SubLogo(const std::string &OperatorName = "") { | ||||
| 			return AddOperator("sub_logo.png", OperatorName); | ||||
| 		} | ||||
|  | ||||
|     private: | ||||
|         inline const static std::vector<std::string>  EmailTemplateNames = { | ||||
|                 "password_reset", | ||||
|                 "email_verification", | ||||
|                 "signup_verification", | ||||
|                 "email_invitation", | ||||
|                 "verification_code", | ||||
|                 "sub_password_reset", | ||||
|                 "sub_email_verification", | ||||
|                 "sub_verification_code" | ||||
|         }; | ||||
|     }; | ||||
| 	  private: | ||||
| 		inline const static std::vector<std::string> EmailTemplateNames = { | ||||
| 			"password_reset", | ||||
| 			"email_verification", | ||||
| 			"sub_signup_verification", | ||||
| 			"email_invitation", | ||||
| 			"verification_code", | ||||
| 			"sub_password_reset", | ||||
| 			"sub_email_verification", | ||||
| 			"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,281 +7,351 @@ | ||||
|  | ||||
| #include "RESTAPI_action_links.h" | ||||
| #include "StorageService.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/OpenAPIRequests.h" | ||||
| #include "framework/RESTAPI_PartHandler.h" | ||||
|  | ||||
| #include "Daemon.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     void RESTAPI_action_links::DoGet() { | ||||
| #if defined(TIP_CERT_SERVICE) | ||||
| 	bool ProcessExternalActionLinks(RESTAPIHandler &handler, const std::string &Id, | ||||
| 									const std::string &Action); | ||||
| #endif | ||||
|  | ||||
|         auto Action = GetParameter("action",""); | ||||
|         auto Id = GetParameter("id",""); | ||||
| 	void RESTAPI_action_links::DoGet() { | ||||
|  | ||||
|         SecurityObjects::ActionLink Link; | ||||
|         if(!StorageService()->ActionLinksDB().GetActionLink(Id,Link)) | ||||
|             return DoReturnA404(); | ||||
| 		auto Action = GetParameter("action", ""); | ||||
| 		auto Id = GetParameter("id", ""); | ||||
|  | ||||
|         if(Action=="password_reset") | ||||
|             return RequestResetPassword(Link); | ||||
|         else if(Action=="sub_password_reset") | ||||
|             return RequestSubResetPassword(Link); | ||||
|         else if(Action=="email_verification") | ||||
|             return DoEmailVerification(Link); | ||||
|         else if(Action=="sub_email_verification") | ||||
|             return DoSubEmailVerification(Link); | ||||
|         else if(Action=="signup_verification") | ||||
|             return DoNewSubVerification(Link); | ||||
|         else | ||||
|             return DoReturnA404(); | ||||
|     } | ||||
| #if defined(TIP_CERT_SERVICE) | ||||
| 		if (!OpenWifi::ProcessExternalActionLinks(*this, Id, Action)) { | ||||
| 			return; | ||||
| 		} | ||||
| #endif | ||||
|  | ||||
|     void RESTAPI_action_links::DoPost() { | ||||
|         auto Action = GetParameter("action",""); | ||||
| 		SecurityObjects::ActionLink Link; | ||||
| 		if (!StorageService()->ActionLinksDB().GetActionLink(Id, Link)) | ||||
| 			return DoReturnA404(); | ||||
|  | ||||
|         if(Action=="password_reset") | ||||
|             return CompleteResetPassword(); | ||||
|         else if(Action=="sub_password_reset") | ||||
|             return CompleteResetPassword(); | ||||
|         else if(Action=="signup_completion") | ||||
|             return CompleteSubVerification(); | ||||
|         else if(Action=="email_invitation") | ||||
|             return CompleteEmailInvitation(); | ||||
|         else | ||||
|             return DoReturnA404(); | ||||
|     } | ||||
| 		if (Action == "password_reset") | ||||
| 			return RequestResetPassword(Link); | ||||
| 		else if (Action == "sub_password_reset") | ||||
| 			return RequestSubResetPassword(Link); | ||||
| 		else if (Action == "email_verification") | ||||
| 			return DoEmailVerification(Link); | ||||
| 		else if (Action == "sub_email_verification") | ||||
| 			return DoSubEmailVerification(Link); | ||||
| 		else if (Action == "signup_verification") | ||||
| 			return DoNewSubVerification(Link); | ||||
| 		else | ||||
| 			return DoReturnA404(); | ||||
| 	} | ||||
|  | ||||
|     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}, | ||||
|                                           {"PASSWORD_VALIDATION", AuthService()->PasswordValidationExpression()}}; | ||||
|         SendHTMLFileBack(FormFile,FormVars); | ||||
|     } | ||||
| 	void RESTAPI_action_links::DoPost() { | ||||
| 		auto Action = GetParameter("action", ""); | ||||
|  | ||||
|     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() + "/signup_verification.html"}; | ||||
|         Types::StringPairVec    FormVars{ {"UUID", Link.id}, | ||||
|                                           {"PASSWORD_VALIDATION", AuthService()->PasswordValidationExpression()}}; | ||||
|         SendHTMLFileBack(FormFile,FormVars); | ||||
|     } | ||||
| 		if (Action == "password_reset") | ||||
| 			return CompleteResetPassword(); | ||||
| 		else if (Action == "sub_password_reset") | ||||
| 			return CompleteResetPassword(); | ||||
| 		else if (Action == "signup_completion") | ||||
| 			return CompleteSubVerification(); | ||||
| 		else if (Action == "email_invitation") | ||||
| 			return CompleteEmailInvitation(); | ||||
| 		else | ||||
| 			return DoReturnA404(); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_action_links::CompleteResetPassword() { | ||||
|         //  form has been posted... | ||||
|         RESTAPI_PartHandler PartHandler; | ||||
|         Poco::Net::HTMLForm Form(*Request, Request->stream(), PartHandler); | ||||
|         if (!Form.empty()) { | ||||
| 	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())); | ||||
| 	} | ||||
|  | ||||
|             auto Password1 = Form.get("password1","bla"); | ||||
|             auto Password2 = Form.get("password2","blu"); | ||||
|             auto Id = Form.get("id",""); | ||||
|             auto now = OpenWifi::Now(); | ||||
| 	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}, | ||||
| 			{"PASSWORD_VALIDATION", AuthService()->PasswordValidationExpression()}}; | ||||
| 		AddGlobalVars(FormVars); | ||||
| 		SendHTMLFileBack(FormFile, FormVars); | ||||
| 	} | ||||
|  | ||||
|             SecurityObjects::ActionLink Link; | ||||
|             if(!StorageService()->ActionLinksDB().GetActionLink(Id,Link)) | ||||
|                 return DoReturnA404(); | ||||
| 	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}, | ||||
| 			{"PASSWORD_VALIDATION", AuthService()->PasswordValidationExpression()}}; | ||||
| 		AddGlobalVars(FormVars); | ||||
| 		SendHTMLFileBack(FormFile, FormVars); | ||||
| 	} | ||||
|  | ||||
|             if(now > Link.expires) { | ||||
|                 StorageService()->ActionLinksDB().CancelAction(Id); | ||||
|                 return DoReturnA404(); | ||||
|             } | ||||
| 	void RESTAPI_action_links::CompleteResetPassword() { | ||||
| 		//  form has been posted... | ||||
| 		RESTAPI_PartHandler PartHandler; | ||||
| 		Poco::Net::HTMLForm Form(*Request, Request->stream(), PartHandler); | ||||
| 		if (!Form.empty()) { | ||||
|  | ||||
|             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); | ||||
|             } | ||||
| 			auto Password1 = Form.get("password1", "bla"); | ||||
| 			auto Password2 = Form.get("password2", "blu"); | ||||
| 			auto Id = Form.get("id", ""); | ||||
| 			auto now = OpenWifi::Now(); | ||||
|  | ||||
|             SecurityObjects::UserInfo   UInfo; | ||||
| 			SecurityObjects::ActionLink Link; | ||||
| 			if (!StorageService()->ActionLinksDB().GetActionLink(Id, Link)) | ||||
| 				return DoReturnA404(); | ||||
|  | ||||
|             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); | ||||
|             } | ||||
| 			if (now > Link.expires) { | ||||
| 				StorageService()->ActionLinksDB().CancelAction(Id); | ||||
| 				return DoReturnA404(); | ||||
| 			} | ||||
|  | ||||
|             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 (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 + ")"}}; | ||||
| 				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); | ||||
|             } | ||||
| 			SecurityObjects::UserInfo UInfo; | ||||
|  | ||||
|             UInfo.modified = OpenWifi::Now(); | ||||
|             if(Link.userAction) | ||||
|                 StorageService()->UserDB().UpdateUserInfo(UInfo.email,Link.userId,UInfo); | ||||
|             else | ||||
|                 StorageService()->SubDB().UpdateUserInfo(UInfo.email,Link.userId,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."}}; | ||||
| 				AddGlobalVars(FormVars); | ||||
| 				return SendHTMLFileBack(FormFile, FormVars); | ||||
| 			} | ||||
|  | ||||
|             Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_success.html"}; | ||||
|             Types::StringPairVec    FormVars{ {"UUID", Id}, | ||||
|                                               {"USERNAME", UInfo.email}, | ||||
|                                               {"ACTION_LINK",MicroService::instance().GetUIURI()}}; | ||||
|             StorageService()->ActionLinksDB().CompleteAction(Id); | ||||
|             SendHTMLFileBack(FormFile,FormVars); | ||||
|         } else { | ||||
|             DoReturnA404(); | ||||
|         } | ||||
|     } | ||||
| 			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); | ||||
| 			} | ||||
|  | ||||
|     void RESTAPI_action_links::CompleteSubVerification() { | ||||
|         RESTAPI_PartHandler PartHandler; | ||||
|         Poco::Net::HTMLForm Form(*Request, Request->stream(), PartHandler); | ||||
| 			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); | ||||
| 			} | ||||
|  | ||||
|         if (!Form.empty()) { | ||||
|             auto Password1 = Form.get("password1","bla"); | ||||
|             auto Password2 = Form.get("password2","blu"); | ||||
|             auto Id = Form.get("id",""); | ||||
|             auto now = OpenWifi::Now(); | ||||
| 			UInfo.modified = OpenWifi::Now(); | ||||
| 			if (Link.userAction) | ||||
| 				StorageService()->UserDB().UpdateUserInfo(UInfo.email, Link.userId, UInfo); | ||||
| 			else | ||||
| 				StorageService()->SubDB().UpdateUserInfo(UInfo.email, Link.userId, UInfo); | ||||
|  | ||||
|             SecurityObjects::ActionLink Link; | ||||
|             if(!StorageService()->ActionLinksDB().GetActionLink(Id,Link)) { | ||||
|                 return DoReturnA404(); | ||||
|             } | ||||
| 			Poco::File FormFile{Daemon()->AssetDir() + "/password_reset_success.html"}; | ||||
| 			Types::StringPairVec FormVars{{"UUID", Id}, | ||||
| 										  {"USERNAME", UInfo.email}, | ||||
| 										  {"ACTION_LINK", MicroService::instance().GetUIURI()}}; | ||||
| 			AddGlobalVars(FormVars); | ||||
| 			StorageService()->ActionLinksDB().CompleteAction(Id); | ||||
| 			SendHTMLFileBack(FormFile, FormVars); | ||||
| 		} else { | ||||
| 			DoReturnA404(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|             if(now > Link.expires) { | ||||
|                 StorageService()->ActionLinksDB().CancelAction(Id); | ||||
|                 return DoReturnA404(); | ||||
|             } | ||||
| 	void RESTAPI_action_links::CompleteSubVerification() { | ||||
| 		RESTAPI_PartHandler PartHandler; | ||||
| 		Poco::Net::HTMLForm Form(*Request, Request->stream(), PartHandler); | ||||
|  | ||||
|             if(Password1!=Password2 || !AuthService()->ValidateSubPassword(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); | ||||
|             } | ||||
| 		if (!Form.empty()) { | ||||
| 			auto Password1 = Form.get("password1", "bla"); | ||||
| 			auto Password2 = Form.get("password2", "blu"); | ||||
| 			auto Id = Form.get("id", ""); | ||||
| 			auto now = OpenWifi::Now(); | ||||
|  | ||||
|             SecurityObjects::UserInfo   UInfo; | ||||
|             bool Found = StorageService()->SubDB().GetUserById(Link.userId,UInfo); | ||||
|             if(!Found) { | ||||
|                 Poco::File  FormFile{ Daemon()->AssetDir() + "/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); | ||||
|             } | ||||
| 			SecurityObjects::ActionLink Link; | ||||
| 			if (!StorageService()->ActionLinksDB().GetActionLink(Id, Link)) { | ||||
| 				return DoReturnA404(); | ||||
| 			} | ||||
|  | ||||
|             if(UInfo.blackListed || UInfo.suspended) { | ||||
|                 Poco::File  FormFile{ Daemon()->AssetDir() + "/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 (now > Link.expires) { | ||||
| 				StorageService()->ActionLinksDB().CancelAction(Id); | ||||
| 				return DoReturnA404(); | ||||
| 			} | ||||
|  | ||||
|             bool GoodPassword = AuthService()->SetSubPassword(Password1,UInfo); | ||||
|             if(!GoodPassword) { | ||||
|                 Poco::File  FormFile{ Daemon()->AssetDir() + "/signup_verification_error.html"}; | ||||
|                 Types::StringPairVec    FormVars{ {"UUID", Id}, | ||||
|                                                   {"ERROR_TEXT", "You cannot reuse one of your recent passwords."}}; | ||||
|                 return SendHTMLFileBack(FormFile,FormVars); | ||||
|             } | ||||
| 			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 + ")"}}; | ||||
| 				AddGlobalVars(FormVars); | ||||
| 				return SendHTMLFileBack(FormFile, FormVars); | ||||
| 			} | ||||
|  | ||||
|             UInfo.modified = OpenWifi::Now(); | ||||
|             UInfo.changePassword = false; | ||||
|             UInfo.lastEmailCheck = OpenWifi::Now(); | ||||
|             UInfo.waitingForEmailCheck = false; | ||||
|             UInfo.validated = OpenWifi::Now(); | ||||
| 			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."}}; | ||||
| 				AddGlobalVars(FormVars); | ||||
| 				return SendHTMLFileBack(FormFile, FormVars); | ||||
| 			} | ||||
|  | ||||
|             StorageService()->SubDB().UpdateUserInfo(UInfo.email,Link.userId,UInfo); | ||||
| 			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); | ||||
| 			} | ||||
|  | ||||
|             Poco::File  FormFile{ Daemon()->AssetDir() + "/signup_verification_success.html"}; | ||||
|             Types::StringPairVec    FormVars{ {"UUID", Id}, | ||||
|                                               {"USERNAME", UInfo.email} }; | ||||
|             StorageService()->ActionLinksDB().CompleteAction(Id); | ||||
| 			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); | ||||
| 			} | ||||
|  | ||||
|             //  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)); | ||||
|             Poco::JSON::Object::Ptr Response; | ||||
|             auto Status = ProvRequest.Do(Response); | ||||
|             std::stringstream ooo; | ||||
|             if(Response!= nullptr) | ||||
|                 Response->stringify(ooo); | ||||
|             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)); | ||||
|         } else { | ||||
|             DoReturnA404(); | ||||
|         } | ||||
|     } | ||||
| 			UInfo.modified = OpenWifi::Now(); | ||||
| 			UInfo.changePassword = false; | ||||
| 			UInfo.lastEmailCheck = OpenWifi::Now(); | ||||
| 			UInfo.waitingForEmailCheck = false; | ||||
| 			UInfo.validated = OpenWifi::Now(); | ||||
|  | ||||
|     void RESTAPI_action_links::DoEmailVerification(SecurityObjects::ActionLink &Link) { | ||||
|         auto now = OpenWifi::Now(); | ||||
| 			StorageService()->SubDB().UpdateUserInfo(UInfo.email, Link.userId, UInfo); | ||||
|  | ||||
|         if(now > Link.expires) { | ||||
|             StorageService()->ActionLinksDB().CancelAction(Link.id); | ||||
|             return DoReturnA404(); | ||||
|         } | ||||
| 			Poco::File FormFile{Daemon()->AssetDir() + "/sub_signup_verification_success.html"}; | ||||
| 			Types::StringPairVec FormVars{{"UUID", Id}, {"USERNAME", UInfo.email}}; | ||||
| 			StorageService()->ActionLinksDB().CompleteAction(Id); | ||||
|  | ||||
|         SecurityObjects::UserInfo 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}, | ||||
|                                           {"ERROR_TEXT", "This does not appear to be a valid email verification link.."}}; | ||||
|             Poco::File FormFile{Daemon()->AssetDir() + "/email_verification_error.html"}; | ||||
|             return SendHTMLFileBack(FormFile, FormVars); | ||||
|         } | ||||
| 			//  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)); | ||||
| 			Poco::JSON::Object::Ptr Response; | ||||
| 			auto Status = ProvRequest.Do(Response); | ||||
| 			std::stringstream ooo; | ||||
| 			if (Response != nullptr) | ||||
| 				Response->stringify(ooo); | ||||
| 			Logger().information(fmt::format( | ||||
| 				"({}): Completed subscriber e-mail verification. Provisioning notified, Error={}.", | ||||
| 				UInfo.email, Status)); | ||||
| 			AddGlobalVars(FormVars); | ||||
| 			SendHTMLFileBack(FormFile, FormVars); | ||||
| 			Logger().information(fmt::format( | ||||
| 				"({}): Completed subscriber e-mail verification. FORM notified.", UInfo.email)); | ||||
| 		} else { | ||||
| 			DoReturnA404(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|         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) | ||||
|             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()}}; | ||||
|         Poco::File FormFile{Daemon()->AssetDir() + "/email_verification_success.html"}; | ||||
|         StorageService()->ActionLinksDB().CompleteAction(Link.id); | ||||
|         SendHTMLFileBack(FormFile, FormVars); | ||||
|     } | ||||
| 	void RESTAPI_action_links::DoEmailVerification(SecurityObjects::ActionLink &Link) { | ||||
| 		auto now = OpenWifi::Now(); | ||||
|  | ||||
|     void RESTAPI_action_links::DoReturnA404() { | ||||
|         Types::StringPairVec FormVars; | ||||
|         Poco::File FormFile{Daemon()->AssetDir() + "/404_error.html"}; | ||||
|         SendHTMLFileBack(FormFile, FormVars); | ||||
|     } | ||||
| 		if (now > Link.expires) { | ||||
| 			StorageService()->ActionLinksDB().CancelAction(Link.id); | ||||
| 			return DoReturnA404(); | ||||
| 		} | ||||
|  | ||||
|     void RESTAPI_action_links::CompleteEmailInvitation() { | ||||
|         /// TODO: | ||||
|     } | ||||
| 		SecurityObjects::UserInfo 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}, | ||||
| 				{"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); | ||||
| 		} | ||||
|  | ||||
|     void RESTAPI_action_links::RequestSubResetPassword([[maybe_unused]] SecurityObjects::ActionLink &Link) { | ||||
| 		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) | ||||
| 			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()}}; | ||||
| 		Poco::File FormFile{Daemon()->AssetDir() + "/email_verification_success.html"}; | ||||
| 		AddGlobalVars(FormVars); | ||||
| 		StorageService()->ActionLinksDB().CompleteAction(Link.id); | ||||
| 		SendHTMLFileBack(FormFile, FormVars); | ||||
| 	} | ||||
|  | ||||
|     } | ||||
| 	void RESTAPI_action_links::DoReturnA404() { | ||||
| 		Types::StringPairVec FormVars; | ||||
| 		Poco::File FormFile{Daemon()->AssetDir() + "/404_error.html"}; | ||||
| 		AddGlobalVars(FormVars); | ||||
| 		SendHTMLFileBack(FormFile, FormVars); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_action_links::DoSubEmailVerification([[maybe_unused]] SecurityObjects::ActionLink &Link) { | ||||
| 	void RESTAPI_action_links::CompleteEmailInvitation() { | ||||
| 		/// TODO: | ||||
| 	} | ||||
|  | ||||
|     } | ||||
| 	void RESTAPI_action_links::RequestSubResetPassword( | ||||
| 		[[maybe_unused]] SecurityObjects::ActionLink &Link) {} | ||||
|  | ||||
| } | ||||
| 	void RESTAPI_action_links::DoSubEmailVerification( | ||||
| 		[[maybe_unused]] SecurityObjects::ActionLink &Link) {} | ||||
|  | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -4,36 +4,35 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_action_links : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_action_links(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) | ||||
|                 : RESTAPIHandler(bindings, L, | ||||
|              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}) {} | ||||
|         static auto PathName() { return std::list<std::string>{"/api/v1/actionLink"}; }; | ||||
|         void RequestResetPassword(SecurityObjects::ActionLink &Link); | ||||
|         void RequestSubResetPassword(SecurityObjects::ActionLink &Link); | ||||
|         void CompleteResetPassword(); | ||||
|         void CompleteSubVerification(); | ||||
|         void DoEmailVerification(SecurityObjects::ActionLink &Link); | ||||
|         void DoSubEmailVerification(SecurityObjects::ActionLink &Link); | ||||
|         void DoReturnA404(); | ||||
|         void DoNewSubVerification(SecurityObjects::ActionLink &Link); | ||||
|         void CompleteEmailInvitation(); | ||||
| 	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) | ||||
| 			: RESTAPIHandler(bindings, L, | ||||
| 							 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}) {} | ||||
| 		static auto PathName() { return std::list<std::string>{"/api/v1/actionLink"}; }; | ||||
| 		void RequestResetPassword(SecurityObjects::ActionLink &Link); | ||||
| 		void RequestSubResetPassword(SecurityObjects::ActionLink &Link); | ||||
| 		void CompleteResetPassword(); | ||||
| 		void CompleteSubVerification(); | ||||
| 		void DoEmailVerification(SecurityObjects::ActionLink &Link); | ||||
| 		void DoSubEmailVerification(SecurityObjects::ActionLink &Link); | ||||
| 		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 DoGet() final; | ||||
| 		void DoPost() final; | ||||
| 		void DoDelete() final{}; | ||||
| 		void DoPut() final{}; | ||||
| 	}; | ||||
| } // namespace OpenWifi | ||||
|   | ||||
							
								
								
									
										165
									
								
								src/RESTAPI/RESTAPI_apiKey_handler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,165 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-11-04. | ||||
| // | ||||
|  | ||||
| #include "RESTAPI_apiKey_handler.h" | ||||
| #include "RESTAPI/RESTAPI_db_helpers.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	void RESTAPI_apiKey_handler::DoGet() { | ||||
| 		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) { | ||||
| 			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) { | ||||
| 				Sanitize(UserInfo_, key); | ||||
| 			} | ||||
| 			Poco::JSON::Object Answer; | ||||
| 			List.to_json(Answer); | ||||
| 			return ReturnObject(Answer); | ||||
| 		} | ||||
| 		return NotFound(); | ||||
| 	} | ||||
|  | ||||
| 	void RESTAPI_apiKey_handler::DoDelete() { | ||||
| 		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) { | ||||
| 			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
| 		} | ||||
|  | ||||
| 		if (user_uuid != UserInfo_.userinfo.id) { | ||||
| 			if (!StorageService()->UserDB().Exists("id", user_uuid)) { | ||||
| 				return NotFound(); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		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) { | ||||
| 				AuthService()->RemoveTokenSystemWide(ApiKey.apiKey); | ||||
| 				DB_.DeleteRecord("id", ApiKeyId); | ||||
| 				return OK(); | ||||
| 			} | ||||
| 			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
| 		} | ||||
| 		return NotFound(); | ||||
| 	} | ||||
|  | ||||
| 	void RESTAPI_apiKey_handler::DoPost() { | ||||
| 		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) { | ||||
| 			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
| 		} | ||||
|  | ||||
| 		if (user_uuid != UserInfo_.userinfo.id) { | ||||
| 			//  Must verify if the user exists | ||||
| 			if (!StorageService()->UserDB().Exists("id", user_uuid)) { | ||||
| 				return BadRequest(RESTAPI::Errors::UserMustExist); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		SecurityObjects::ApiKeyEntry NewKey; | ||||
| 		if (!NewKey.from_json(ParsedBody_)) { | ||||
| 			return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
| 		} | ||||
| 		NewKey.lastUse = 0; | ||||
|  | ||||
| 		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()) { | ||||
| 			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()) { | ||||
| 				return BadRequest(RESTAPI::Errors::ApiKeyNameAlreadyExists); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		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.created = Utils::Now(); | ||||
|  | ||||
| 		if (DB_.CreateRecord(NewKey)) { | ||||
| 			Poco::JSON::Object Answer; | ||||
| 			NewKey.to_json(Answer); | ||||
| 			return ReturnObject(Answer); | ||||
| 		} | ||||
| 		return BadRequest(RESTAPI::Errors::RecordNotCreated); | ||||
| 	} | ||||
|  | ||||
| 	void RESTAPI_apiKey_handler::DoPut() { | ||||
| 		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) { | ||||
| 			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
| 		} | ||||
| 		SecurityObjects::ApiKeyEntry NewKey; | ||||
| 		if (!NewKey.from_json(ParsedBody_)) { | ||||
| 			return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
| 		} | ||||
|  | ||||
| 		SecurityObjects::ApiKeyEntry ExistingKey; | ||||
| 		if (!DB_.GetRecord("id", NewKey.id, ExistingKey)) { | ||||
| 			return BadRequest(RESTAPI::Errors::ApiKeyDoesNotExist); | ||||
| 		} | ||||
|  | ||||
| 		if (ExistingKey.userUuid != user_uuid) { | ||||
| 			return BadRequest(RESTAPI::Errors::MissingUserID); | ||||
| 		} | ||||
|  | ||||
| 		AssignIfPresent(ParsedBody_, "description", ExistingKey.description); | ||||
|  | ||||
| 		if (DB_.UpdateRecord("id", ExistingKey.id, ExistingKey)) { | ||||
| 			Poco::JSON::Object Answer; | ||||
| 			ExistingKey.to_json(Answer); | ||||
| 			return ReturnObject(Answer); | ||||
| 		} | ||||
| 		BadRequest(RESTAPI::Errors::RecordNotUpdated); | ||||
| 	} | ||||
|  | ||||
| } // namespace OpenWifi | ||||
							
								
								
									
										32
									
								
								src/RESTAPI/RESTAPI_apiKey_handler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,32 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-11-04. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #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) | ||||
| 			: RESTAPIHandler(bindings, L, | ||||
| 							 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) {} | ||||
| 		static auto PathName() { return std::list<std::string>{"/api/v1/apiKey/{uuid}"}; }; | ||||
|  | ||||
| 	  private: | ||||
| 		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; | ||||
| 	void RESTAPI_asset_server::DoGet() { | ||||
| 		Poco::File AssetFile; | ||||
|  | ||||
|         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()) { | ||||
|             return NotFound(); | ||||
|         } | ||||
|         SendFile(AssetFile); | ||||
|     } | ||||
| } | ||||
| 		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()) { | ||||
| 			return NotFound(); | ||||
| 		} | ||||
| 		SendFile(AssetFile); | ||||
| 	} | ||||
| } // namespace OpenWifi | ||||
| @@ -4,31 +4,29 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "../framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_asset_server : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_asset_server(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) | ||||
|                 : RESTAPIHandler(bindings, L, | ||||
|                                  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"}; }; | ||||
|         void DoGet() final; | ||||
|         void DoPost() final {}; | ||||
|         void DoDelete() final {}; | ||||
|         void DoPut() final {}; | ||||
|  | ||||
|     private: | ||||
|  | ||||
|     }; | ||||
| } | ||||
| 	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) | ||||
| 			: RESTAPIHandler(bindings, L, | ||||
| 							 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"}; | ||||
| 		}; | ||||
| 		void DoGet() final; | ||||
| 		void DoPost() final{}; | ||||
| 		void DoDelete() final{}; | ||||
| 		void DoPut() final{}; | ||||
|  | ||||
| 	  private: | ||||
| 	}; | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -5,78 +5,85 @@ | ||||
| #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 "framework/MicroService.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     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); | ||||
|             Name_ = Parameters.get(RESTAPI::Protocol::NAME, RESTAPI::Protocol::UNNAMED); | ||||
|         } | ||||
|         Poco::CountingInputStream InputStream(Stream); | ||||
|         Poco::StreamCopier::copyStream(InputStream, OutputStream_); | ||||
|         Length_ = OutputStream_.str().size(); | ||||
|     }; | ||||
| 	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); | ||||
| 			Name_ = Parameters.get(RESTAPI::Protocol::NAME, RESTAPI::Protocol::UNNAMED); | ||||
| 		} | ||||
| 		Poco::CountingInputStream InputStream(Stream); | ||||
| 		Poco::StreamCopier::copyStream(InputStream, OutputStream_); | ||||
| 		Length_ = OutputStream_.str().size(); | ||||
| 	}; | ||||
|  | ||||
|     void RESTAPI_avatar_handler::DoPost() { | ||||
|         std::string Id = UserInfo_.userinfo.id; | ||||
|         SecurityObjects::UserInfo UInfo; | ||||
| 	void RESTAPI_avatar_handler::DoPost() { | ||||
| 		std::string Id = UserInfo_.userinfo.id; | ||||
| 		SecurityObjects::UserInfo UInfo; | ||||
|  | ||||
|         std::stringstream SS; | ||||
|         AvatarPartHandler partHandler(Id, Logger_, SS); | ||||
|         Poco::Net::HTMLForm form(*Request, Request->stream(), partHandler); | ||||
|         Poco::JSON::Object Answer; | ||||
| 		std::stringstream SS; | ||||
| 		AvatarPartHandler partHandler(Id, Logger_, SS); | ||||
| 		Poco::Net::HTMLForm form(*Request, Request->stream(), partHandler); | ||||
| 		Poco::JSON::Object Answer; | ||||
|  | ||||
|         if (!partHandler.Name().empty() && partHandler.Length()< MicroService::instance().ConfigGetInt("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)); | ||||
|         } else { | ||||
|             Answer.set(RESTAPI::Protocol::AVATARID, Id); | ||||
|             Answer.set(RESTAPI::Protocol::ERRORCODE, 13); | ||||
|             Answer.set(RESTAPI::Protocol::ERRORTEXT, "Avatar upload could not complete."); | ||||
|         } | ||||
|         ReturnObject(Answer); | ||||
|     } | ||||
| 		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)); | ||||
| 		} else { | ||||
| 			Answer.set(RESTAPI::Protocol::AVATARID, Id); | ||||
| 			Answer.set(RESTAPI::Protocol::ERRORCODE, 13); | ||||
| 			Answer.set(RESTAPI::Protocol::ERRORTEXT, "Avatar upload could not complete."); | ||||
| 		} | ||||
| 		ReturnObject(Answer); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_avatar_handler::DoGet() { | ||||
|         std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); | ||||
|         if (Id.empty()) { | ||||
|             return NotFound(); | ||||
|         } | ||||
| 	void RESTAPI_avatar_handler::DoGet() { | ||||
| 		std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); | ||||
| 		if (Id.empty()) { | ||||
| 			return NotFound(); | ||||
| 		} | ||||
|  | ||||
|         std::string Type, Name, AvatarContent; | ||||
|         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())); | ||||
|         return SendFileContent(AvatarContent, Type, Name); | ||||
|     } | ||||
| 		std::string Type, Name, AvatarContent; | ||||
| 		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())); | ||||
| 		return SendFileContent(AvatarContent, Type, Name); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_avatar_handler::DoDelete() { | ||||
|         std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); | ||||
| 	void RESTAPI_avatar_handler::DoDelete() { | ||||
| 		std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); | ||||
|  | ||||
|         if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && Id!=UserInfo_.userinfo.id) { | ||||
|             return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
|         } | ||||
| 		if (UserInfo_.userinfo.userRole != SecurityObjects::ROOT && Id != UserInfo_.userinfo.id) { | ||||
| 			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
| 		} | ||||
|  | ||||
|         if (!StorageService()->AvatarDB().DeleteAvatar(UserInfo_.userinfo.email, Id)) { | ||||
|             return NotFound(); | ||||
|         } | ||||
| 		if (!StorageService()->AvatarDB().DeleteAvatar(UserInfo_.userinfo.email, Id)) { | ||||
| 			return NotFound(); | ||||
| 		} | ||||
|  | ||||
|         Logger().information(fmt::format("Deleted avatar for {}",UserInfo_.userinfo.email)); | ||||
|         StorageService()->UserDB().SetAvatar(Id,""); | ||||
|         OK(); | ||||
|     } | ||||
| } | ||||
| 		Logger().information(fmt::format("Deleted avatar for {}", UserInfo_.userinfo.email)); | ||||
| 		StorageService()->UserDB().SetAvatar(Id, ""); | ||||
| 		OK(); | ||||
| 	} | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -3,51 +3,47 @@ | ||||
| // | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.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){ | ||||
|         } | ||||
|         void handlePart(const Poco::Net::MessageHeader &Header, std::istream &Stream); | ||||
|         [[nodiscard]] uint64_t Length() const { return Length_; } | ||||
|         [[nodiscard]] std::string &Name() { return Name_; } | ||||
|         [[nodiscard]] std::string &ContentType() { return FileType_; } | ||||
| 	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) {} | ||||
| 		void handlePart(const Poco::Net::MessageHeader &Header, std::istream &Stream); | ||||
| 		[[nodiscard]] uint64_t Length() const { return Length_; } | ||||
| 		[[nodiscard]] std::string &Name() { return Name_; } | ||||
| 		[[nodiscard]] std::string &ContentType() { return FileType_; } | ||||
|  | ||||
|     private: | ||||
|         uint64_t        Length_ = 0; | ||||
|         std::string     FileType_; | ||||
|         std::string     Name_; | ||||
|         std::string     Id_; | ||||
|         Poco::Logger    &Logger_; | ||||
|         std::stringstream &OutputStream_; | ||||
| 	  private: | ||||
| 		uint64_t Length_ = 0; | ||||
| 		std::string FileType_; | ||||
| 		std::string Name_; | ||||
| 		std::string Id_; | ||||
| 		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_GenericServer &Server, uint64_t TransactionId, bool Internal) | ||||
|                 : RESTAPIHandler(bindings, L, | ||||
|                                  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) {} | ||||
|         static auto PathName() { return std::list<std::string>{"/api/v1/avatar/{id}"}; }; | ||||
| 	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) | ||||
| 			: RESTAPIHandler(bindings, L, | ||||
| 							 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) {} | ||||
| 		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 DoGet() final; | ||||
| 		void DoPost() final; | ||||
| 		void DoDelete() final; | ||||
| 		void DoPut() final{}; | ||||
| 	}; | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -8,10 +8,15 @@ | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     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::UserInfo &U) { | ||||
| 		U.currentPassword.clear(); | ||||
| 		U.lastPasswords.clear(); | ||||
| 		U.oauthType.clear(); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| 	inline void Sanitize([[maybe_unused]] const SecurityObjects::UserInfoAndPolicy &User, | ||||
| 						 SecurityObjects::ApiKeyEntry &U) { | ||||
| 		U.salt.clear(); | ||||
| 	} | ||||
| } // namespace OpenWifi | ||||
| @@ -3,36 +3,29 @@ | ||||
| // | ||||
|  | ||||
| #include "RESTAPI_email_handler.h" | ||||
|  | ||||
|  | ||||
| #include "Poco/Exception.h" | ||||
| #include "Poco/JSON/Parser.h" | ||||
|  | ||||
| #include "SMTPMailerService.h" | ||||
| #include "framework/ow_constants.h" | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     void RESTAPI_email_handler::DoPost() { | ||||
|         const auto & Obj = ParsedBody_; | ||||
|         if (Obj->has("subject") && | ||||
|             Obj->has("from") && | ||||
|             Obj->has("text") && | ||||
|             Obj->has("recipients") && | ||||
|             Obj->isArray("recipients")) { | ||||
| 	void RESTAPI_email_handler::DoPost() { | ||||
| 		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"); | ||||
|             auto Recipient = Recipients->get(0).toString(); | ||||
|             MessageAttributes Attrs; | ||||
|             Attrs[RECIPIENT_EMAIL] = Recipient; | ||||
|             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)) { | ||||
|                 return OK(); | ||||
|             } | ||||
|             return ReturnStatus(Poco::Net::HTTPResponse::HTTP_SERVICE_UNAVAILABLE); | ||||
|         } | ||||
|         BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
|     } | ||||
| } | ||||
| 			Poco::JSON::Array::Ptr Recipients = Obj->getArray("recipients"); | ||||
| 			auto Recipient = Recipients->get(0).toString(); | ||||
| 			MessageAttributes Attrs; | ||||
| 			Attrs[RECIPIENT_EMAIL] = Recipient; | ||||
| 			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)) { | ||||
| 				return OK(); | ||||
| 			} | ||||
| 			return ReturnStatus(Poco::Net::HTTPResponse::HTTP_SERVICE_UNAVAILABLE); | ||||
| 		} | ||||
| 		BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
| 	} | ||||
| } // namespace OpenWifi | ||||
| @@ -4,22 +4,22 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_email_handler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_email_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &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 {}; | ||||
|         void DoPost() final; | ||||
|         void DoDelete() final {}; | ||||
|         void DoPut() final {}; | ||||
|     }; | ||||
| } | ||||
| 	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) | ||||
| 			: 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{}; | ||||
| 		void DoPost() final; | ||||
| 		void DoDelete() final{}; | ||||
| 		void DoPut() final{}; | ||||
| 	}; | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -9,170 +9,176 @@ | ||||
| #include "Poco/JSON/Parser.h" | ||||
|  | ||||
| #include "AuthService.h" | ||||
| #include "RESTAPI_oauth2_handler.h" | ||||
| #include "MFAServer.h" | ||||
| #include "framework/ow_constants.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "StorageService.h" | ||||
| #include "RESTAPI_db_helpers.h" | ||||
| #include "RESTAPI_oauth2_handler.h" | ||||
| #include "StorageService.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/ow_constants.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     void RESTAPI_oauth2_handler::DoGet() { | ||||
|         bool Expired = false, Contacted = false; | ||||
|         if (!IsAuthorized(Expired, Contacted)) { | ||||
|             if (Expired) | ||||
|                 return UnAuthorized(RESTAPI::Errors::EXPIRED_TOKEN); | ||||
|             return UnAuthorized(RESTAPI::Errors::INVALID_TOKEN); | ||||
|         } | ||||
|         if (GetBoolParameter(RESTAPI::Protocol::ME)) { | ||||
|             Logger_.information(fmt::format("REQUEST-ME({}): Request for {}", Request->clientAddress().toString(), | ||||
|                                             UserInfo_.userinfo.email)); | ||||
|             Poco::JSON::Object Me; | ||||
|             SecurityObjects::UserInfo ReturnedUser = UserInfo_.userinfo; | ||||
|             Sanitize(UserInfo_, ReturnedUser); | ||||
|             ReturnedUser.to_json(Me); | ||||
|             return ReturnObject(Me); | ||||
|         } | ||||
|         BadRequest(RESTAPI::Errors::UnrecognizedRequest); | ||||
|     } | ||||
| 	void RESTAPI_oauth2_handler::DoGet() { | ||||
| 		bool Expired = false, Contacted = false; | ||||
| 		if (!IsAuthorized(Expired, Contacted)) { | ||||
| 			if (Expired) | ||||
| 				return UnAuthorized(RESTAPI::Errors::EXPIRED_TOKEN); | ||||
| 			return UnAuthorized(RESTAPI::Errors::INVALID_TOKEN); | ||||
| 		} | ||||
| 		if (GetBoolParameter(RESTAPI::Protocol::ME)) { | ||||
| 			Logger_.information(fmt::format("REQUEST-ME({}): Request for {}", | ||||
| 											Request->clientAddress().toString(), | ||||
| 											UserInfo_.userinfo.email)); | ||||
| 			Poco::JSON::Object Me; | ||||
| 			SecurityObjects::UserInfo ReturnedUser = UserInfo_.userinfo; | ||||
| 			Sanitize(UserInfo_, ReturnedUser); | ||||
| 			ReturnedUser.to_json(Me); | ||||
| 			return ReturnObject(Me); | ||||
| 		} | ||||
| 		BadRequest(RESTAPI::Errors::UnrecognizedRequest); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_oauth2_handler::DoDelete() { | ||||
|         auto Token = GetBinding(RESTAPI::Protocol::TOKEN, ""); | ||||
|         std::string SessionToken; | ||||
|         try { | ||||
|             Poco::Net::OAuth20Credentials Auth(*Request); | ||||
|             if (Auth.getScheme() == "Bearer") { | ||||
|                 SessionToken = Auth.getBearerToken(); | ||||
|             } | ||||
|         } catch (const Poco::Exception &E) { | ||||
|             return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
|         } | ||||
|         if (Token.empty() || (Token != SessionToken)) { | ||||
|             return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
|         } | ||||
| 	void RESTAPI_oauth2_handler::DoDelete() { | ||||
| 		auto Token = GetBinding(RESTAPI::Protocol::TOKEN, ""); | ||||
| 		std::string SessionToken; | ||||
| 		try { | ||||
| 			Poco::Net::OAuth20Credentials Auth(*Request); | ||||
| 			if (Auth.getScheme() == "Bearer") { | ||||
| 				SessionToken = Auth.getBearerToken(); | ||||
| 			} | ||||
| 		} catch (const Poco::Exception &E) { | ||||
| 			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
| 		} | ||||
| 		if (Token.empty() || (Token != SessionToken)) { | ||||
| 			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
| 		} | ||||
|  | ||||
|         AuthService()->Logout(Token); | ||||
|         return ReturnStatus(Poco::Net::HTTPResponse::HTTP_NO_CONTENT, true); | ||||
|     } | ||||
| 		AuthService()->Logout(Token); | ||||
| 		return ReturnStatus(Poco::Net::HTTPResponse::HTTP_NO_CONTENT, true); | ||||
| 	} | ||||
|  | ||||
| 	void RESTAPI_oauth2_handler::DoPost() { | ||||
|  | ||||
|         const auto & Obj = ParsedBody_; | ||||
|         if(Obj == nullptr) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
|         } | ||||
| 		const auto &Obj = ParsedBody_; | ||||
| 		if (Obj == nullptr) { | ||||
| 			return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
| 		} | ||||
|  | ||||
|         auto userId = GetS(RESTAPI::Protocol::USERID, Obj); | ||||
|         auto password = GetS(RESTAPI::Protocol::PASSWORD, Obj); | ||||
|         auto newPassword = GetS(RESTAPI::Protocol::NEWPASSWORD, Obj); | ||||
|         auto refreshToken = GetS("refreshToken", Obj); | ||||
|         auto grant_type = GetParameter("grant_type"); | ||||
| 		auto userId = GetS(RESTAPI::Protocol::USERID, Obj); | ||||
| 		auto password = GetS(RESTAPI::Protocol::PASSWORD, Obj); | ||||
| 		auto newPassword = GetS(RESTAPI::Protocol::NEWPASSWORD, Obj); | ||||
| 		auto refreshToken = GetS("refreshToken", Obj); | ||||
| 		auto grant_type = GetParameter("grant_type"); | ||||
|  | ||||
|         Poco::toLowerInPlace(userId); | ||||
| 		Poco::toLowerInPlace(userId); | ||||
|  | ||||
|         if(!refreshToken.empty() && grant_type == "refresh_token") { | ||||
|             SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|             if(AuthService()->RefreshUserToken(*Request, refreshToken, UInfo)) { | ||||
|                 Poco::JSON::Object  Answer; | ||||
|                 UInfo.webtoken.to_json(Answer); | ||||
|                 return ReturnObject(Answer); | ||||
|             } else { | ||||
|                 return UnAuthorized(RESTAPI::Errors::CANNOT_REFRESH_TOKEN); | ||||
|             } | ||||
|         } | ||||
| 		if (!refreshToken.empty() && grant_type == "refresh_token") { | ||||
| 			SecurityObjects::UserInfoAndPolicy UInfo; | ||||
| 			if (AuthService()->RefreshUserToken(*Request, refreshToken, UInfo)) { | ||||
| 				Poco::JSON::Object Answer; | ||||
| 				UInfo.webtoken.to_json(Answer); | ||||
| 				return ReturnObject(Answer); | ||||
| 			} else { | ||||
| 				return UnAuthorized(RESTAPI::Errors::CANNOT_REFRESH_TOKEN); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|         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::ACCESSPOLICY, AuthService()->GetAccessPolicy()); | ||||
|             Answer.set(RESTAPI::Protocol::PASSWORDPOLICY, AuthService()->GetPasswordPolicy()); | ||||
|             return ReturnObject(Answer); | ||||
|         } | ||||
| 		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::ACCESSPOLICY, AuthService()->GetAccessPolicy()); | ||||
| 			Answer.set(RESTAPI::Protocol::PASSWORDPOLICY, AuthService()->GetPasswordPolicy()); | ||||
| 			return ReturnObject(Answer); | ||||
| 		} | ||||
|  | ||||
|         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)); | ||||
|                 SecurityObjects::ActionLink NewLink; | ||||
| 		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)); | ||||
| 				SecurityObjects::ActionLink NewLink; | ||||
|  | ||||
|                 NewLink.action = OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD; | ||||
|                 NewLink.id = MicroService::CreateUUID(); | ||||
|                 NewLink.userId = UInfo1.id; | ||||
|                 NewLink.created = OpenWifi::Now(); | ||||
|                 NewLink.expires = NewLink.created + (24*60*60); | ||||
|                 NewLink.userAction = true; | ||||
|                 StorageService()->ActionLinksDB().CreateAction(NewLink); | ||||
| 				NewLink.action = OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD; | ||||
| 				NewLink.id = MicroService::CreateUUID(); | ||||
| 				NewLink.userId = UInfo1.id; | ||||
| 				NewLink.created = OpenWifi::Now(); | ||||
| 				NewLink.expires = NewLink.created + (24 * 60 * 60); | ||||
| 				NewLink.userAction = true; | ||||
| 				StorageService()->ActionLinksDB().CreateAction(NewLink); | ||||
|  | ||||
|                 Poco::JSON::Object ReturnObj; | ||||
|                 SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|                 UInfo.webtoken.userMustChangePassword = true; | ||||
|                 UInfo.webtoken.to_json(ReturnObj); | ||||
|                 return ReturnObject(ReturnObj); | ||||
|             } else { | ||||
|                 Poco::JSON::Object ReturnObj; | ||||
|                 SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|                 UInfo.webtoken.userMustChangePassword = true; | ||||
|                 UInfo.webtoken.to_json(ReturnObj); | ||||
|                 return ReturnObject(ReturnObj); | ||||
|             } | ||||
|         } | ||||
| 				Poco::JSON::Object ReturnObj; | ||||
| 				SecurityObjects::UserInfoAndPolicy UInfo; | ||||
| 				UInfo.webtoken.userMustChangePassword = true; | ||||
| 				UInfo.webtoken.to_json(ReturnObj); | ||||
| 				return ReturnObject(ReturnObj); | ||||
| 			} else { | ||||
| 				Poco::JSON::Object ReturnObj; | ||||
| 				SecurityObjects::UserInfoAndPolicy UInfo; | ||||
| 				UInfo.webtoken.userMustChangePassword = true; | ||||
| 				UInfo.webtoken.to_json(ReturnObj); | ||||
| 				return ReturnObject(ReturnObj); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|         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)) | ||||
|                     return OK(); | ||||
|             } | ||||
|             return UnAuthorized(RESTAPI::Errors::BAD_MFA_TRANSACTION); | ||||
|         } | ||||
| 		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)) | ||||
| 					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")) { | ||||
|                 SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|                 if(MFAServer()->CompleteMFAChallenge(Obj,UInfo)) { | ||||
|                     Poco::JSON::Object ReturnObj; | ||||
|                     UInfo.webtoken.to_json(ReturnObj); | ||||
|                     return ReturnObject(ReturnObj); | ||||
|                 } | ||||
|             } | ||||
|             return UnAuthorized(RESTAPI::Errors::MFA_FAILURE); | ||||
|         } | ||||
| 		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)) { | ||||
| 					Poco::JSON::Object ReturnObj; | ||||
| 					UInfo.webtoken.to_json(ReturnObj); | ||||
| 					return ReturnObject(ReturnObj); | ||||
| 				} | ||||
| 			} | ||||
| 			return UnAuthorized(RESTAPI::Errors::MFA_FAILURE); | ||||
| 		} | ||||
|  | ||||
|         SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|         bool Expired=false; | ||||
|         auto Code=AuthService()->Authorize(userId, password, newPassword, UInfo, Expired); | ||||
|         if (Code==SUCCESS) { | ||||
|             Poco::JSON::Object 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."); | ||||
|             } | ||||
|             UInfo.webtoken.to_json(ReturnObj); | ||||
|             return ReturnObject(ReturnObj); | ||||
|         } else { | ||||
|  | ||||
|             switch(Code) { | ||||
|                 case INVALID_CREDENTIALS: | ||||
|                     return UnAuthorized(RESTAPI::Errors::INVALID_CREDENTIALS); | ||||
|                 case PASSWORD_INVALID: | ||||
|                     return UnAuthorized(RESTAPI::Errors::PASSWORD_INVALID); | ||||
|                 case PASSWORD_ALREADY_USED: | ||||
|                     return UnAuthorized(RESTAPI::Errors::PASSWORD_ALREADY_USED); | ||||
|                 case USERNAME_PENDING_VERIFICATION: | ||||
|                     return UnAuthorized(RESTAPI::Errors::USERNAME_PENDING_VERIFICATION); | ||||
|                 case PASSWORD_CHANGE_REQUIRED: | ||||
|                     return UnAuthorized(RESTAPI::Errors::PASSWORD_CHANGE_REQUIRED); | ||||
|                 default: | ||||
|                     return UnAuthorized(RESTAPI::Errors::INVALID_CREDENTIALS); | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
| 		SecurityObjects::UserInfoAndPolicy UInfo; | ||||
| 		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)) { | ||||
| 					return ReturnObject(ReturnObj); | ||||
| 				} | ||||
| 				Logger_.warning( | ||||
| 					"MFA Seems to be broken. Please fix. Disabling MFA checking for now."); | ||||
| 			} | ||||
| 			UInfo.webtoken.to_json(ReturnObj); | ||||
| 			return ReturnObject(ReturnObj); | ||||
| 		} | ||||
| 		case INVALID_CREDENTIALS: | ||||
| 			return UnAuthorized(RESTAPI::Errors::INVALID_CREDENTIALS); | ||||
| 		case PASSWORD_INVALID: | ||||
| 			return UnAuthorized(RESTAPI::Errors::PASSWORD_INVALID); | ||||
| 		case PASSWORD_ALREADY_USED: | ||||
| 			return UnAuthorized(RESTAPI::Errors::PASSWORD_ALREADY_USED); | ||||
| 		case USERNAME_PENDING_VERIFICATION: | ||||
| 			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); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| } // namespace OpenWifi | ||||
| @@ -7,26 +7,27 @@ | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| 	class RESTAPI_oauth2_handler : public RESTAPIHandler { | ||||
| 	  public: | ||||
| 	    RESTAPI_oauth2_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &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_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 | ||||
|   | ||||
| @@ -7,30 +7,30 @@ | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     void RESTAPI_preferences::DoGet() { | ||||
|         SecurityObjects::Preferences    P; | ||||
|         Poco::JSON::Object  Answer; | ||||
|         StorageService()->PreferencesDB().GetPreferences(UserInfo_.userinfo.id, P); | ||||
|         P.to_json(Answer); | ||||
|         ReturnObject(Answer); | ||||
|     } | ||||
| 	void RESTAPI_preferences::DoGet() { | ||||
| 		SecurityObjects::Preferences P; | ||||
| 		Poco::JSON::Object Answer; | ||||
| 		StorageService()->PreferencesDB().GetPreferences(UserInfo_.userinfo.id, P); | ||||
| 		P.to_json(Answer); | ||||
| 		ReturnObject(Answer); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_preferences::DoPut() { | ||||
| 	void RESTAPI_preferences::DoPut() { | ||||
|  | ||||
|         SecurityObjects::Preferences    P; | ||||
| 		SecurityObjects::Preferences P; | ||||
|  | ||||
|         const auto & RawObject = ParsedBody_; | ||||
|         if(!P.from_json(RawObject)) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
|         } | ||||
| 		const auto &RawObject = ParsedBody_; | ||||
| 		if (!P.from_json(RawObject)) { | ||||
| 			return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
| 		} | ||||
|  | ||||
|         P.id = UserInfo_.userinfo.id; | ||||
|         P.modified = OpenWifi::Now(); | ||||
|         StorageService()->PreferencesDB().SetPreferences(P); | ||||
| 		P.id = UserInfo_.userinfo.id; | ||||
| 		P.modified = OpenWifi::Now(); | ||||
| 		StorageService()->PreferencesDB().SetPreferences(P); | ||||
|  | ||||
|         Poco::JSON::Object  Answer; | ||||
|         P.to_json(Answer); | ||||
|         ReturnObject(Answer); | ||||
|     } | ||||
| 		Poco::JSON::Object Answer; | ||||
| 		P.to_json(Answer); | ||||
| 		ReturnObject(Answer); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| } // namespace OpenWifi | ||||
| @@ -4,24 +4,23 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_preferences : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_preferences(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &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_OPTIONS}, | ||||
|             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 {}; | ||||
|     }; | ||||
| } | ||||
| 	class RESTAPI_preferences : public RESTAPIHandler { | ||||
| 	  public: | ||||
| 		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, | ||||
| 													  Poco::Net::HTTPRequest::HTTP_PUT, | ||||
| 													  Poco::Net::HTTPRequest::HTTP_OPTIONS}, | ||||
| 							 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{}; | ||||
| 	}; | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -2,87 +2,66 @@ | ||||
| // Created by stephane bourque on 2021-10-23. | ||||
| // | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| #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_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_GenericServer & 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 | ||||
|         >(Path, Bindings, L, S,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_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_GenericServer & 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_signup_handler | ||||
|         >(Path, Bindings, L, S, TransactionId); | ||||
|     } | ||||
| } | ||||
| 		return RESTAPI_Router_I< | ||||
| 			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,72 +3,74 @@ | ||||
| // | ||||
|  | ||||
| #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; | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     void RESTAPI_signup_handler::DoPost() { | ||||
|         auto UserName = GetParameter("email"); | ||||
|         auto signupUUID = GetParameter("signupUUID"); | ||||
|         auto owner = GetParameter("owner"); | ||||
|         auto operatorName = GetParameter("operatorName"); | ||||
|         if(UserName.empty() || signupUUID.empty() || owner.empty() || operatorName.empty()) { | ||||
|             Logger().error("Signup requires: email, signupUUID, operatorName, and owner."); | ||||
|             return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
|         } | ||||
| 	void RESTAPI_signup_handler::DoPost() { | ||||
| 		auto UserName = GetParameter("email"); | ||||
| 		auto signupUUID = GetParameter("signupUUID"); | ||||
| 		auto owner = GetParameter("owner"); | ||||
| 		auto operatorName = GetParameter("operatorName"); | ||||
| 		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)) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidEmailAddress); | ||||
|         } | ||||
| 		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()) { | ||||
|                 return BadRequest(RESTAPI::Errors::SignupAlreadySigned); | ||||
|             } | ||||
| 		// Do we already exist? Can only signup once... | ||||
| 		SecurityObjects::UserInfo Existing; | ||||
| 		if (StorageService()->SubDB().GetUserByEmail(UserName, Existing)) { | ||||
| 			if (Existing.signingUp.empty()) { | ||||
| 				return BadRequest(RESTAPI::Errors::SignupAlreadySigned); | ||||
| 			} | ||||
|  | ||||
|             if(Existing.waitingForEmailCheck) { | ||||
|                 return BadRequest(RESTAPI::Errors::SignupEmailCheck); | ||||
|             } | ||||
| 			if (Existing.waitingForEmailCheck) { | ||||
| 				return BadRequest(RESTAPI::Errors::SignupEmailCheck); | ||||
| 			} | ||||
|  | ||||
|             return BadRequest(RESTAPI::Errors::SignupWaitingForDevice); | ||||
|         } | ||||
| 			return BadRequest(RESTAPI::Errors::SignupWaitingForDevice); | ||||
| 		} | ||||
|  | ||||
|         SecurityObjects::UserInfo   NewSub; | ||||
|         NewSub.signingUp = operatorName + ":" + signupUUID; | ||||
|         NewSub.waitingForEmailCheck = true; | ||||
|         NewSub.name = UserName; | ||||
|         NewSub.modified = OpenWifi::Now(); | ||||
|         NewSub.creationDate = OpenWifi::Now(); | ||||
|         NewSub.id = MicroService::instance().CreateUUID(); | ||||
|         NewSub.email = UserName; | ||||
|         NewSub.userRole = SecurityObjects::SUBSCRIBER; | ||||
|         NewSub.changePassword = true; | ||||
|         NewSub.owner = owner; | ||||
| 		SecurityObjects::UserInfo NewSub; | ||||
| 		NewSub.signingUp = operatorName + ":" + signupUUID; | ||||
| 		NewSub.waitingForEmailCheck = true; | ||||
| 		NewSub.name = UserName; | ||||
| 		NewSub.modified = OpenWifi::Now(); | ||||
| 		NewSub.creationDate = OpenWifi::Now(); | ||||
| 		NewSub.id = MicroServiceCreateUUID(); | ||||
| 		NewSub.email = UserName; | ||||
| 		NewSub.userRole = SecurityObjects::SUBSCRIBER; | ||||
| 		NewSub.changePassword = true; | ||||
| 		NewSub.owner = owner; | ||||
|  | ||||
|         StorageService()->SubDB().CreateRecord(NewSub); | ||||
| 		StorageService()->SubDB().CreateRecord(NewSub); | ||||
|  | ||||
|         Logger_.information(fmt::format("SIGNUP-PASSWORD({}): Request for {}", Request->clientAddress().toString(), UserName)); | ||||
|         SecurityObjects::ActionLink NewLink; | ||||
| 		Logger_.information(fmt::format("SIGNUP-PASSWORD({}): Request for {}", | ||||
| 										Request->clientAddress().toString(), UserName)); | ||||
| 		SecurityObjects::ActionLink NewLink; | ||||
|  | ||||
|         NewLink.action = OpenWifi::SecurityObjects::LinkActions::SUB_SIGNUP; | ||||
|         NewLink.id = MicroService::CreateUUID(); | ||||
|         NewLink.userId = NewSub.id; | ||||
|         NewLink.created = OpenWifi::Now(); | ||||
|         NewLink.expires = NewLink.created + (1*60*60);  // 1 hour | ||||
|         NewLink.userAction = false; | ||||
|         StorageService()->ActionLinksDB().CreateAction(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.userAction = false; | ||||
| 		StorageService()->ActionLinksDB().CreateAction(NewLink); | ||||
|  | ||||
|         Poco::JSON::Object  Answer; | ||||
|         NewSub.to_json(Answer); | ||||
|         return ReturnObject(Answer); | ||||
|     } | ||||
| 		Poco::JSON::Object Answer; | ||||
| 		NewSub.to_json(Answer); | ||||
| 		return ReturnObject(Answer); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_signup_handler::DoPut() { | ||||
|         // TODO | ||||
|     } | ||||
| 	void RESTAPI_signup_handler::DoPut() { | ||||
| 		// TODO | ||||
| 	} | ||||
|  | ||||
| } | ||||
| } // namespace OpenWifi | ||||
| @@ -4,36 +4,35 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_signup_handler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_signup_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) | ||||
|                 : RESTAPIHandler(bindings, L, | ||||
|                                  std::vector<std::string>{ | ||||
|                                          Poco::Net::HTTPRequest::HTTP_POST, | ||||
|                                          Poco::Net::HTTPRequest::HTTP_OPTIONS, | ||||
|                                          Poco::Net::HTTPRequest::HTTP_PUT}, | ||||
|                                  Server, | ||||
|                                  TransactionId, | ||||
|                                  Internal, false, true ){} | ||||
| 	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) | ||||
| 			: RESTAPIHandler(bindings, L, | ||||
| 							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST, | ||||
| 													  Poco::Net::HTTPRequest::HTTP_OPTIONS, | ||||
| 													  Poco::Net::HTTPRequest::HTTP_PUT}, | ||||
| 							 Server, TransactionId, Internal, false, true) {} | ||||
|  | ||||
|         static auto PathName() { return std::list<std::string>{"/api/v1/signup"}; }; | ||||
| 		static auto PathName() { return std::list<std::string>{"/api/v1/signup"}; }; | ||||
|  | ||||
| /*        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 DoPost() final; | ||||
|         void DoPut() final ; | ||||
|         void DoDelete() final {}; | ||||
|     private: | ||||
| 		/*        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 DoPost() final; | ||||
| 		void DoPut() final; | ||||
| 		void DoDelete() final{}; | ||||
|  | ||||
|     }; | ||||
| } | ||||
| 	  private: | ||||
| 	}; | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -5,55 +5,53 @@ | ||||
| #include "RESTAPI_sms_handler.h" | ||||
| #include "SMSSender.h" | ||||
| #include "framework/ow_constants.h" | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     void OpenWifi::RESTAPI_sms_handler::DoPost() { | ||||
|         const auto &Obj = ParsedBody_; | ||||
| 	void OpenWifi::RESTAPI_sms_handler::DoPost() { | ||||
| 		const auto &Obj = ParsedBody_; | ||||
|  | ||||
|         if(!SMSSender()->Enabled()) { | ||||
|             return BadRequest(RESTAPI::Errors::SMSMFANotEnabled); | ||||
|         } | ||||
| 		if (!SMSSender()->Enabled()) { | ||||
| 			return BadRequest(RESTAPI::Errors::SMSMFANotEnabled); | ||||
| 		} | ||||
|  | ||||
|         std::string Arg; | ||||
|         if(HasParameter("validateNumber",Arg) && Arg=="true" && Obj->has("to")) { | ||||
|             auto Number = Obj->get("to").toString(); | ||||
|             if(SMSSender()->StartValidation(Number, UserInfo_.userinfo.email)) { | ||||
|                 return OK(); | ||||
|             } | ||||
|             return BadRequest(RESTAPI::Errors::SMSCouldNotBeSentRetry); | ||||
|         } | ||||
| 		std::string Arg; | ||||
| 		if (HasParameter("validateNumber", Arg) && Arg == "true" && Obj->has("to")) { | ||||
| 			auto Number = Obj->get("to").toString(); | ||||
| 			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")) { | ||||
|             auto Number = Obj->get("to").toString(); | ||||
|             if(SMSSender()->CompleteValidation(Number, Code, UserInfo_.userinfo.email)) { | ||||
|                 return OK(); | ||||
|             } | ||||
|             return BadRequest(RESTAPI::Errors::SMSCouldNotValidate); | ||||
|         } | ||||
| 		std::string Code; | ||||
| 		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)) { | ||||
| 				return OK(); | ||||
| 			} | ||||
| 			return BadRequest(RESTAPI::Errors::SMSCouldNotValidate); | ||||
| 		} | ||||
|  | ||||
|         if( UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && | ||||
|             UserInfo_.userinfo.userRole!=SecurityObjects::PARTNER && | ||||
|             UserInfo_.userinfo.userRole!=SecurityObjects::ADMIN) { | ||||
|             return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
|         } | ||||
|         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)) | ||||
|                 return OK(); | ||||
| 			std::string PhoneNumber = Obj->get("to").toString(); | ||||
| 			std::string Text = Obj->get("text").toString(); | ||||
| 			if (SMSSender()->Send(PhoneNumber, Text)) | ||||
| 				return OK(); | ||||
|  | ||||
|             return InternalError(RESTAPI::Errors::SMSCouldNotBeSentRetry); | ||||
|         } | ||||
|         BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
|     } | ||||
| 			return InternalError(RESTAPI::Errors::SMSCouldNotBeSentRetry); | ||||
| 		} | ||||
| 		BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| } // namespace OpenWifi | ||||
| @@ -4,22 +4,22 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_sms_handler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_sms_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &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 {}; | ||||
|         void DoPost() final; | ||||
|         void DoDelete() final {}; | ||||
|         void DoPut() final {}; | ||||
|     }; | ||||
| } | ||||
| 	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) | ||||
| 			: 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{}; | ||||
| 		void DoPost() final; | ||||
| 		void DoDelete() final{}; | ||||
| 		void DoPut() final{}; | ||||
| 	}; | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -5,77 +5,84 @@ | ||||
| #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 "framework/MicroService.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     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); | ||||
|             Name_ = Parameters.get(RESTAPI::Protocol::NAME, RESTAPI::Protocol::UNNAMED); | ||||
|         } | ||||
|         Poco::CountingInputStream InputStream(Stream); | ||||
|         Poco::StreamCopier::copyStream(InputStream, OutputStream_); | ||||
|         Length_ = OutputStream_.str().size(); | ||||
|     }; | ||||
| 	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); | ||||
| 			Name_ = Parameters.get(RESTAPI::Protocol::NAME, RESTAPI::Protocol::UNNAMED); | ||||
| 		} | ||||
| 		Poco::CountingInputStream InputStream(Stream); | ||||
| 		Poco::StreamCopier::copyStream(InputStream, OutputStream_); | ||||
| 		Length_ = OutputStream_.str().size(); | ||||
| 	}; | ||||
|  | ||||
|     void RESTAPI_subavatar_handler::DoPost() { | ||||
|         std::string Id = UserInfo_.userinfo.id; | ||||
|         SecurityObjects::UserInfo UInfo; | ||||
| 	void RESTAPI_subavatar_handler::DoPost() { | ||||
| 		std::string Id = UserInfo_.userinfo.id; | ||||
| 		SecurityObjects::UserInfo UInfo; | ||||
|  | ||||
|         std::stringstream SS; | ||||
|         SubAvatarPartHandler partHandler(Id, Logger_, SS); | ||||
|         Poco::Net::HTMLForm form(*Request, Request->stream(), partHandler); | ||||
|         Poco::JSON::Object Answer; | ||||
| 		std::stringstream SS; | ||||
| 		SubAvatarPartHandler partHandler(Id, Logger_, SS); | ||||
| 		Poco::Net::HTMLForm form(*Request, Request->stream(), partHandler); | ||||
| 		Poco::JSON::Object Answer; | ||||
|  | ||||
|         if (!partHandler.Name().empty() && partHandler.Length()< MicroService::instance().ConfigGetInt("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)); | ||||
|         } else { | ||||
|             Answer.set(RESTAPI::Protocol::AVATARID, Id); | ||||
|             Answer.set(RESTAPI::Protocol::ERRORCODE, 13); | ||||
|             Answer.set(RESTAPI::Protocol::ERRORTEXT, "Avatar upload could not complete."); | ||||
|         } | ||||
|         ReturnObject(Answer); | ||||
|     } | ||||
| 		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)); | ||||
| 		} else { | ||||
| 			Answer.set(RESTAPI::Protocol::AVATARID, Id); | ||||
| 			Answer.set(RESTAPI::Protocol::ERRORCODE, 13); | ||||
| 			Answer.set(RESTAPI::Protocol::ERRORTEXT, "Avatar upload could not complete."); | ||||
| 		} | ||||
| 		ReturnObject(Answer); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_subavatar_handler::DoGet() { | ||||
|         std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); | ||||
|         if (Id.empty()) { | ||||
|             return NotFound(); | ||||
|         } | ||||
| 	void RESTAPI_subavatar_handler::DoGet() { | ||||
| 		std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); | ||||
| 		if (Id.empty()) { | ||||
| 			return NotFound(); | ||||
| 		} | ||||
|  | ||||
|         std::string Type, Name, AvatarContent; | ||||
|         if (!StorageService()->SubAvatarDB().GetAvatar(UserInfo_.userinfo.email, Id, AvatarContent, Type, Name)) { | ||||
|             return NotFound(); | ||||
|         } | ||||
|         Logger().information(fmt::format("Retrieving avatar for {}",UserInfo_.userinfo.email)); | ||||
|         return SendFileContent(AvatarContent, Type, Name); | ||||
|     } | ||||
| 		std::string Type, Name, AvatarContent; | ||||
| 		if (!StorageService()->SubAvatarDB().GetAvatar(UserInfo_.userinfo.email, Id, AvatarContent, | ||||
| 													   Type, Name)) { | ||||
| 			return NotFound(); | ||||
| 		} | ||||
| 		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, ""); | ||||
| 	void RESTAPI_subavatar_handler::DoDelete() { | ||||
| 		std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); | ||||
|  | ||||
|         if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && Id!=UserInfo_.userinfo.id) { | ||||
|             return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
|         } | ||||
| 		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,""); | ||||
|         OK(); | ||||
|     } | ||||
| } | ||||
| 		if (!StorageService()->SubAvatarDB().DeleteAvatar(UserInfo_.userinfo.email, Id)) { | ||||
| 			return NotFound(); | ||||
| 		} | ||||
| 		Logger().information(fmt::format("Deleted avatar for {}", UserInfo_.userinfo.email)); | ||||
| 		StorageService()->SubDB().SetAvatar(Id, ""); | ||||
| 		OK(); | ||||
| 	} | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -3,51 +3,47 @@ | ||||
| // | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.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){ | ||||
|         } | ||||
|         void handlePart(const Poco::Net::MessageHeader &Header, std::istream &Stream); | ||||
|         [[nodiscard]] uint64_t Length() const { return Length_; } | ||||
|         [[nodiscard]] std::string &Name() { return Name_; } | ||||
|         [[nodiscard]] std::string &ContentType() { return FileType_; } | ||||
| 	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) {} | ||||
| 		void handlePart(const Poco::Net::MessageHeader &Header, std::istream &Stream); | ||||
| 		[[nodiscard]] uint64_t Length() const { return Length_; } | ||||
| 		[[nodiscard]] std::string &Name() { return Name_; } | ||||
| 		[[nodiscard]] std::string &ContentType() { return FileType_; } | ||||
|  | ||||
|     private: | ||||
|         uint64_t        Length_ = 0; | ||||
|         std::string     FileType_; | ||||
|         std::string     Name_; | ||||
|         std::string     Id_; | ||||
|         Poco::Logger    &Logger_; | ||||
|         std::stringstream &OutputStream_; | ||||
| 	  private: | ||||
| 		uint64_t Length_ = 0; | ||||
| 		std::string FileType_; | ||||
| 		std::string Name_; | ||||
| 		std::string Id_; | ||||
| 		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_GenericServer &Server, uint64_t TransactionId, bool Internal) | ||||
|                 : RESTAPIHandler(bindings, L, | ||||
|                                  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) {} | ||||
|         static auto PathName() { return std::list<std::string>{"/api/v1/subavatar/{id}"}; }; | ||||
| 	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) | ||||
| 			: RESTAPIHandler(bindings, L, | ||||
| 							 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) {} | ||||
| 		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 DoGet() final; | ||||
| 		void DoPost() final; | ||||
| 		void DoDelete() final; | ||||
| 		void DoPut() final{}; | ||||
| 	}; | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -3,135 +3,140 @@ | ||||
| // | ||||
|  | ||||
| #include "RESTAPI_submfa_handler.h" | ||||
| #include "StorageService.h" | ||||
| #include "SMSSender.h" | ||||
| #include "StorageService.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     void RESTAPI_submfa_handler::DoGet() { | ||||
|         SecurityObjects::UserInfo   User; | ||||
| 	void RESTAPI_submfa_handler::DoGet() { | ||||
| 		SecurityObjects::UserInfo User; | ||||
|  | ||||
|         if (StorageService()->SubDB().GetUserById(UserInfo_.userinfo.id,User)) { | ||||
|             Poco::JSON::Object              Answer; | ||||
|             SecurityObjects::SubMfaConfig   MFC; | ||||
| 		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") { | ||||
|                     MFC.sms = User.userTypeProprietaryInfo.mobiles[0].number; | ||||
|                     MFC.type = "sms"; | ||||
|                 } else if(User.userTypeProprietaryInfo.mfa.method == "email") { | ||||
|                     MFC.email = User.email; | ||||
|                     MFC.type = "email"; | ||||
|                 } | ||||
|             } else { | ||||
|                 MFC.type = "disabled"; | ||||
|             } | ||||
|             MFC.to_json(Answer); | ||||
|             return ReturnObject(Answer); | ||||
|         } | ||||
|         NotFound(); | ||||
|     } | ||||
| 			MFC.id = User.id; | ||||
| 			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") { | ||||
| 					MFC.email = User.email; | ||||
| 					MFC.type = "email"; | ||||
| 				} | ||||
| 			} else { | ||||
| 				MFC.type = "disabled"; | ||||
| 			} | ||||
| 			MFC.to_json(Answer); | ||||
| 			return ReturnObject(Answer); | ||||
| 		} | ||||
| 		NotFound(); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_submfa_handler::DoPut() { | ||||
| 	void RESTAPI_submfa_handler::DoPut() { | ||||
|  | ||||
|         try { | ||||
|             const auto & Body = ParsedBody_; | ||||
| 		try { | ||||
| 			const auto &Body = ParsedBody_; | ||||
|  | ||||
|             SecurityObjects::SubMfaConfig MFC; | ||||
| 			SecurityObjects::SubMfaConfig MFC; | ||||
|  | ||||
|             if (!MFC.from_json(Body)) { | ||||
|                 return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
|             } | ||||
| 			if (!MFC.from_json(Body)) { | ||||
| 				return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
| 			} | ||||
|  | ||||
|             if (MFC.type == "disabled") { | ||||
|                 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); | ||||
| 			if (MFC.type == "disabled") { | ||||
| 				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); | ||||
|  | ||||
|                 Poco::JSON::Object Answer; | ||||
|                 MFC.to_json(Answer); | ||||
|                 return ReturnObject(Answer); | ||||
|             } else if (MFC.type == "email") { | ||||
|                 SecurityObjects::UserInfo User; | ||||
| 				Poco::JSON::Object Answer; | ||||
| 				MFC.to_json(Answer); | ||||
| 				return ReturnObject(Answer); | ||||
| 			} else if (MFC.type == "email") { | ||||
| 				SecurityObjects::UserInfo User; | ||||
|  | ||||
|                 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().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); | ||||
|  | ||||
|                 MFC.sms = MFC.sms; | ||||
|                 MFC.type = "email"; | ||||
|                 MFC.email = UserInfo_.userinfo.email; | ||||
|                 MFC.id = MicroService::instance().CreateUUID(); | ||||
| 				MFC.sms = MFC.sms; | ||||
| 				MFC.type = "email"; | ||||
| 				MFC.email = UserInfo_.userinfo.email; | ||||
| 				MFC.id = MicroServiceCreateUUID(); | ||||
|  | ||||
|                 Poco::JSON::Object Answer; | ||||
|                 MFC.to_json(Answer); | ||||
|                 return ReturnObject(Answer); | ||||
| 				Poco::JSON::Object Answer; | ||||
| 				MFC.to_json(Answer); | ||||
| 				return ReturnObject(Answer); | ||||
|  | ||||
|             } else if (MFC.type == "sms") { | ||||
|                 if (GetBoolParameter("startValidation", false)) { | ||||
|                     if (MFC.sms.empty()) { | ||||
|                         return BadRequest(RESTAPI::Errors::SMSMissingPhoneNumber); | ||||
|                     } | ||||
| 			} else if (MFC.type == "sms") { | ||||
| 				if (GetBoolParameter("startValidation", false)) { | ||||
| 					if (MFC.sms.empty()) { | ||||
| 						return BadRequest(RESTAPI::Errors::SMSMissingPhoneNumber); | ||||
| 					} | ||||
|  | ||||
|                     if(!SMSSender()->Enabled()) { | ||||
|                         return BadRequest(RESTAPI::Errors::SMSMFANotEnabled); | ||||
|                     } | ||||
| 					if (!SMSSender()->Enabled()) { | ||||
| 						return BadRequest(RESTAPI::Errors::SMSMFANotEnabled); | ||||
| 					} | ||||
|  | ||||
|                     if (SMSSender()->StartValidation(MFC.sms, UserInfo_.userinfo.email)) { | ||||
|                         return OK(); | ||||
|                     } else { | ||||
|                         return InternalError(RESTAPI::Errors::SMSTryLater); | ||||
|                     } | ||||
|                 } else if (GetBoolParameter("completeValidation", false)) { | ||||
| 					if (SMSSender()->StartValidation(MFC.sms, UserInfo_.userinfo.email)) { | ||||
| 						return OK(); | ||||
| 					} else { | ||||
| 						return InternalError(RESTAPI::Errors::SMSTryLater); | ||||
| 					} | ||||
| 				} else if (GetBoolParameter("completeValidation", false)) { | ||||
|  | ||||
|                     if(!SMSSender()->Enabled()) { | ||||
|                         return BadRequest(RESTAPI::Errors::SMSMFANotEnabled); | ||||
|                     } | ||||
| 					if (!SMSSender()->Enabled()) { | ||||
| 						return BadRequest(RESTAPI::Errors::SMSMFANotEnabled); | ||||
| 					} | ||||
|  | ||||
|                     auto ChallengeCode = GetParameter("challengeCode", ""); | ||||
|                     if (ChallengeCode.empty()) { | ||||
|                         return BadRequest(RESTAPI::Errors::SMSMissingChallenge); | ||||
|                     } | ||||
|                     if (MFC.sms.empty()) { | ||||
|                         return BadRequest(RESTAPI::Errors::SMSMissingPhoneNumber); | ||||
|                     } | ||||
|                     if (SMSSender()->CompleteValidation(MFC.sms, ChallengeCode, UserInfo_.userinfo.email)) { | ||||
|                         SecurityObjects::UserInfo User; | ||||
| 					auto ChallengeCode = GetParameter("challengeCode", ""); | ||||
| 					if (ChallengeCode.empty()) { | ||||
| 						return BadRequest(RESTAPI::Errors::SMSMissingChallenge); | ||||
| 					} | ||||
| 					if (MFC.sms.empty()) { | ||||
| 						return BadRequest(RESTAPI::Errors::SMSMissingPhoneNumber); | ||||
| 					} | ||||
| 					if (SMSSender()->CompleteValidation(MFC.sms, ChallengeCode, | ||||
| 														UserInfo_.userinfo.email)) { | ||||
| 						SecurityObjects::UserInfo User; | ||||
|  | ||||
|                         StorageService()->SubDB().GetUserById(UserInfo_.userinfo.id, User); | ||||
|                         User.userTypeProprietaryInfo.mfa.enabled = true; | ||||
|                         User.userTypeProprietaryInfo.mfa.method = "sms"; | ||||
|                         SecurityObjects::MobilePhoneNumber PhoneNumber; | ||||
|                         PhoneNumber.number = MFC.sms; | ||||
|                         PhoneNumber.primary = true; | ||||
|                         PhoneNumber.verified = true; | ||||
|                         User.userTypeProprietaryInfo.mobiles.clear(); | ||||
|                         User.userTypeProprietaryInfo.mobiles.push_back(PhoneNumber); | ||||
| 						StorageService()->SubDB().GetUserById(UserInfo_.userinfo.id, User); | ||||
| 						User.userTypeProprietaryInfo.mfa.enabled = true; | ||||
| 						User.userTypeProprietaryInfo.mfa.method = "sms"; | ||||
| 						SecurityObjects::MobilePhoneNumber PhoneNumber; | ||||
| 						PhoneNumber.number = MFC.sms; | ||||
| 						PhoneNumber.primary = true; | ||||
| 						PhoneNumber.verified = true; | ||||
| 						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"; | ||||
|                         MFC.email = UserInfo_.userinfo.email; | ||||
|                         MFC.id = MicroService::instance().CreateUUID(); | ||||
| 						MFC.sms = MFC.sms; | ||||
| 						MFC.type = "sms"; | ||||
| 						MFC.email = UserInfo_.userinfo.email; | ||||
| 						MFC.id = MicroServiceCreateUUID(); | ||||
|  | ||||
|                         Poco::JSON::Object Answer; | ||||
|                         MFC.to_json(Answer); | ||||
| 						Poco::JSON::Object Answer; | ||||
| 						MFC.to_json(Answer); | ||||
|  | ||||
|                         return ReturnObject(Answer); | ||||
| 						return ReturnObject(Answer); | ||||
|  | ||||
|                     } else { | ||||
|                         return InternalError(RESTAPI::Errors::SMSTryLater); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } catch (const Poco::Exception &E) { | ||||
|             Logger_.log(E); | ||||
|         } | ||||
|         return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
|     } | ||||
| 					} else { | ||||
| 						return InternalError(RESTAPI::Errors::SMSTryLater); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} catch (const Poco::Exception &E) { | ||||
| 			Logger_.log(E); | ||||
| 		} | ||||
| 		return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -4,24 +4,24 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_submfa_handler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_submfa_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &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) {} | ||||
|         static auto PathName() { return std::list<std::string>{"/api/v1/submfa"}; }; | ||||
|         void DoGet() final; | ||||
|         void DoPost() final {}; | ||||
|         void DoDelete() final {}; | ||||
|         void DoPut() final ; | ||||
|     }; | ||||
| } | ||||
| 	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) | ||||
| 			: 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) {} | ||||
| 		static auto PathName() { return std::list<std::string>{"/api/v1/submfa"}; }; | ||||
| 		void DoGet() final; | ||||
| 		void DoPost() final{}; | ||||
| 		void DoDelete() final{}; | ||||
| 		void DoPut() final; | ||||
| 	}; | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -5,161 +5,167 @@ | ||||
| #include "RESTAPI_suboauth2_handler.h" | ||||
| #include "AuthService.h" | ||||
| #include "MFAServer.h" | ||||
| #include "framework/MicroService.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) | ||||
|                 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(), | ||||
|                                              UserInfo_.userinfo.email)); | ||||
|             Poco::JSON::Object Me; | ||||
|             SecurityObjects::UserInfo   ReturnedUser = UserInfo_.userinfo; | ||||
|             Sanitize(UserInfo_, ReturnedUser); | ||||
|             ReturnedUser.to_json(Me); | ||||
|             return ReturnObject(Me); | ||||
|         } | ||||
|         BadRequest(RESTAPI::Errors::UnrecognizedRequest); | ||||
|     } | ||||
| 	void RESTAPI_suboauth2_handler::DoGet() { | ||||
| 		bool Expired = false, Contacted = false; | ||||
| 		if (!IsAuthorized(Expired, Contacted, true)) { | ||||
| 			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(), | ||||
| 											UserInfo_.userinfo.email)); | ||||
| 			Poco::JSON::Object Me; | ||||
| 			SecurityObjects::UserInfo ReturnedUser = UserInfo_.userinfo; | ||||
| 			Sanitize(UserInfo_, ReturnedUser); | ||||
| 			ReturnedUser.to_json(Me); | ||||
| 			return ReturnObject(Me); | ||||
| 		} | ||||
| 		BadRequest(RESTAPI::Errors::UnrecognizedRequest); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_suboauth2_handler::DoDelete() { | ||||
|         auto Token = GetBinding(RESTAPI::Protocol::TOKEN, ""); | ||||
|         std::string SessionToken; | ||||
|         try { | ||||
|             Poco::Net::OAuth20Credentials Auth(*Request); | ||||
|             if (Auth.getScheme() == "Bearer") { | ||||
|                 SessionToken = Auth.getBearerToken(); | ||||
|             } | ||||
|         } catch (const Poco::Exception &E) { | ||||
|             return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
|         } | ||||
|         if (Token.empty() || (Token != SessionToken)) { | ||||
|             return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
|         } | ||||
|         AuthService()->SubLogout(Token); | ||||
|         return ReturnStatus(Poco::Net::HTTPResponse::HTTP_NO_CONTENT, true); | ||||
|     } | ||||
| 	void RESTAPI_suboauth2_handler::DoDelete() { | ||||
| 		auto Token = GetBinding(RESTAPI::Protocol::TOKEN, ""); | ||||
| 		std::string SessionToken; | ||||
| 		try { | ||||
| 			Poco::Net::OAuth20Credentials Auth(*Request); | ||||
| 			if (Auth.getScheme() == "Bearer") { | ||||
| 				SessionToken = Auth.getBearerToken(); | ||||
| 			} | ||||
| 		} catch (const Poco::Exception &E) { | ||||
| 			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
| 		} | ||||
| 		if (Token.empty() || (Token != SessionToken)) { | ||||
| 			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
| 		} | ||||
| 		AuthService()->SubLogout(Token); | ||||
| 		return ReturnStatus(Poco::Net::HTTPResponse::HTTP_NO_CONTENT, true); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_suboauth2_handler::DoPost() { | ||||
|         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); | ||||
|         auto refreshToken = GetS("refreshToken", Obj); | ||||
|         auto grant_type = GetParameter("grant_type"); | ||||
| 	void RESTAPI_suboauth2_handler::DoPost() { | ||||
| 		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); | ||||
| 		auto refreshToken = GetS("refreshToken", Obj); | ||||
| 		auto grant_type = GetParameter("grant_type"); | ||||
|  | ||||
|         Poco::toLowerInPlace(userId); | ||||
| 		Poco::toLowerInPlace(userId); | ||||
|  | ||||
|         if(!refreshToken.empty() && grant_type == "refresh_token") { | ||||
|             SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|             if(AuthService()->RefreshSubToken(*Request, refreshToken, UInfo)) { | ||||
|                 Poco::JSON::Object  Answer; | ||||
|                 UInfo.webtoken.to_json(Answer); | ||||
|                 return ReturnObject(Answer); | ||||
|             } else { | ||||
|                 return UnAuthorized(RESTAPI::Errors::CANNOT_REFRESH_TOKEN); | ||||
|             } | ||||
|         } | ||||
| 		if (!refreshToken.empty() && grant_type == "refresh_token") { | ||||
| 			SecurityObjects::UserInfoAndPolicy UInfo; | ||||
| 			if (AuthService()->RefreshSubToken(*Request, refreshToken, UInfo)) { | ||||
| 				Poco::JSON::Object Answer; | ||||
| 				UInfo.webtoken.to_json(Answer); | ||||
| 				return ReturnObject(Answer); | ||||
| 			} else { | ||||
| 				return UnAuthorized(RESTAPI::Errors::CANNOT_REFRESH_TOKEN); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|         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::ACCESSPOLICY, AuthService()->GetSubAccessPolicy()); | ||||
|             Answer.set(RESTAPI::Protocol::PASSWORDPOLICY, AuthService()->GetSubPasswordPolicy()); | ||||
|             return ReturnObject(Answer); | ||||
|         } | ||||
| 		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::ACCESSPOLICY, AuthService()->GetSubAccessPolicy()); | ||||
| 			Answer.set(RESTAPI::Protocol::PASSWORDPOLICY, AuthService()->GetSubPasswordPolicy()); | ||||
| 			return ReturnObject(Answer); | ||||
| 		} | ||||
|  | ||||
|         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)); | ||||
|                 SecurityObjects::ActionLink NewLink; | ||||
| 		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)); | ||||
| 				SecurityObjects::ActionLink NewLink; | ||||
|  | ||||
|                 NewLink.action = OpenWifi::SecurityObjects::LinkActions::SUB_FORGOT_PASSWORD; | ||||
|                 NewLink.id = MicroService::CreateUUID(); | ||||
|                 NewLink.userId = UInfo1.id; | ||||
|                 NewLink.created = OpenWifi::Now(); | ||||
|                 NewLink.expires = NewLink.created + (24*60*60); | ||||
|                 NewLink.userAction = false; | ||||
|                 StorageService()->ActionLinksDB().CreateAction(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.userAction = false; | ||||
| 				StorageService()->ActionLinksDB().CreateAction(NewLink); | ||||
|  | ||||
|                 Poco::JSON::Object ReturnObj; | ||||
|                 SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|                 UInfo.webtoken.userMustChangePassword = true; | ||||
|                 UInfo.webtoken.to_json(ReturnObj); | ||||
|                 return ReturnObject(ReturnObj); | ||||
|             } else { | ||||
|                 Poco::JSON::Object ReturnObj; | ||||
|                 SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|                 UInfo.webtoken.userMustChangePassword = true; | ||||
|                 UInfo.webtoken.to_json(ReturnObj); | ||||
|                 return ReturnObject(ReturnObj); | ||||
|             } | ||||
|         } | ||||
| 				Poco::JSON::Object ReturnObj; | ||||
| 				SecurityObjects::UserInfoAndPolicy UInfo; | ||||
| 				UInfo.webtoken.userMustChangePassword = true; | ||||
| 				UInfo.webtoken.to_json(ReturnObj); | ||||
| 				return ReturnObject(ReturnObj); | ||||
| 			} else { | ||||
| 				Poco::JSON::Object ReturnObj; | ||||
| 				SecurityObjects::UserInfoAndPolicy UInfo; | ||||
| 				UInfo.webtoken.userMustChangePassword = true; | ||||
| 				UInfo.webtoken.to_json(ReturnObj); | ||||
| 				return ReturnObject(ReturnObj); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|         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)) | ||||
|                     return OK(); | ||||
|             } | ||||
|             return UnAuthorized(RESTAPI::Errors::BAD_MFA_TRANSACTION); | ||||
|         } | ||||
| 		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)) | ||||
| 					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")) { | ||||
|                 SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|                 if(MFAServer()->CompleteMFAChallenge(Obj,UInfo)) { | ||||
|                     Poco::JSON::Object ReturnObj; | ||||
|                     UInfo.webtoken.to_json(ReturnObj); | ||||
|                     return ReturnObject(ReturnObj); | ||||
|                 } | ||||
|             } | ||||
|             return UnAuthorized(RESTAPI::Errors::MFA_FAILURE); | ||||
|         } | ||||
| 		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)) { | ||||
| 					Poco::JSON::Object ReturnObj; | ||||
| 					UInfo.webtoken.to_json(ReturnObj); | ||||
| 					return ReturnObject(ReturnObj); | ||||
| 				} | ||||
| 			} | ||||
| 			return UnAuthorized(RESTAPI::Errors::MFA_FAILURE); | ||||
| 		} | ||||
|  | ||||
|         SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|         bool Expired=false; | ||||
|         auto Code=AuthService()->AuthorizeSub(userId, password, newPassword, UInfo, Expired); | ||||
|         if (Code==SUCCESS) { | ||||
|             Poco::JSON::Object 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."); | ||||
|             } | ||||
|             UInfo.webtoken.to_json(ReturnObj); | ||||
|             return ReturnObject(ReturnObj); | ||||
|         } else { | ||||
|             switch(Code) { | ||||
|                 case INVALID_CREDENTIALS: | ||||
|                     return UnAuthorized(RESTAPI::Errors::INVALID_CREDENTIALS); | ||||
|                 case PASSWORD_INVALID: | ||||
|                     return UnAuthorized(RESTAPI::Errors::PASSWORD_INVALID); | ||||
|                 case PASSWORD_ALREADY_USED: | ||||
|                     return UnAuthorized(RESTAPI::Errors::PASSWORD_ALREADY_USED); | ||||
|                 case USERNAME_PENDING_VERIFICATION: | ||||
|                     return UnAuthorized(RESTAPI::Errors::USERNAME_PENDING_VERIFICATION); | ||||
|                 case PASSWORD_CHANGE_REQUIRED: | ||||
|                     return UnAuthorized(RESTAPI::Errors::PASSWORD_CHANGE_REQUIRED); | ||||
|                 default: | ||||
|                     return UnAuthorized(RESTAPI::Errors::INVALID_CREDENTIALS); break; | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 		SecurityObjects::UserInfoAndPolicy UInfo; | ||||
| 		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)) { | ||||
| 					return ReturnObject(ReturnObj); | ||||
| 				} | ||||
| 				Logger_.warning( | ||||
| 					"MFA Seems to be broken. Please fix. Disabling MFA checking for now."); | ||||
| 			} | ||||
| 			UInfo.webtoken.to_json(ReturnObj); | ||||
| 			return ReturnObject(ReturnObj); | ||||
| 		} | ||||
| 		case INVALID_CREDENTIALS: | ||||
| 			return UnAuthorized(RESTAPI::Errors::INVALID_CREDENTIALS); | ||||
| 		case PASSWORD_INVALID: | ||||
| 			return UnAuthorized(RESTAPI::Errors::PASSWORD_INVALID); | ||||
| 		case PASSWORD_ALREADY_USED: | ||||
| 			return UnAuthorized(RESTAPI::Errors::PASSWORD_ALREADY_USED); | ||||
| 		case USERNAME_PENDING_VERIFICATION: | ||||
| 			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); | ||||
| 		} | ||||
| 	} | ||||
| } // namespace OpenWifi | ||||
| @@ -3,25 +3,27 @@ | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_suboauth2_handler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_suboauth2_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &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"}; }; | ||||
|         void DoGet() final; | ||||
|         void DoPost() final; | ||||
|         void DoDelete() final; | ||||
|         void DoPut() final {}; | ||||
|     }; | ||||
| } | ||||
| 	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) | ||||
| 			: 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"}; | ||||
| 		}; | ||||
| 		void DoGet() final; | ||||
| 		void DoPost() final; | ||||
| 		void DoDelete() final; | ||||
| 		void DoPut() final{}; | ||||
| 	}; | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -7,30 +7,30 @@ | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     void RESTAPI_subpreferences::DoGet() { | ||||
|         SecurityObjects::Preferences    P; | ||||
|         Poco::JSON::Object  Answer; | ||||
|         StorageService()->SubPreferencesDB().GetPreferences(UserInfo_.userinfo.id, P); | ||||
|         P.to_json(Answer); | ||||
|         ReturnObject(Answer); | ||||
|     } | ||||
| 	void RESTAPI_subpreferences::DoGet() { | ||||
| 		SecurityObjects::Preferences P; | ||||
| 		Poco::JSON::Object Answer; | ||||
| 		StorageService()->SubPreferencesDB().GetPreferences(UserInfo_.userinfo.id, P); | ||||
| 		P.to_json(Answer); | ||||
| 		ReturnObject(Answer); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_subpreferences::DoPut() { | ||||
| 	void RESTAPI_subpreferences::DoPut() { | ||||
|  | ||||
|         SecurityObjects::Preferences    P; | ||||
| 		SecurityObjects::Preferences P; | ||||
|  | ||||
|         const auto & RawObject = ParsedBody_; | ||||
|         if(!P.from_json(RawObject)) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
|         } | ||||
| 		const auto &RawObject = ParsedBody_; | ||||
| 		if (!P.from_json(RawObject)) { | ||||
| 			return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
| 		} | ||||
|  | ||||
|         P.id = UserInfo_.userinfo.id; | ||||
|         P.modified = OpenWifi::Now(); | ||||
|         StorageService()->SubPreferencesDB().SetPreferences(P); | ||||
| 		P.id = UserInfo_.userinfo.id; | ||||
| 		P.modified = OpenWifi::Now(); | ||||
| 		StorageService()->SubPreferencesDB().SetPreferences(P); | ||||
|  | ||||
|         Poco::JSON::Object  Answer; | ||||
|         P.to_json(Answer); | ||||
|         ReturnObject(Answer); | ||||
|     } | ||||
| 		Poco::JSON::Object Answer; | ||||
| 		P.to_json(Answer); | ||||
| 		ReturnObject(Answer); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| } // namespace OpenWifi | ||||
| @@ -4,24 +4,23 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_subpreferences : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_subpreferences(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &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_OPTIONS}, | ||||
|             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 {}; | ||||
|     }; | ||||
| } | ||||
| 	class RESTAPI_subpreferences : public RESTAPIHandler { | ||||
| 	  public: | ||||
| 		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, | ||||
| 													  Poco::Net::HTTPRequest::HTTP_PUT, | ||||
| 													  Poco::Net::HTTPRequest::HTTP_OPTIONS}, | ||||
| 							 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{}; | ||||
| 	}; | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -5,33 +5,35 @@ | ||||
| #include "RESTAPI_subtotp_handler.h" | ||||
|  | ||||
| #include "TotpCache.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     void RESTAPI_subtotp_handler::DoGet() { | ||||
| 	void RESTAPI_subtotp_handler::DoGet() { | ||||
|  | ||||
|         auto Reset = GetBoolParameter("reset",false); | ||||
|         std::string QRCode; | ||||
| 		auto Reset = GetBoolParameter("reset", false); | ||||
| 		std::string QRCode; | ||||
|  | ||||
|         if(TotpCache()->StartValidation(UserInfo_.userinfo,true,QRCode,Reset)) { | ||||
|             return SendFileContent(QRCode, "image/svg+xml","qrcode.svg"); | ||||
|         } | ||||
|         return BadRequest(RESTAPI::Errors::InvalidCommand); | ||||
|     } | ||||
| 		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; | ||||
| 	void RESTAPI_subtotp_handler::DoPut() { | ||||
| 		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 )) { | ||||
|             Poco::JSON::Object Answer; | ||||
|             Answer.set("nextIndex", nextIndex); | ||||
|             Answer.set("moreCodes", moreCodes); | ||||
|             return ReturnObject(Answer); | ||||
|         } | ||||
|         return BadRequest(Error); | ||||
|     } | ||||
| 		RESTAPI::Errors::msg Error; | ||||
| 		if (TotpCache()->ContinueValidation(UserInfo_.userinfo, true, Value, nextIndex, moreCodes, | ||||
| 											Error)) { | ||||
| 			Poco::JSON::Object Answer; | ||||
| 			Answer.set("nextIndex", nextIndex); | ||||
| 			Answer.set("moreCodes", moreCodes); | ||||
| 			return ReturnObject(Answer); | ||||
| 		} | ||||
| 		return BadRequest(Error); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -2,28 +2,25 @@ | ||||
| // Created by stephane bourque on 2022-01-31. | ||||
| // | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_subtotp_handler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_subtotp_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &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_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 DoPut() final; | ||||
|     private: | ||||
| 	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) | ||||
| 			: RESTAPIHandler(bindings, L, | ||||
| 							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET, | ||||
| 													  Poco::Net::HTTPRequest::HTTP_PUT, | ||||
| 													  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 DoPut() final; | ||||
|  | ||||
|     }; | ||||
| } | ||||
| 	  private: | ||||
| 	}; | ||||
| } // namespace OpenWifi | ||||
|   | ||||
| @@ -3,314 +3,338 @@ | ||||
| // | ||||
|  | ||||
| #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" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     void RESTAPI_subuser_handler::DoGet() { | ||||
|         std::string Id = GetBinding("id", ""); | ||||
|         if(Id.empty()) { | ||||
|             return BadRequest(RESTAPI::Errors::MissingUserID); | ||||
|         } | ||||
| 	void RESTAPI_subuser_handler::DoGet() { | ||||
| 		std::string Id = GetBinding("id", ""); | ||||
| 		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)) { | ||||
|                 return NotFound(); | ||||
|             } | ||||
|         } else if(!StorageService()->SubDB().GetUserById(Id,UInfo)) { | ||||
|             return NotFound(); | ||||
|         } | ||||
| 		Poco::toLowerInPlace(Id); | ||||
| 		std::string Arg; | ||||
| 		SecurityObjects::UserInfo UInfo; | ||||
| 		if (HasParameter("byEmail", Arg) && Arg == "true") { | ||||
| 			if (!StorageService()->SubDB().GetUserByEmail(Id, UInfo)) { | ||||
| 				return NotFound(); | ||||
| 			} | ||||
| 		} else if (!StorageService()->SubDB().GetUserById(Id, UInfo)) { | ||||
| 			return NotFound(); | ||||
| 		} | ||||
|  | ||||
|         Poco::JSON::Object  UserInfoObject; | ||||
|         Sanitize(UserInfo_, UInfo); | ||||
|         UInfo.to_json(UserInfoObject); | ||||
|         ReturnObject(UserInfoObject); | ||||
|     } | ||||
| 		Poco::JSON::Object UserInfoObject; | ||||
| 		Sanitize(UserInfo_, UInfo); | ||||
| 		UInfo.to_json(UserInfoObject); | ||||
| 		ReturnObject(UserInfoObject); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_subuser_handler::DoDelete() { | ||||
|         std::string Id = GetBinding("id", ""); | ||||
|         if(Id.empty()) { | ||||
|             return BadRequest(RESTAPI::Errors::MissingUserID); | ||||
|         } | ||||
| 	void RESTAPI_subuser_handler::DoDelete() { | ||||
| 		std::string Id = GetBinding("id", ""); | ||||
| 		if (Id.empty()) { | ||||
| 			return BadRequest(RESTAPI::Errors::MissingUserID); | ||||
| 		} | ||||
|  | ||||
|         SecurityObjects::UserInfo TargetUser; | ||||
|         if(!StorageService()->SubDB().GetUserById(Id,TargetUser)) { | ||||
|             return NotFound(); | ||||
|         } | ||||
| 		SecurityObjects::UserInfo TargetUser; | ||||
| 		if (!StorageService()->SubDB().GetUserById(Id, TargetUser)) { | ||||
| 			return NotFound(); | ||||
| 		} | ||||
|  | ||||
|         if(TargetUser.userRole != SecurityObjects::SUBSCRIBER) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidUserRole); | ||||
|         } | ||||
| 		if (TargetUser.userRole != SecurityObjects::SUBSCRIBER) { | ||||
| 			return BadRequest(RESTAPI::Errors::InvalidUserRole); | ||||
| 		} | ||||
|  | ||||
|         if(!Internal_ && !ACLProcessor::Can(UserInfo_.userinfo, TargetUser,ACLProcessor::DELETE)) { | ||||
|             return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
|         } | ||||
| 		if (!Internal_ && | ||||
| 			!ACLProcessor::Can(UserInfo_.userinfo, TargetUser, ACLProcessor::DELETE)) { | ||||
| 			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
| 		} | ||||
|  | ||||
|         if(!StorageService()->SubDB().DeleteUser(UserInfo_.userinfo.email,Id)) { | ||||
|             return NotFound(); | ||||
|         } | ||||
| 		if (!StorageService()->SubDB().DeleteUser(UserInfo_.userinfo.email, Id)) { | ||||
| 			return NotFound(); | ||||
| 		} | ||||
|  | ||||
|         AuthService()->DeleteSubUserFromCache(Id); | ||||
|         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)); | ||||
|         OK(); | ||||
|     } | ||||
| 		AuthService()->DeleteSubUserFromCache(Id); | ||||
| 		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)); | ||||
| 		OK(); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_subuser_handler::DoPost() { | ||||
|         std::string Id = GetBinding("id", ""); | ||||
|         if(Id!="0") { | ||||
|             return BadRequest(RESTAPI::Errors::IdMustBe0); | ||||
|         } | ||||
| 	void RESTAPI_subuser_handler::DoPost() { | ||||
| 		std::string Id = GetBinding("id", ""); | ||||
| 		if (Id != "0") { | ||||
| 			return BadRequest(RESTAPI::Errors::IdMustBe0); | ||||
| 		} | ||||
|  | ||||
|         SecurityObjects::UserInfo   NewUser; | ||||
|         const auto & RawObject = ParsedBody_; | ||||
|         if(!NewUser.from_json(RawObject)) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
|         } | ||||
| 		SecurityObjects::UserInfo NewUser; | ||||
| 		const auto &RawObject = ParsedBody_; | ||||
| 		if (!NewUser.from_json(RawObject)) { | ||||
| 			return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
| 		} | ||||
|  | ||||
|         if(NewUser.userRole == SecurityObjects::UNKNOWN || NewUser.userRole != SecurityObjects::SUBSCRIBER) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidUserRole); | ||||
|         } | ||||
| 		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)) { | ||||
|             return BadRequest(RESTAPI::Errors::UserAlreadyExists); | ||||
|         } | ||||
| 		Poco::toLowerInPlace(NewUser.email); | ||||
| 		SecurityObjects::UserInfo Existing; | ||||
| 		if (StorageService()->SubDB().GetUserByEmail(NewUser.email, Existing)) { | ||||
| 			return BadRequest(RESTAPI::Errors::UserAlreadyExists); | ||||
| 		} | ||||
|  | ||||
|         if(!Internal_ && !ACLProcessor::Can(UserInfo_.userinfo,NewUser,ACLProcessor::CREATE)) { | ||||
|             return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
|         } | ||||
| 		if (!Internal_ && !ACLProcessor::Can(UserInfo_.userinfo, NewUser, ACLProcessor::CREATE)) { | ||||
| 			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
| 		} | ||||
|  | ||||
|         Poco::toLowerInPlace(NewUser.email); | ||||
|         if(!Utils::ValidEMailAddress(NewUser.email)) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidEmailAddress); | ||||
|         } | ||||
| 		Poco::toLowerInPlace(NewUser.email); | ||||
| 		if (!Utils::ValidEMailAddress(NewUser.email)) { | ||||
| 			return BadRequest(RESTAPI::Errors::InvalidEmailAddress); | ||||
| 		} | ||||
|  | ||||
|         if(!NewUser.currentPassword.empty()) { | ||||
|             if(!AuthService()->ValidateSubPassword(NewUser.currentPassword)) { | ||||
|                 return BadRequest(RESTAPI::Errors::InvalidPassword); | ||||
|             } | ||||
|         } | ||||
| 		if (!NewUser.currentPassword.empty()) { | ||||
| 			if (!AuthService()->ValidateSubPassword(NewUser.currentPassword)) { | ||||
| 				return BadRequest(RESTAPI::Errors::InvalidPassword); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|         if(NewUser.name.empty()) | ||||
|             NewUser.name = NewUser.email; | ||||
| 		if (NewUser.name.empty()) | ||||
| 			NewUser.name = NewUser.email; | ||||
|  | ||||
|         //  You cannot enable MFA during user creation | ||||
|         NewUser.userTypeProprietaryInfo.mfa.enabled = false; | ||||
|         NewUser.userTypeProprietaryInfo.mfa.method = ""; | ||||
|         NewUser.userTypeProprietaryInfo.mobiles.clear(); | ||||
|         NewUser.userTypeProprietaryInfo.authenticatorSecret.clear(); | ||||
| 		//  You cannot enable MFA during user creation | ||||
| 		NewUser.userTypeProprietaryInfo.mfa.enabled = false; | ||||
| 		NewUser.userTypeProprietaryInfo.mfa.method = ""; | ||||
| 		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)); | ||||
|             return BadRequest(RESTAPI::Errors::RecordNotCreated); | ||||
|         } | ||||
| 		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)); | ||||
|             return NotFound(); | ||||
|         } | ||||
| 		if (!StorageService()->SubDB().GetUserByEmail(NewUser.email, NewUser)) { | ||||
| 			Logger_.information(fmt::format("User '{}' but not retrieved.", NewUser.email)); | ||||
| 			return NotFound(); | ||||
| 		} | ||||
|  | ||||
|         Poco::JSON::Object  UserInfoObject; | ||||
|         Sanitize(UserInfo_, NewUser); | ||||
|         NewUser.to_json(UserInfoObject); | ||||
|         ReturnObject(UserInfoObject); | ||||
|         Logger_.information(fmt::format("User '{}' has been added by '{}')",NewUser.email, UserInfo_.userinfo.email)); | ||||
|     } | ||||
| 		Poco::JSON::Object UserInfoObject; | ||||
| 		Sanitize(UserInfo_, NewUser); | ||||
| 		NewUser.to_json(UserInfoObject); | ||||
| 		ReturnObject(UserInfoObject); | ||||
| 		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()) { | ||||
|             return BadRequest(RESTAPI::Errors::MissingUserID); | ||||
|         } | ||||
| 	void RESTAPI_subuser_handler::DoPut() { | ||||
| 		std::string Id = GetBinding("id", ""); | ||||
| 		if (Id.empty()) { | ||||
| 			return BadRequest(RESTAPI::Errors::MissingUserID); | ||||
| 		} | ||||
|  | ||||
|         SecurityObjects::UserInfo   Existing; | ||||
|         if(!StorageService()->SubDB().GetUserById(Id,Existing)) { | ||||
|             return NotFound(); | ||||
|         } | ||||
| 		SecurityObjects::UserInfo Existing; | ||||
| 		if (!StorageService()->SubDB().GetUserById(Id, Existing)) { | ||||
| 			return NotFound(); | ||||
| 		} | ||||
|  | ||||
|         if(!Internal_ && !ACLProcessor::Can(UserInfo_.userinfo,Existing,ACLProcessor::MODIFY)) { | ||||
|             return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
|         } | ||||
| 		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) || | ||||
|                 (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); | ||||
|                 SecurityObjects::UserInfo   NewUserInfo; | ||||
|                 StorageService()->SubDB().GetUserByEmail(UserInfo_.userinfo.email,NewUserInfo); | ||||
|                 Poco::JSON::Object  ModifiedObject; | ||||
|                 Sanitize(UserInfo_, NewUserInfo); | ||||
|                 NewUserInfo.to_json(ModifiedObject); | ||||
|                 return ReturnObject(ModifiedObject); | ||||
|             } else { | ||||
|                 return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
|             } | ||||
|         } | ||||
| 		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); | ||||
| 				SecurityObjects::UserInfo NewUserInfo; | ||||
| 				StorageService()->SubDB().GetUserByEmail(UserInfo_.userinfo.email, NewUserInfo); | ||||
| 				Poco::JSON::Object ModifiedObject; | ||||
| 				Sanitize(UserInfo_, NewUserInfo); | ||||
| 				NewUserInfo.to_json(ModifiedObject); | ||||
| 				return ReturnObject(ModifiedObject); | ||||
| 			} else { | ||||
| 				return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|         if(GetBoolParameter("forgotPassword")) { | ||||
|             Existing.changePassword = true; | ||||
|             Logger_.information(fmt::format("FORGOTTEN-PASSWORD({}): Request for {}", Request->clientAddress().toString(), Existing.email)); | ||||
| 		if (GetBoolParameter("forgotPassword")) { | ||||
| 			Existing.changePassword = true; | ||||
| 			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 = MicroService::CreateUUID(); | ||||
|             NewLink.userId = Existing.id; | ||||
|             NewLink.created = OpenWifi::Now(); | ||||
|             NewLink.expires = NewLink.created + (24*60*60); | ||||
|             NewLink.userAction = false; | ||||
|             StorageService()->ActionLinksDB().CreateAction(NewLink); | ||||
| 			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.userAction = false; | ||||
| 			StorageService()->ActionLinksDB().CreateAction(NewLink); | ||||
|  | ||||
|             return OK(); | ||||
|         } | ||||
| 			return OK(); | ||||
| 		} | ||||
|  | ||||
|         SecurityObjects::UserInfo   NewUser; | ||||
|         const auto & RawObject = ParsedBody_; | ||||
|         if(!NewUser.from_json(RawObject)) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
|         } | ||||
| 		SecurityObjects::UserInfo NewUser; | ||||
| 		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)) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidUserRole); | ||||
|         } | ||||
| 		// some basic validations | ||||
| 		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); | ||||
| 		// 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); | ||||
|  | ||||
|         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) { | ||||
|                     return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
|                 } | ||||
|                 Existing.userRole = NewRole; | ||||
|             } | ||||
|         } | ||||
| 		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) { | ||||
| 					return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
| 				} | ||||
| 				Existing.userRole = NewRole; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|         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}; | ||||
|                 Existing.notes.push_back(ii); | ||||
|             } | ||||
|         } | ||||
|         if(RawObject->has("currentPassword")) { | ||||
|             if(!AuthService()->ValidateSubPassword(RawObject->get("currentPassword").toString())) { | ||||
|                 return BadRequest(RESTAPI::Errors::InvalidPassword); | ||||
|             } | ||||
|             if(!AuthService()->SetPassword(RawObject->get("currentPassword").toString(),Existing)) { | ||||
|                 return BadRequest(RESTAPI::Errors::PasswordRejected); | ||||
|             } | ||||
|         } | ||||
| 		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}; | ||||
| 				Existing.notes.push_back(ii); | ||||
| 			} | ||||
| 		} | ||||
| 		if (RawObject->has("currentPassword")) { | ||||
| 			if (!AuthService()->ValidateSubPassword(RawObject->get("currentPassword").toString())) { | ||||
| 				return BadRequest(RESTAPI::Errors::InvalidPassword); | ||||
| 			} | ||||
| 			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 (!MFAMETHODS::Validate(NewUser.userTypeProprietaryInfo.mfa.method)) { | ||||
|                     return BadRequest(RESTAPI::Errors::BadMFAMethod); | ||||
|                 } | ||||
| 		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 && | ||||
|                     NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::SMS && | ||||
|                     !SMSSender()->Enabled()) { | ||||
|                     return BadRequest(RESTAPI::Errors::SMSMFANotEnabled); | ||||
|                 } | ||||
| 				if (NewUser.userTypeProprietaryInfo.mfa.enabled && | ||||
| 					NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::SMS && | ||||
| 					!SMSSender()->Enabled()) { | ||||
| 					return BadRequest(RESTAPI::Errors::SMSMFANotEnabled); | ||||
| 				} | ||||
|  | ||||
|                 if( NewUser.userTypeProprietaryInfo.mfa.enabled && | ||||
|                     NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::EMAIL && | ||||
|                     !SMTPMailerService()->Enabled()) { | ||||
|                     return BadRequest(RESTAPI::Errors::EMailMFANotEnabled); | ||||
|                 } | ||||
| 				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.enabled = true; | ||||
| 				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()) { | ||||
|                         return BadRequest(RESTAPI::Errors::NeedMobileNumber); | ||||
|                     } | ||||
|                     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[0].verified = true; | ||||
|                     Existing.userTypeProprietaryInfo.authenticatorSecret.clear(); | ||||
|                 } 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)) { | ||||
|                         Existing.userTypeProprietaryInfo.authenticatorSecret = Secret; | ||||
|                     } else if (!Existing.userTypeProprietaryInfo.authenticatorSecret.empty()) { | ||||
|                         // we allow someone to use their old secret | ||||
|                     } else { | ||||
|                         return BadRequest(RESTAPI::Errors::AuthenticatorVerificationIncomplete); | ||||
|                     } | ||||
|                 } else if (NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::EMAIL) { | ||||
|                     Existing.userTypeProprietaryInfo.mobiles.clear(); | ||||
|                     Existing.userTypeProprietaryInfo.authenticatorSecret.clear(); | ||||
|                 } | ||||
|             } else { | ||||
|                 Existing.userTypeProprietaryInfo.authenticatorSecret.clear(); | ||||
|                 Existing.userTypeProprietaryInfo.mobiles.clear(); | ||||
|                 Existing.userTypeProprietaryInfo.mfa.enabled = false; | ||||
|             } | ||||
|         } | ||||
| 				if (NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::SMS) { | ||||
| 					if (NewUser.userTypeProprietaryInfo.mobiles.empty()) { | ||||
| 						return BadRequest(RESTAPI::Errors::NeedMobileNumber); | ||||
| 					} | ||||
| 					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[0].verified = true; | ||||
| 					Existing.userTypeProprietaryInfo.authenticatorSecret.clear(); | ||||
| 				} 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)) { | ||||
| 						Existing.userTypeProprietaryInfo.authenticatorSecret = Secret; | ||||
| 					} else if (!Existing.userTypeProprietaryInfo.authenticatorSecret.empty()) { | ||||
| 						// we allow someone to use their old secret | ||||
| 					} else { | ||||
| 						return BadRequest(RESTAPI::Errors::AuthenticatorVerificationIncomplete); | ||||
| 					} | ||||
| 				} else if (NewUser.userTypeProprietaryInfo.mfa.method == MFAMETHODS::EMAIL) { | ||||
| 					Existing.userTypeProprietaryInfo.mobiles.clear(); | ||||
| 					Existing.userTypeProprietaryInfo.authenticatorSecret.clear(); | ||||
| 				} | ||||
| 			} else { | ||||
| 				Existing.userTypeProprietaryInfo.authenticatorSecret.clear(); | ||||
| 				Existing.userTypeProprietaryInfo.mobiles.clear(); | ||||
| 				Existing.userTypeProprietaryInfo.mfa.enabled = false; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|         if(StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email,Id,Existing)) { | ||||
|             SecurityObjects::UserInfo   NewUserInfo; | ||||
|             StorageService()->SubDB().GetUserById(Id,NewUserInfo); | ||||
|             Poco::JSON::Object  ModifiedObject; | ||||
|             Sanitize(UserInfo_, NewUserInfo); | ||||
|             NewUserInfo.to_json(ModifiedObject); | ||||
|             return ReturnObject(ModifiedObject); | ||||
|         } | ||||
|         BadRequest(RESTAPI::Errors::RecordNotUpdated); | ||||
|     } | ||||
| } | ||||
| 		if (StorageService()->SubDB().UpdateUserInfo(UserInfo_.userinfo.email, Id, Existing)) { | ||||
| 			SecurityObjects::UserInfo NewUserInfo; | ||||
| 			StorageService()->SubDB().GetUserById(Id, NewUserInfo); | ||||
| 			Poco::JSON::Object ModifiedObject; | ||||
| 			Sanitize(UserInfo_, NewUserInfo); | ||||
| 			NewUserInfo.to_json(ModifiedObject); | ||||
| 			return ReturnObject(ModifiedObject); | ||||
| 		} | ||||
| 		BadRequest(RESTAPI::Errors::RecordNotUpdated); | ||||
| 	} | ||||
| } // namespace OpenWifi | ||||
| @@ -4,28 +4,27 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_subuser_handler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_subuser_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) | ||||
|         : RESTAPIHandler(bindings, L, | ||||
|                          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) {} | ||||
|         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: | ||||
| 	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) | ||||
| 			: RESTAPIHandler(bindings, L, | ||||
| 							 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) {} | ||||
| 		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: | ||||
| 	}; | ||||
| } // namespace OpenWifi | ||||
|   | ||||