mirror of
https://github.com/Telecominfraproject/wlan-cloud-ucentralsec.git
synced 2026-01-27 02:23:03 +00:00
Initial
This commit is contained in:
@@ -52,6 +52,7 @@ include_directories(/usr/local/include /usr/local/opt/openssl/include src inclu
|
||||
add_executable( ucentralsec
|
||||
build
|
||||
src/Daemon.h src/Daemon.cpp
|
||||
src/MicroService.cpp src/MicroService.h
|
||||
src/SubSystemServer.cpp src/SubSystemServer.h
|
||||
src/RESTAPI_unknownRequestHandler.h src/RESTAPI_unknownRequestHandler.cpp
|
||||
src/RESTAPI_oauth2Handler.h src/RESTAPI_oauth2Handler.cpp
|
||||
@@ -66,7 +67,10 @@ add_executable( ucentralsec
|
||||
src/storage_identity.cpp
|
||||
src/Utils.cpp src/Utils.h
|
||||
src/storage_sqlite.cpp src/storage_odbc.cpp src/storage_sqlite.cpp src/storage_pgql.cpp src/storage_mysql.cpp
|
||||
src/storage_tables.cpp)
|
||||
src/storage_tables.cpp src/SMTPMailerService.cpp src/SMTPMailerService.h
|
||||
src/RESTAPI_users_handler.cpp src/RESTAPI_users_handler.h
|
||||
src/RESTAPI_user_handler.cpp src/RESTAPI_user_handler.h
|
||||
src/RESTAPI_action_links.cpp src/RESTAPI_action_links.h)
|
||||
|
||||
target_link_libraries(ucentralsec PUBLIC
|
||||
${Poco_LIBRARIES} ${Boost_LIBRARIES} ${ZLIB_LIBRARIES} ${AWSSDK_LINK_LIBRARIES} CppKafka::cppkafka )
|
||||
|
||||
36
docs/design/email_verification.md
Normal file
36
docs/design/email_verification.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# User Validation Actions
|
||||
The system uses email and links to offer user validation. Validation may include email verification, password reset
|
||||
requests, etc.
|
||||
|
||||
## Action links
|
||||
All action links will come back to the endpoint
|
||||
```
|
||||
https://site.com:port/api/v1/actions?command=payload
|
||||
```
|
||||
|
||||
## Payload
|
||||
The system encrypts the payload with its private key. A base64 encoder convert the encrypted message to something
|
||||
valid for a URI.
|
||||
|
||||
```json
|
||||
{
|
||||
"id" : "uuid",
|
||||
"email" : "email@host.com",
|
||||
"type" : "verificationType"
|
||||
}
|
||||
```
|
||||
|
||||
- `id` : UUID of the request that should be outstanding
|
||||
- `email` : email address of the user under verification
|
||||
- `type` : could be one of "emailValidation", "passwordResetRequest", or other.
|
||||
|
||||
## Templates
|
||||
Email templates come in 2 flavors: txt and html. The system replaces the following occurrences with system variables:
|
||||
|
||||
- {{action}} : the link that the user should press. That link should include the payload as above.
|
||||
- {{name}} : the name of the user field.
|
||||
- {{recipient}} : user email, asi-is
|
||||
|
||||
|
||||
|
||||
|
||||
109
docs/design/user_creation_flow.md
Normal file
109
docs/design/user_creation_flow.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# USER creation flow
|
||||
|
||||
## pre-requisite
|
||||
To create a user in the system, someone must first login as the super user as configured in the properties file. Once logged in as super user,
|
||||
thata person should create another user and convey the super user bit to them too. From that point on, only that second
|
||||
created super user should be used. We will call superuser root0 and the created superuser root1
|
||||
|
||||
## About usernames
|
||||
|
||||
### email is your username
|
||||
Your email address is your username. The username is case-insensitive.
|
||||
|
||||
### ASCII characters only
|
||||
Usernames must only use ASCII characters.
|
||||
|
||||
### forcing domain names
|
||||
You can allow only certain domain names by configuring the service with `email.includeonly` parameter
|
||||
```asm
|
||||
email.includeonly = mycorporatedomain.com
|
||||
```
|
||||
|
||||
### excluding email domains
|
||||
You may exclude e-mail domains you will not accept emails from in the configuration.
|
||||
You could, for example, not allow people in gmail by adding
|
||||
```asm
|
||||
email.exclude = gmail.com
|
||||
```
|
||||
|
||||
### precedence
|
||||
If `email.includeonly` is used, `email.exclude` is ignored.
|
||||
|
||||
## Creating a username
|
||||
In order to create a username, root1 must use the `/user/0` API call. The creation of a username involves:
|
||||
- the service will email the new user to verify her email address
|
||||
- the username remains dormant until the email verification completes
|
||||
- the email verification maybe canceled anytime by deleting the username
|
||||
- the email verification process times-out after `email.verification.timeout` in minutes
|
||||
- the new user must change her password using the `/oauth2?changePassword=true` and filling in a `WebTokenRequestChangePassword` request
|
||||
- the system will not accept any other calls until the user has changed her password
|
||||
|
||||
## Values accepted in user creation
|
||||
The user creation request must provide the following in the `UserInfo` of the `post`.
|
||||
|
||||
```asm
|
||||
id required = 0
|
||||
name optional = a string for the user display
|
||||
description: optional = a description of this user
|
||||
avatar: optional = an avatar URI
|
||||
email required = valid email address used as user name
|
||||
validated: ignored
|
||||
validationEmail: ignored
|
||||
validationDate: ignored
|
||||
created: ignored
|
||||
valiadationURI: ignored
|
||||
changePassword: ignored
|
||||
lastLogin: ignored
|
||||
currentLoginURI: ignored
|
||||
lastPasswordChange: ignored
|
||||
lastEmailCheck: ignored
|
||||
currentPassword: ignored
|
||||
lastPasswords: ignored
|
||||
waitingForEmailCheck: ignored
|
||||
notes: optional = cumulative notes that may be added in for this user
|
||||
location: optionsl = UUID of a provisioning server location
|
||||
owner: optional = UUID of a providioning server owner
|
||||
suspended: optional = if true, the user can change password but not do anything else
|
||||
blackListed: ignored
|
||||
locale: optional = 2 letter code of country language, default to EN. If the language specified is not supported, EN is assumed.
|
||||
userType: required = root/admin/csr/sub/system/special, defaults to sub
|
||||
oauthType: optional = if using oauth, a recognized oauth provider
|
||||
oauthUserInfo: ignored
|
||||
```
|
||||
|
||||
## Values accepted during user update
|
||||
When doing a `put`, these are the accepted fields.
|
||||
|
||||
```asm
|
||||
id required = must match the ID in the path
|
||||
name optional = a string for the user display
|
||||
description: optional = a description of this user
|
||||
avatar: optional = an avatar URI
|
||||
email ignored
|
||||
validated: ignored
|
||||
validationEmail: ignored
|
||||
validationDate: ignored
|
||||
created: ignored
|
||||
valiadationURI: ignored
|
||||
changePassword: optonal = set to true to force a password change for the user
|
||||
lastLogin: ignored
|
||||
currentLoginURI: ignored
|
||||
lastPasswordChange: ignored
|
||||
lastEmailCheck: ignored
|
||||
currentPassword: ignored
|
||||
lastPasswords: ignored
|
||||
waitingForEmailCheck: ignored
|
||||
notes: optional = cumulative notes that may be added in for this user
|
||||
location: optionsl = UUID of a provisioning server location
|
||||
owner: optional = UUID of a providioning server owner
|
||||
suspended: optional = if true, the user can change password but not do anything else
|
||||
blackListed: optional = if true, user cannot login/deleted
|
||||
locale: optional = 2 letter code of country language, default to EN. If the language specified is not supported, EN is assumed.
|
||||
userType: required = root/admin/csr/sub/system/special, defaults to sub
|
||||
oauthType: optional = if using oauth, a recognized oauth provider
|
||||
oauthUserInfo: ignored
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ openapi: 3.0.1
|
||||
info:
|
||||
title: uCentral Security API
|
||||
description: A process to manage security logins
|
||||
version: 0.0.1
|
||||
version: 0.0.2
|
||||
license:
|
||||
name: BSD3
|
||||
url: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
|
||||
@@ -64,7 +64,6 @@ components:
|
||||
format: uuid
|
||||
|
||||
schemas:
|
||||
|
||||
GenericErrorResponse:
|
||||
description: Typical error response
|
||||
properties:
|
||||
@@ -104,6 +103,28 @@ components:
|
||||
userId: support@example.com
|
||||
password: support
|
||||
|
||||
WebTokenRequestChangePassword:
|
||||
description: User Id and password.
|
||||
type: object
|
||||
required:
|
||||
- userId
|
||||
- password
|
||||
properties:
|
||||
userId:
|
||||
type: string
|
||||
default: support@example.com
|
||||
oldPassword:
|
||||
type: string
|
||||
default: support
|
||||
newPassword:
|
||||
type: string
|
||||
default: support
|
||||
refreshToken:
|
||||
type: string
|
||||
example:
|
||||
userId: support@example.com
|
||||
password: support
|
||||
|
||||
WebTokenResult:
|
||||
description: Login and Refresh Tokens to be used in subsequent API calls.
|
||||
type: object
|
||||
@@ -125,6 +146,8 @@ components:
|
||||
created:
|
||||
type: integer
|
||||
format: int64
|
||||
userMustChangePassword:
|
||||
type: boolean
|
||||
aclTemplate:
|
||||
$ref: '#/components/schemas/WebTokenAclTemplate'
|
||||
|
||||
@@ -203,6 +226,109 @@ components:
|
||||
items:
|
||||
$ref: '#/components/schemas/SystemEndpoint'
|
||||
|
||||
UserInfo:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
avatar:
|
||||
type: string
|
||||
format: uri
|
||||
email:
|
||||
type: string
|
||||
validated:
|
||||
type: boolean
|
||||
validationEmail:
|
||||
type: string
|
||||
validationDate:
|
||||
type: integer
|
||||
format: int64
|
||||
created:
|
||||
type: integer
|
||||
format: int64
|
||||
valiadationURI:
|
||||
type: string
|
||||
changePassword:
|
||||
type: boolean
|
||||
lastLogin:
|
||||
type: integer
|
||||
format: int64
|
||||
currentLoginURI:
|
||||
type: string
|
||||
lastPasswordChange:
|
||||
type: integer
|
||||
format: int64
|
||||
lastEmailCheck:
|
||||
type: integer
|
||||
format: int64
|
||||
currentPassword:
|
||||
type: string
|
||||
lastPasswords:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
waitingForEmailCheck:
|
||||
type: boolean
|
||||
notes:
|
||||
type: string
|
||||
location:
|
||||
type: string
|
||||
format: uuid
|
||||
owner:
|
||||
type: string
|
||||
format: uuid
|
||||
suspended:
|
||||
type: boolean
|
||||
blackListed:
|
||||
type: boolean
|
||||
locale:
|
||||
type: string
|
||||
userRole:
|
||||
type: string
|
||||
enum:
|
||||
- root
|
||||
- admin
|
||||
- sub
|
||||
- csr
|
||||
- system
|
||||
- special
|
||||
oauthType:
|
||||
type: string
|
||||
enum:
|
||||
- internal
|
||||
- normal
|
||||
- gmail
|
||||
- facebook
|
||||
- linkedin
|
||||
- instagram
|
||||
oauthUserInfo:
|
||||
type: string
|
||||
|
||||
UserList:
|
||||
type: object
|
||||
properties:
|
||||
list:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UserInfo'
|
||||
|
||||
|
||||
#########################################################################################
|
||||
##
|
||||
## These are endpoints that all services in the uCentral stack must provide
|
||||
##
|
||||
#########################################################################################
|
||||
AnyPayload:
|
||||
type: object
|
||||
properties:
|
||||
Document:
|
||||
type: string
|
||||
|
||||
StringList:
|
||||
type: object
|
||||
properties:
|
||||
@@ -249,6 +375,54 @@ components:
|
||||
- $ref: '#/components/schemas/StringList'
|
||||
- $ref: '#/components/schemas/TagValuePairList'
|
||||
|
||||
ProfileAction:
|
||||
type: object
|
||||
properties:
|
||||
resource:
|
||||
type: string
|
||||
access:
|
||||
type: string
|
||||
enum:
|
||||
- READ
|
||||
- MODIFY
|
||||
- DELETE
|
||||
- CREATE
|
||||
- TEST
|
||||
- MOVE
|
||||
|
||||
SecurityProfile:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
policy:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ProfileAction'
|
||||
role:
|
||||
type: string
|
||||
notes:
|
||||
type: string
|
||||
|
||||
SecurityProfileList:
|
||||
type: object
|
||||
properties:
|
||||
profiles:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/SecurityProfile'
|
||||
|
||||
#########################################################################################
|
||||
##
|
||||
## End of uCentral system wide values
|
||||
##
|
||||
#########################################################################################
|
||||
|
||||
|
||||
paths:
|
||||
/oauth2:
|
||||
@@ -257,13 +431,21 @@ paths:
|
||||
- Authentication
|
||||
summary: Get access token - to be used as Bearer token header for all other API requests.
|
||||
operationId: getAccessToken
|
||||
parameters:
|
||||
- in: query
|
||||
name: changePassword
|
||||
schema:
|
||||
type: string
|
||||
required: false
|
||||
requestBody:
|
||||
description: User id and password
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/WebTokenRequest'
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/WebTokenRequestChangePassword'
|
||||
- $ref: '#/components/schemas/WebTokenRequest'
|
||||
responses:
|
||||
200:
|
||||
description: successful operation
|
||||
@@ -319,6 +501,140 @@ paths:
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/users:
|
||||
get:
|
||||
tags:
|
||||
- User Management
|
||||
summary: Retrieve a list of existing users as well as some information about them.
|
||||
operationId: getUsers
|
||||
parameters:
|
||||
- in: query
|
||||
name: offset
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
required: false
|
||||
- in: query
|
||||
name: limit
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
required: false
|
||||
- in: query
|
||||
description: Selecting this option means the newest record will be returned. Use limit to select how many.
|
||||
name: filter
|
||||
schema:
|
||||
type: string
|
||||
required: false
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/schemas/UserList'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/user/{id}:
|
||||
get:
|
||||
tags:
|
||||
- User Management
|
||||
operationId: getUser
|
||||
summary: Retrieve the information for a single user
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/schemas/UserInfo'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
delete:
|
||||
tags:
|
||||
- User Management
|
||||
operationId: deleteUser
|
||||
summary: Delete s single user
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/responses/Success'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
post:
|
||||
tags:
|
||||
- User Management
|
||||
operationId: createUser
|
||||
summary: Create a single user
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
#must be set to 0 for user creation
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
requestBody:
|
||||
description: User details (some fields are ignored during creation)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UserInfo'
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/schemas/UserInfo'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
put:
|
||||
tags:
|
||||
- User Management
|
||||
operationId: updateUser
|
||||
summary: Modifying a single user
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
requestBody:
|
||||
description: User details (some fields are ignored during update)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UserInfo'
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/schemas/UserInfo'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
|
||||
#########################################################################################
|
||||
##
|
||||
## These are endpoints that all services in the uCentral stack must provide
|
||||
##
|
||||
#########################################################################################
|
||||
|
||||
/system:
|
||||
post:
|
||||
tags:
|
||||
@@ -342,3 +658,91 @@ paths:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/callbackChannel:
|
||||
post:
|
||||
tags:
|
||||
- Callback
|
||||
summary: Generic callback hook
|
||||
operationId: postCallback
|
||||
parameters:
|
||||
- in: query
|
||||
name: subscribe
|
||||
schema:
|
||||
type: boolean
|
||||
required: false
|
||||
- in: query
|
||||
name: uri
|
||||
schema:
|
||||
type: string
|
||||
format: uri
|
||||
- in: query
|
||||
name: key
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: topics
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: id
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: topic
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
description: A generic JSONDocument, may be empty too {}
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AnyPayload'
|
||||
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/responses/Success'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/securityProfiles:
|
||||
get:
|
||||
tags:
|
||||
- Security
|
||||
summary: Retrieve the list of security profiles for a specific service type
|
||||
operationId: getSecurituProfiles
|
||||
parameters:
|
||||
- in: query
|
||||
description: Pagination start (starts at 1. If not specified, 1 is assumed)
|
||||
name: offset
|
||||
schema:
|
||||
type: integer
|
||||
required: false
|
||||
- in: query
|
||||
description: Maximum number of entries to return (if absent, no limit is assumed)
|
||||
name: limit
|
||||
schema:
|
||||
type: integer
|
||||
required: false
|
||||
- in: query
|
||||
description: Filter the results
|
||||
name: filter
|
||||
schema:
|
||||
type: string
|
||||
required: false
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/schemas/SecurityProfileList'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
#########################################################################################
|
||||
##
|
||||
## These are endpoints that all services in the uCentral stack must provide
|
||||
##
|
||||
#########################################################################################
|
||||
|
||||
@@ -40,11 +40,6 @@ namespace uCentral {
|
||||
return 1; // some compilers complain...
|
||||
}
|
||||
|
||||
AuthService::AuthService() noexcept: SubSystemServer("Authentication", "AUTH-SVR", "authentication")
|
||||
{
|
||||
std::string E{"SHA512"};
|
||||
}
|
||||
|
||||
int AuthService::Start() {
|
||||
Signer_.setRSAKey(Daemon()->Key());
|
||||
Signer_.addAllAlgorithms();
|
||||
@@ -193,18 +188,17 @@ namespace uCentral {
|
||||
|
||||
if(Mechanism_=="internal")
|
||||
{
|
||||
if(((UserName == DefaultUserName_) && (Password == DefaultPassword_)) || !Secure_)
|
||||
if(((UserName == DefaultUserName_) && (DefaultPassword_== ComputePasswordHash(UserName,Password))) || !Secure_)
|
||||
{
|
||||
ACL.PortalLogin_ = ACL.Read_ = ACL.ReadWrite_ = ACL.ReadWriteCreate_ = ACL.Delete_ = true;
|
||||
ACL.PortalLogin_ = ACL.Read_ = ACL.ReadWrite_ = ACL.ReadWriteCreate_ = ACL.Delete_ = true;
|
||||
CreateToken(UserName, ResultToken, ACL);
|
||||
return true;
|
||||
}
|
||||
} else if (Mechanism_=="db") {
|
||||
SHA2_.update(Password + UserName);
|
||||
auto EncryptedPassword = uCentral::Utils::ToHex(SHA2_.digest());
|
||||
auto PasswordHash = ComputePasswordHash(UserName, Password);
|
||||
|
||||
std::string TUser{UserName};
|
||||
if(Storage()->GetIdentity(TUser,EncryptedPassword,USERNAME,ACL)) {
|
||||
if(Storage()->GetIdentity(TUser,PasswordHash,USERNAME,ACL)) {
|
||||
CreateToken(UserName, ResultToken, ACL);
|
||||
return true;
|
||||
}
|
||||
@@ -212,4 +206,10 @@ namespace uCentral {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string AuthService::ComputePasswordHash(const std::string &UserName, const std::string &Password) {
|
||||
std::string UName = Poco::trim(Poco::toLower(UserName));
|
||||
SHA2_.update(Password + UName);
|
||||
return uCentral::Utils::ToHex(SHA2_.digest());
|
||||
}
|
||||
|
||||
} // end of namespace
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace uCentral{
|
||||
class AuthService : public SubSystemServer {
|
||||
public:
|
||||
|
||||
typedef std::map<std::string,uCentral::Objects::WebToken> WebTokenMap;
|
||||
enum ACCESS_TYPE {
|
||||
USERNAME,
|
||||
SERVER,
|
||||
@@ -48,18 +49,23 @@ namespace uCentral{
|
||||
void Logout(const std::string &token);
|
||||
[[nodiscard]] std::string GenerateToken(const std::string & UserName, ACCESS_TYPE Type, int NumberOfDays);
|
||||
[[nodiscard]] bool ValidateToken(const std::string & Token, std::string & SessionToken, struct uCentral::Objects::WebToken & UserInfo );
|
||||
|
||||
[[nodiscard]] std::string ComputePasswordHash(const std::string &UserName, const std::string &Password);
|
||||
[[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);
|
||||
private:
|
||||
static AuthService *instance_;
|
||||
std::map<std::string,uCentral::Objects::WebToken> Tokens_;
|
||||
WebTokenMap Tokens_;
|
||||
bool Secure_ = false ;
|
||||
std::string DefaultUserName_;
|
||||
std::string DefaultPassword_;
|
||||
std::string Mechanism_;
|
||||
bool AutoProvisioning_ = false ;
|
||||
Poco::JWT::Signer Signer_;
|
||||
Poco::SHA2Engine SHA2_;
|
||||
AuthService() noexcept;
|
||||
|
||||
AuthService() noexcept:
|
||||
SubSystemServer("Authentication", "AUTH-SVR", "authentication")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline AuthService * AuthService() { return AuthService::instance(); }
|
||||
|
||||
310
src/Daemon.cpp
310
src/Daemon.cpp
@@ -14,319 +14,46 @@
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "Poco/Util/Application.h"
|
||||
#include "Poco/Util/ServerApplication.h"
|
||||
#include "Poco/Util/Option.h"
|
||||
#include "Poco/Util/OptionSet.h"
|
||||
#include "Poco/Util/HelpFormatter.h"
|
||||
#include "Poco/Environment.h"
|
||||
#include "Poco/Path.h"
|
||||
#include "Poco/File.h"
|
||||
#include "Poco/Net/SSLManager.h"
|
||||
|
||||
#include "Utils.h"
|
||||
#include "Daemon.h"
|
||||
|
||||
#include "ALBHealthCheckServer.h"
|
||||
#include "KafkaManager.h"
|
||||
#include "StorageService.h"
|
||||
#include "RESTAPI_server.h"
|
||||
#include "SMTPMailerService.h"
|
||||
|
||||
#include "Daemon.h"
|
||||
|
||||
namespace uCentral {
|
||||
class Daemon *Daemon::instance_ = nullptr;
|
||||
|
||||
void MyErrorHandler::exception(const Poco::Exception & E) {
|
||||
Poco::Thread * CurrentThread = Poco::Thread::current();
|
||||
Daemon()->logger().log(E);
|
||||
Daemon()->logger().error(Poco::format("Exception occurred in %s",CurrentThread->getName()));
|
||||
}
|
||||
|
||||
void MyErrorHandler::exception(const std::exception & E) {
|
||||
Poco::Thread * CurrentThread = Poco::Thread::current();
|
||||
Daemon()->logger().warning(Poco::format("std::exception on %s",CurrentThread->getName()));
|
||||
}
|
||||
|
||||
void MyErrorHandler::exception() {
|
||||
Poco::Thread * CurrentThread = Poco::Thread::current();
|
||||
Daemon()->logger().warning(Poco::format("exception on %s",CurrentThread->getName()));
|
||||
}
|
||||
|
||||
void Daemon::Exit(int Reason) {
|
||||
std::exit(Reason);
|
||||
}
|
||||
|
||||
void Daemon::initialize(Application &self) {
|
||||
|
||||
Poco::Net::initializeSSL();
|
||||
|
||||
SubSystems_ = Types::SubSystemVec{
|
||||
Storage(),
|
||||
RESTAPI_Server(),
|
||||
KafkaManager(),
|
||||
ALBHealthCheckServer()
|
||||
};
|
||||
|
||||
std::string Location = Poco::Environment::get(uCentral::DAEMON_CONFIG_ENV_VAR,".");
|
||||
Poco::Path ConfigFile;
|
||||
|
||||
ConfigFile = ConfigFileName_.empty() ? Location + "/" + uCentral::DAEMON_PROPERTIES_FILENAME : ConfigFileName_;
|
||||
|
||||
if(!ConfigFile.isFile())
|
||||
{
|
||||
std::cerr << uCentral::DAEMON_APP_NAME << ": Configuration " << ConfigFile.toString() << " does not seem to exist. Please set "
|
||||
<< uCentral::DAEMON_ROOT_ENV_VAR << " env variable the path of the "
|
||||
<< uCentral::DAEMON_PROPERTIES_FILENAME << " file." << std::endl;
|
||||
std::exit(Poco::Util::Application::EXIT_CONFIG);
|
||||
class Daemon *Daemon::instance() {
|
||||
if (instance_ == nullptr) {
|
||||
instance_ = new Daemon(vDAEMON_PROPERTIES_FILENAME,
|
||||
vDAEMON_ROOT_ENV_VAR,
|
||||
vDAEMON_CONFIG_ENV_VAR,
|
||||
vDAEMON_APP_NAME,
|
||||
Types::SubSystemVec{
|
||||
Storage(),
|
||||
RESTAPI_Server(),
|
||||
KafkaManager(),
|
||||
SMTPMailerService(),
|
||||
ALBHealthCheckServer()
|
||||
});
|
||||
}
|
||||
|
||||
static const char * LogFilePathKey = "logging.channels.c2.path";
|
||||
|
||||
loadConfiguration(ConfigFile.toString());
|
||||
|
||||
if(LogDir_.empty()) {
|
||||
std::string OriginalLogFileValue = ConfigPath(LogFilePathKey);
|
||||
config().setString(LogFilePathKey, OriginalLogFileValue);
|
||||
} else {
|
||||
config().setString(LogFilePathKey, LogDir_);
|
||||
}
|
||||
Poco::File DataDir(ConfigPath("ucentral.system.data"));
|
||||
DataDir_ = DataDir.path();
|
||||
if(!DataDir.exists()) {
|
||||
try {
|
||||
DataDir.createDirectory();
|
||||
} catch (const Poco::Exception &E) {
|
||||
logger().log(E);
|
||||
}
|
||||
}
|
||||
std::string KeyFile = ConfigPath("ucentral.service.key");
|
||||
AppKey_ = Poco::SharedPtr<Poco::Crypto::RSAKey>(new Poco::Crypto::RSAKey("", KeyFile, ""));
|
||||
ID_ = ConfigGetInt("ucentral.system.id",1);
|
||||
if(!DebugMode_)
|
||||
DebugMode_ = ConfigGetBool("ucentral.system.debug",false);
|
||||
|
||||
InitializeSubSystemServers();
|
||||
logger().information("Starting...");
|
||||
ServerApplication::initialize(self);
|
||||
return instance_;
|
||||
}
|
||||
|
||||
void Daemon::uninitialize() {
|
||||
// add your own uninitialization code here
|
||||
ServerApplication::uninitialize();
|
||||
void Daemon::initialize(Poco::Util::Application &self) {
|
||||
MicroService::initialize(*this);
|
||||
}
|
||||
|
||||
void Daemon::reinitialize(Poco::Util::Application &self) {
|
||||
ServerApplication::reinitialize(self);
|
||||
// add your own reinitialization code here
|
||||
}
|
||||
|
||||
void Daemon::defineOptions(Poco::Util::OptionSet &options) {
|
||||
ServerApplication::defineOptions(options);
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("help", "", "display help information on command line arguments")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.callback(Poco::Util::OptionCallback<Daemon>(this, &Daemon::handleHelp)));
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("file", "", "specify the configuration file")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.argument("file")
|
||||
.callback(Poco::Util::OptionCallback<Daemon>(this, &Daemon::handleConfig)));
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("debug", "", "to run in debug, set to true")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.callback(Poco::Util::OptionCallback<Daemon>(this, &Daemon::handleDebug)));
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("logs", "", "specify the log directory and file (i.e. dir/file.log)")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.argument("dir")
|
||||
.callback(Poco::Util::OptionCallback<Daemon>(this, &Daemon::handleLogs)));
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("version", "", "get the version and quit.")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.callback(Poco::Util::OptionCallback<Daemon>(this, &Daemon::handleVersion)));
|
||||
|
||||
}
|
||||
|
||||
std::string Daemon::Version() {
|
||||
std::string V = APP_VERSION;
|
||||
std::string B = BUILD_NUMBER;
|
||||
return V + "(" + B + ")";
|
||||
}
|
||||
|
||||
void Daemon::handleHelp(const std::string &name, const std::string &value) {
|
||||
HelpRequested_ = true;
|
||||
displayHelp();
|
||||
stopOptionsProcessing();
|
||||
}
|
||||
|
||||
void Daemon::handleVersion(const std::string &name, const std::string &value) {
|
||||
HelpRequested_ = true;
|
||||
std::cout << Version() << std::endl;
|
||||
stopOptionsProcessing();
|
||||
}
|
||||
|
||||
void Daemon::handleDebug(const std::string &name, const std::string &value) {
|
||||
if(value == "true")
|
||||
DebugMode_ = true ;
|
||||
}
|
||||
|
||||
void Daemon::handleLogs(const std::string &name, const std::string &value) {
|
||||
LogDir_ = value;
|
||||
}
|
||||
|
||||
void Daemon::handleConfig(const std::string &name, const std::string &value) {
|
||||
ConfigFileName_ = value;
|
||||
}
|
||||
|
||||
void Daemon::displayHelp() {
|
||||
Poco::Util::HelpFormatter helpFormatter(options());
|
||||
helpFormatter.setCommand(commandName());
|
||||
helpFormatter.setUsage("OPTIONS");
|
||||
helpFormatter.setHeader("A " + std::string(uCentral::DAEMON_APP_NAME) + " implementation for TIP.");
|
||||
helpFormatter.format(std::cout);
|
||||
}
|
||||
|
||||
void Daemon::InitializeSubSystemServers() {
|
||||
for(auto i:SubSystems_)
|
||||
addSubsystem(i);
|
||||
}
|
||||
|
||||
void Daemon::StartSubSystemServers() {
|
||||
for(auto i:SubSystems_)
|
||||
i->Start();
|
||||
}
|
||||
|
||||
void Daemon::StopSubSystemServers() {
|
||||
for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i)
|
||||
(*i)->Stop();
|
||||
}
|
||||
|
||||
std::string Daemon::CreateUUID() {
|
||||
return UUIDGenerator_.create().toString();
|
||||
}
|
||||
|
||||
bool Daemon::SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) {
|
||||
try {
|
||||
auto P = Poco::Logger::parseLevel(Level);
|
||||
auto Sub = Poco::toLower(SubSystem);
|
||||
|
||||
if (Sub == "all") {
|
||||
for (auto i : SubSystems_) {
|
||||
i->Logger().setLevel(P);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
std::cout << "Sub:" << SubSystem << " Level:" << Level << std::endl;
|
||||
for (auto i : SubSystems_) {
|
||||
if (Sub == Poco::toLower(i->Name())) {
|
||||
i->Logger().setLevel(P);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const Poco::Exception & E) {
|
||||
std::cout << "Exception" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Types::StringVec Daemon::GetSubSystems() const {
|
||||
Types::StringVec Result;
|
||||
for(auto i:SubSystems_)
|
||||
Result.push_back(i->Name());
|
||||
return Result;
|
||||
}
|
||||
|
||||
Types::StringPairVec Daemon::GetLogLevels() const {
|
||||
Types::StringPairVec Result;
|
||||
|
||||
for(auto &i:SubSystems_) {
|
||||
auto P = std::make_pair( i->Name(), Utils::LogLevelToString(i->GetLoggingLevel()));
|
||||
Result.push_back(P);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
const Types::StringVec & Daemon::GetLogLevelNames() const {
|
||||
static Types::StringVec LevelNames{"none", "fatal", "critical", "error", "warning", "notice", "information", "debug", "trace" };
|
||||
return LevelNames;
|
||||
}
|
||||
|
||||
uint64_t Daemon::ConfigGetInt(const std::string &Key,uint64_t Default) {
|
||||
return (uint64_t) config().getInt64(Key,Default);
|
||||
}
|
||||
|
||||
uint64_t Daemon::ConfigGetInt(const std::string &Key) {
|
||||
return config().getInt(Key);
|
||||
}
|
||||
|
||||
uint64_t Daemon::ConfigGetBool(const std::string &Key,bool Default) {
|
||||
return config().getBool(Key,Default);
|
||||
}
|
||||
|
||||
uint64_t Daemon::ConfigGetBool(const std::string &Key) {
|
||||
return config().getBool(Key);
|
||||
}
|
||||
|
||||
std::string Daemon::ConfigGetString(const std::string &Key,const std::string & Default) {
|
||||
return config().getString(Key, Default);
|
||||
}
|
||||
|
||||
std::string Daemon::ConfigGetString(const std::string &Key) {
|
||||
return config().getString(Key);
|
||||
}
|
||||
|
||||
std::string Daemon::ConfigPath(const std::string &Key,const std::string & Default) {
|
||||
std::string R = config().getString(Key, Default);
|
||||
return Poco::Path::expand(R);
|
||||
}
|
||||
|
||||
std::string Daemon::ConfigPath(const std::string &Key) {
|
||||
std::string R = config().getString(Key);
|
||||
return Poco::Path::expand(R);
|
||||
}
|
||||
|
||||
int Daemon::main(const ArgVec &args) {
|
||||
|
||||
Poco::ErrorHandler::set(&AppErrorHandler_);
|
||||
|
||||
if (!HelpRequested_) {
|
||||
Poco::Logger &logger = Poco::Logger::get(uCentral::DAEMON_APP_NAME );
|
||||
logger.notice(Poco::format("Starting %s version %s.",std::string(uCentral::DAEMON_APP_NAME), Version()));
|
||||
|
||||
if(Poco::Net::Socket::supportsIPv6())
|
||||
logger.information("System supports IPv6.");
|
||||
else
|
||||
logger.information("System does NOT support IPv6.");
|
||||
|
||||
if (config().getBool("application.runAsDaemon", false))
|
||||
{
|
||||
logger.information("Starting as a daemon.");
|
||||
}
|
||||
|
||||
StartSubSystemServers();
|
||||
instance()->waitForTerminationRequest();
|
||||
StopSubSystemServers();
|
||||
|
||||
logger.notice(Poco::format("Stopped %s...",std::string(uCentral::DAEMON_APP_NAME)));
|
||||
}
|
||||
|
||||
return Application::EXIT_OK;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
try {
|
||||
|
||||
auto App = uCentral::Daemon::instance();
|
||||
auto ExitCode = App->run(argc, argv);
|
||||
delete App;
|
||||
@@ -339,4 +66,5 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// end of namespace
|
||||
|
||||
91
src/Daemon.h
91
src/Daemon.h
@@ -17,85 +17,36 @@
|
||||
#include "Poco/UUIDGenerator.h"
|
||||
#include "Poco/ErrorHandler.h"
|
||||
#include "Poco/Crypto/RSAKey.h"
|
||||
#include "Poco/Crypto/CipherFactory.h"
|
||||
#include "Poco/Crypto/Cipher.h"
|
||||
|
||||
|
||||
#include "SubSystemServer.h"
|
||||
#include "uCentralTypes.h"
|
||||
|
||||
using Poco::Util::ServerApplication;
|
||||
#include "MicroService.h"
|
||||
|
||||
namespace uCentral {
|
||||
|
||||
static const char * DAEMON_PROPERTIES_FILENAME = "ucentralsec.properties";
|
||||
static const char * DAEMON_ROOT_ENV_VAR = "UCENTRALSEC_ROOT";
|
||||
static const char * DAEMON_CONFIG_ENV_VAR = "UCENTRALSEC_CONFIG";
|
||||
static const char * DAEMON_APP_NAME = "uCentralSec";
|
||||
|
||||
class MyErrorHandler : public Poco::ErrorHandler {
|
||||
static const char * vDAEMON_PROPERTIES_FILENAME = "ucentralsec.properties";
|
||||
static const char * vDAEMON_ROOT_ENV_VAR = "UCENTRALSEC_ROOT";
|
||||
static const char * vDAEMON_CONFIG_ENV_VAR = "UCENTRALSEC_CONFIG";
|
||||
static const char * vDAEMON_APP_NAME = "uCentralSec";
|
||||
class Daemon : public MicroService {
|
||||
public:
|
||||
void exception(const Poco::Exception & E) override;
|
||||
void exception(const std::exception & E) override;
|
||||
void exception() override;
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
class Daemon : public ServerApplication {
|
||||
|
||||
public:
|
||||
int main(const ArgVec &args) override;
|
||||
void initialize(Application &self) override;
|
||||
void uninitialize() override;
|
||||
void reinitialize(Application &self) override;
|
||||
void defineOptions(Poco::Util::OptionSet &options) override;
|
||||
void handleHelp(const std::string &name, const std::string &value);
|
||||
void handleVersion(const std::string &name, const std::string &value);
|
||||
void handleDebug(const std::string &name, const std::string &value);
|
||||
void handleLogs(const std::string &name, const std::string &value);
|
||||
void handleConfig(const std::string &name, const std::string &value);
|
||||
void displayHelp();
|
||||
|
||||
static Daemon *instance() {
|
||||
if (instance_ == nullptr) {
|
||||
instance_ = new Daemon;
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
void InitializeSubSystemServers();
|
||||
void StartSubSystemServers();
|
||||
void StopSubSystemServers();
|
||||
void Exit(int Reason);
|
||||
bool SetSubsystemLogLevel(const std::string & SubSystem, const std::string & Level);
|
||||
[[nodiscard]] static std::string Version();
|
||||
[[nodiscard]] const Poco::SharedPtr<Poco::Crypto::RSAKey> & Key() { return AppKey_; }
|
||||
[[nodiscard]] inline const std::string & DataDir() { return DataDir_; }
|
||||
[[nodiscard]] std::string CreateUUID();
|
||||
[[nodiscard]] bool Debug() const { return DebugMode_; }
|
||||
[[nodiscard]] uint64_t ID() const { return ID_; }
|
||||
[[nodiscard]] Types::StringVec GetSubSystems() const;
|
||||
[[nodiscard]] Types::StringPairVec GetLogLevels() const;
|
||||
[[nodiscard]] const Types::StringVec & GetLogLevelNames() const;
|
||||
[[nodiscard]] std::string ConfigGetString(const std::string &Key,const std::string & Default);
|
||||
[[nodiscard]] std::string ConfigGetString(const std::string &Key);
|
||||
[[nodiscard]] std::string ConfigPath(const std::string &Key,const std::string & Default);
|
||||
[[nodiscard]] std::string ConfigPath(const std::string &Key);
|
||||
[[nodiscard]] uint64_t ConfigGetInt(const std::string &Key,uint64_t Default);
|
||||
[[nodiscard]] uint64_t ConfigGetInt(const std::string &Key);
|
||||
[[nodiscard]] uint64_t ConfigGetBool(const std::string &Key,bool Default);
|
||||
[[nodiscard]] uint64_t ConfigGetBool(const std::string &Key);
|
||||
explicit Daemon(std::string PropFile,
|
||||
std::string RootEnv,
|
||||
std::string ConfigEnv,
|
||||
std::string AppName,
|
||||
Types::SubSystemVec SubSystems) :
|
||||
MicroService( PropFile, RootEnv, ConfigEnv, AppName, SubSystems) {};
|
||||
|
||||
bool AutoProvisioning() const { return AutoProvisioning_ ; }
|
||||
[[nodiscard]] std::string IdentifyDevice(const std::string & Compatible) const;
|
||||
void initialize(Poco::Util::Application &self);
|
||||
static Daemon *instance();
|
||||
private:
|
||||
static Daemon *instance_;
|
||||
bool HelpRequested_ = false;
|
||||
std::string LogDir_;
|
||||
std::string ConfigFileName_;
|
||||
Poco::UUIDGenerator UUIDGenerator_;
|
||||
MyErrorHandler AppErrorHandler_;
|
||||
uint64_t ID_ = 1;
|
||||
Poco::SharedPtr<Poco::Crypto::RSAKey> AppKey_ = nullptr;
|
||||
bool DebugMode_ = false;
|
||||
std::string DataDir_;
|
||||
Types::SubSystemVec SubSystems_;
|
||||
bool AutoProvisioning_ = false;
|
||||
Types::StringMapStringSet DeviceTypeIdentifications_;
|
||||
};
|
||||
|
||||
inline Daemon * Daemon() { return Daemon::instance(); }
|
||||
|
||||
319
src/MicroService.cpp
Normal file
319
src/MicroService.cpp
Normal file
@@ -0,0 +1,319 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-06-22.
|
||||
//
|
||||
#include <cstdlib>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "Poco/Util/Application.h"
|
||||
#include "Poco/Util/ServerApplication.h"
|
||||
#include "Poco/Util/Option.h"
|
||||
#include "Poco/Util/OptionSet.h"
|
||||
#include "Poco/Util/HelpFormatter.h"
|
||||
#include "Poco/Environment.h"
|
||||
#include "Poco/Net/HTTPSStreamFactory.h"
|
||||
#include "Poco/Net/HTTPStreamFactory.h"
|
||||
#include "Poco/Net/FTPSStreamFactory.h"
|
||||
#include "Poco/Net/FTPStreamFactory.h"
|
||||
#include "Poco/Path.h"
|
||||
#include "Poco/File.h"
|
||||
#include "Poco/String.h"
|
||||
|
||||
#include "MicroService.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace uCentral {
|
||||
|
||||
void MyErrorHandler::exception(const Poco::Exception & E) {
|
||||
Poco::Thread * CurrentThread = Poco::Thread::current();
|
||||
App_.logger().log(E);
|
||||
App_.logger().error(Poco::format("Exception occurred in %s",CurrentThread->getName()));
|
||||
}
|
||||
|
||||
void MyErrorHandler::exception(const std::exception & E) {
|
||||
Poco::Thread * CurrentThread = Poco::Thread::current();
|
||||
App_.logger().warning(Poco::format("std::exception on %s",CurrentThread->getName()));
|
||||
}
|
||||
|
||||
void MyErrorHandler::exception() {
|
||||
Poco::Thread * CurrentThread = Poco::Thread::current();
|
||||
App_.logger().warning(Poco::format("exception on %s",CurrentThread->getName()));
|
||||
}
|
||||
|
||||
void MicroService::Exit(int Reason) {
|
||||
std::exit(Reason);
|
||||
}
|
||||
|
||||
void MicroService::initialize(Poco::Util::Application &self) {
|
||||
Poco::Net::initializeSSL();
|
||||
Poco::Net::HTTPStreamFactory::registerFactory();
|
||||
Poco::Net::HTTPSStreamFactory::registerFactory();
|
||||
Poco::Net::FTPStreamFactory::registerFactory();
|
||||
Poco::Net::FTPSStreamFactory::registerFactory();
|
||||
std::string Location = Poco::Environment::get(DAEMON_CONFIG_ENV_VAR,".");
|
||||
Poco::Path ConfigFile;
|
||||
|
||||
ConfigFile = ConfigFileName_.empty() ? Location + "/" + DAEMON_PROPERTIES_FILENAME : ConfigFileName_;
|
||||
|
||||
if(!ConfigFile.isFile())
|
||||
{
|
||||
std::cerr << DAEMON_APP_NAME << ": Configuration "
|
||||
<< ConfigFile.toString() << " does not seem to exist. Please set " + DAEMON_CONFIG_ENV_VAR
|
||||
+ " env variable the path of the " + DAEMON_PROPERTIES_FILENAME + " file." << std::endl;
|
||||
std::exit(Poco::Util::Application::EXIT_CONFIG);
|
||||
}
|
||||
|
||||
static const char * LogFilePathKey = "logging.channels.c2.path";
|
||||
|
||||
loadConfiguration(ConfigFile.toString());
|
||||
|
||||
if(LogDir_.empty()) {
|
||||
std::string OriginalLogFileValue = ConfigPath(LogFilePathKey);
|
||||
config().setString(LogFilePathKey, OriginalLogFileValue);
|
||||
} else {
|
||||
config().setString(LogFilePathKey, LogDir_);
|
||||
}
|
||||
Poco::File DataDir(ConfigPath("ucentral.system.data"));
|
||||
DataDir_ = DataDir.path();
|
||||
if(!DataDir.exists()) {
|
||||
try {
|
||||
DataDir.createDirectory();
|
||||
} catch (const Poco::Exception &E) {
|
||||
logger().log(E);
|
||||
}
|
||||
}
|
||||
std::string KeyFile = ConfigPath("ucentral.service.key");
|
||||
AppKey_ = Poco::SharedPtr<Poco::Crypto::RSAKey>(new Poco::Crypto::RSAKey("", KeyFile, ""));
|
||||
Cipher_ = CipherFactory_.createCipher(*AppKey_);
|
||||
ID_ = Utils::GetSystemId();
|
||||
if(!DebugMode_)
|
||||
DebugMode_ = ConfigGetBool("ucentral.system.debug",false);
|
||||
|
||||
InitializeSubSystemServers();
|
||||
ServerApplication::initialize(self);
|
||||
}
|
||||
|
||||
void MicroService::uninitialize() {
|
||||
// add your own uninitialization code here
|
||||
ServerApplication::uninitialize();
|
||||
}
|
||||
|
||||
void MicroService::reinitialize(Poco::Util::Application &self) {
|
||||
ServerApplication::reinitialize(self);
|
||||
// add your own reinitialization code here
|
||||
}
|
||||
|
||||
void MicroService::defineOptions(Poco::Util::OptionSet &options) {
|
||||
ServerApplication::defineOptions(options);
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("help", "", "display help information on command line arguments")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleHelp)));
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("file", "", "specify the configuration file")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.argument("file")
|
||||
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleConfig)));
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("debug", "", "to run in debug, set to true")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleDebug)));
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("logs", "", "specify the log directory and file (i.e. dir/file.log)")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.argument("dir")
|
||||
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleLogs)));
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("version", "", "get the version and quit.")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleVersion)));
|
||||
|
||||
}
|
||||
|
||||
std::string MicroService::Version() {
|
||||
std::string V = APP_VERSION;
|
||||
std::string B = BUILD_NUMBER;
|
||||
return V + "(" + B + ")";
|
||||
}
|
||||
|
||||
void MicroService::handleHelp(const std::string &name, const std::string &value) {
|
||||
HelpRequested_ = true;
|
||||
displayHelp();
|
||||
stopOptionsProcessing();
|
||||
}
|
||||
|
||||
void MicroService::handleVersion(const std::string &name, const std::string &value) {
|
||||
HelpRequested_ = true;
|
||||
std::cout << Version() << std::endl;
|
||||
stopOptionsProcessing();
|
||||
}
|
||||
|
||||
void MicroService::handleDebug(const std::string &name, const std::string &value) {
|
||||
if(value == "true")
|
||||
DebugMode_ = true ;
|
||||
}
|
||||
|
||||
void MicroService::handleLogs(const std::string &name, const std::string &value) {
|
||||
LogDir_ = value;
|
||||
}
|
||||
|
||||
void MicroService::handleConfig(const std::string &name, const std::string &value) {
|
||||
ConfigFileName_ = value;
|
||||
}
|
||||
|
||||
void MicroService::displayHelp() {
|
||||
Poco::Util::HelpFormatter helpFormatter(options());
|
||||
helpFormatter.setCommand(commandName());
|
||||
helpFormatter.setUsage("OPTIONS");
|
||||
helpFormatter.setHeader("A " + DAEMON_APP_NAME + " implementation for TIP.");
|
||||
helpFormatter.format(std::cout);
|
||||
}
|
||||
|
||||
void MicroService::InitializeSubSystemServers() {
|
||||
for(auto i:SubSystems_)
|
||||
addSubsystem(i);
|
||||
}
|
||||
|
||||
void MicroService::StartSubSystemServers() {
|
||||
for(auto i:SubSystems_)
|
||||
i->Start();
|
||||
}
|
||||
|
||||
void MicroService::StopSubSystemServers() {
|
||||
for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i)
|
||||
(*i)->Stop();
|
||||
}
|
||||
|
||||
std::string MicroService::CreateUUID() {
|
||||
return UUIDGenerator_.create().toString();
|
||||
}
|
||||
|
||||
bool MicroService::SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) {
|
||||
try {
|
||||
auto P = Poco::Logger::parseLevel(Level);
|
||||
auto Sub = Poco::toLower(SubSystem);
|
||||
|
||||
if (Sub == "all") {
|
||||
for (auto i : SubSystems_) {
|
||||
i->Logger().setLevel(P);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
std::cout << "Sub:" << SubSystem << " Level:" << Level << std::endl;
|
||||
for (auto i : SubSystems_) {
|
||||
if (Sub == Poco::toLower(i->Name())) {
|
||||
i->Logger().setLevel(P);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const Poco::Exception & E) {
|
||||
std::cout << "Exception" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Types::StringVec MicroService::GetSubSystems() const {
|
||||
Types::StringVec Result;
|
||||
for(auto i:SubSystems_)
|
||||
Result.push_back(i->Name());
|
||||
return Result;
|
||||
}
|
||||
|
||||
Types::StringPairVec MicroService::GetLogLevels() const {
|
||||
Types::StringPairVec Result;
|
||||
|
||||
for(auto &i:SubSystems_) {
|
||||
auto P = std::make_pair( i->Name(), Utils::LogLevelToString(i->GetLoggingLevel()));
|
||||
Result.push_back(P);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
const Types::StringVec & MicroService::GetLogLevelNames() const {
|
||||
static Types::StringVec LevelNames{"none", "fatal", "critical", "error", "warning", "notice", "information", "debug", "trace" };
|
||||
return LevelNames;
|
||||
}
|
||||
|
||||
uint64_t MicroService::ConfigGetInt(const std::string &Key,uint64_t Default) {
|
||||
return (uint64_t) config().getInt64(Key,Default);
|
||||
}
|
||||
|
||||
uint64_t MicroService::ConfigGetInt(const std::string &Key) {
|
||||
return config().getInt(Key);
|
||||
}
|
||||
|
||||
uint64_t MicroService::ConfigGetBool(const std::string &Key,bool Default) {
|
||||
return config().getBool(Key,Default);
|
||||
}
|
||||
|
||||
uint64_t MicroService::ConfigGetBool(const std::string &Key) {
|
||||
return config().getBool(Key);
|
||||
}
|
||||
|
||||
std::string MicroService::ConfigGetString(const std::string &Key,const std::string & Default) {
|
||||
return config().getString(Key, Default);
|
||||
}
|
||||
|
||||
std::string MicroService::ConfigGetString(const std::string &Key) {
|
||||
return config().getString(Key);
|
||||
}
|
||||
|
||||
std::string MicroService::ConfigPath(const std::string &Key,const std::string & Default) {
|
||||
std::string R = config().getString(Key, Default);
|
||||
return Poco::Path::expand(R);
|
||||
}
|
||||
|
||||
std::string MicroService::ConfigPath(const std::string &Key) {
|
||||
std::string R = config().getString(Key);
|
||||
return Poco::Path::expand(R);
|
||||
}
|
||||
|
||||
std::string MicroService::Encrypt(const std::string &S) {
|
||||
return Cipher_->encryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);;
|
||||
}
|
||||
|
||||
std::string MicroService::Decrypt(const std::string &S) {
|
||||
return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);;
|
||||
}
|
||||
|
||||
int MicroService::main(const ArgVec &args) {
|
||||
|
||||
MyErrorHandler ErrorHandler(*this);
|
||||
Poco::ErrorHandler::set(&ErrorHandler);
|
||||
|
||||
if (!HelpRequested_) {
|
||||
Poco::Logger &logger = Poco::Logger::get(DAEMON_APP_NAME);
|
||||
logger.notice(Poco::format("Starting %s version %s.",DAEMON_APP_NAME, Version()));
|
||||
|
||||
if(Poco::Net::Socket::supportsIPv6())
|
||||
logger.information("System supports IPv6.");
|
||||
else
|
||||
logger.information("System does NOT support IPv6.");
|
||||
|
||||
if (config().getBool("application.runAsDaemon", false)) {
|
||||
logger.information("Starting as a daemon.");
|
||||
}
|
||||
logger.information(Poco::format("System ID set to %Lu",ID_));
|
||||
StartSubSystemServers();
|
||||
waitForTerminationRequest();
|
||||
StopSubSystemServers();
|
||||
|
||||
logger.notice(Poco::format("Stopped %s...",DAEMON_APP_NAME));
|
||||
}
|
||||
|
||||
return Application::EXIT_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
109
src/MicroService.h
Normal file
109
src/MicroService.h
Normal file
@@ -0,0 +1,109 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-06-22.
|
||||
//
|
||||
|
||||
#ifndef UCENTRALGW_MICROSERVICE_H
|
||||
#define UCENTRALGW_MICROSERVICE_H
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#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 "uCentralTypes.h"
|
||||
#include "SubSystemServer.h"
|
||||
|
||||
namespace uCentral {
|
||||
|
||||
class MyErrorHandler : public Poco::ErrorHandler {
|
||||
public:
|
||||
explicit MyErrorHandler(Poco::Util::Application &App) : App_(App) {}
|
||||
void exception(const Poco::Exception & E) override;
|
||||
void exception(const std::exception & E) override;
|
||||
void exception() override;
|
||||
private:
|
||||
Poco::Util::Application &App_;
|
||||
};
|
||||
|
||||
class MicroService : public Poco::Util::ServerApplication {
|
||||
public:
|
||||
explicit MicroService( std::string PropFile,
|
||||
std::string RootEnv,
|
||||
std::string ConfigVar,
|
||||
std::string AppName,
|
||||
Types::SubSystemVec Subsystems) :
|
||||
DAEMON_PROPERTIES_FILENAME(std::move(PropFile)),
|
||||
DAEMON_ROOT_ENV_VAR(std::move(RootEnv)),
|
||||
DAEMON_CONFIG_ENV_VAR(std::move(ConfigVar)),
|
||||
DAEMON_APP_NAME(std::move(AppName)),
|
||||
SubSystems_(Subsystems) {}
|
||||
|
||||
int main(const ArgVec &args) override;
|
||||
void initialize(Application &self) override;
|
||||
void uninitialize() override;
|
||||
void reinitialize(Application &self) override;
|
||||
void defineOptions(Poco::Util::OptionSet &options) override;
|
||||
void handleHelp(const std::string &name, const std::string &value);
|
||||
void handleVersion(const std::string &name, const std::string &value);
|
||||
void handleDebug(const std::string &name, const std::string &value);
|
||||
void handleLogs(const std::string &name, const std::string &value);
|
||||
void handleConfig(const std::string &name, const std::string &value);
|
||||
void displayHelp();
|
||||
|
||||
void InitializeSubSystemServers();
|
||||
void StartSubSystemServers();
|
||||
void StopSubSystemServers();
|
||||
void Exit(int Reason);
|
||||
bool SetSubsystemLogLevel(const std::string & SubSystem, const std::string & Level);
|
||||
[[nodiscard]] static std::string Version();
|
||||
[[nodiscard]] const Poco::SharedPtr<Poco::Crypto::RSAKey> & Key() { return AppKey_; }
|
||||
[[nodiscard]] inline const std::string & DataDir() { return DataDir_; }
|
||||
[[nodiscard]] std::string CreateUUID();
|
||||
[[nodiscard]] bool Debug() const { return DebugMode_; }
|
||||
[[nodiscard]] uint64_t ID() const { return ID_; }
|
||||
[[nodiscard]] Types::StringVec GetSubSystems() const;
|
||||
[[nodiscard]] Types::StringPairVec GetLogLevels() const;
|
||||
[[nodiscard]] const Types::StringVec & GetLogLevelNames() const;
|
||||
[[nodiscard]] std::string ConfigGetString(const std::string &Key,const std::string & Default);
|
||||
[[nodiscard]] std::string ConfigGetString(const std::string &Key);
|
||||
[[nodiscard]] std::string ConfigPath(const std::string &Key,const std::string & Default);
|
||||
[[nodiscard]] std::string ConfigPath(const std::string &Key);
|
||||
[[nodiscard]] uint64_t ConfigGetInt(const std::string &Key,uint64_t Default);
|
||||
[[nodiscard]] uint64_t ConfigGetInt(const std::string &Key);
|
||||
[[nodiscard]] uint64_t ConfigGetBool(const std::string &Key,bool Default);
|
||||
[[nodiscard]] uint64_t ConfigGetBool(const std::string &Key);
|
||||
[[nodiscard]] std::string Encrypt(const std::string &S);
|
||||
[[nodiscard]] std::string Decrypt(const std::string &S);
|
||||
|
||||
private:
|
||||
bool HelpRequested_ = false;
|
||||
std::string LogDir_;
|
||||
std::string ConfigFileName_;
|
||||
Poco::UUIDGenerator UUIDGenerator_;
|
||||
uint64_t ID_ = 1;
|
||||
Poco::SharedPtr<Poco::Crypto::RSAKey> AppKey_ = nullptr;
|
||||
bool DebugMode_ = false;
|
||||
std::string DataDir_;
|
||||
Types::SubSystemVec SubSystems_;
|
||||
Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory();
|
||||
Poco::Crypto::Cipher * Cipher_ = nullptr;
|
||||
|
||||
std::string DAEMON_PROPERTIES_FILENAME;
|
||||
std::string DAEMON_ROOT_ENV_VAR;
|
||||
std::string DAEMON_CONFIG_ENV_VAR;
|
||||
std::string DAEMON_APP_NAME;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // UCENTRALGW_MICROSERVICE_H
|
||||
11
src/RESTAPI_action_links.cpp
Normal file
11
src/RESTAPI_action_links.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-06-22.
|
||||
//
|
||||
|
||||
#include "RESTAPI_action_links.h"
|
||||
|
||||
namespace uCentral {
|
||||
void RESTAPI_action_links:: handleRequest(Poco::Net::HTTPServerRequest &Request,
|
||||
Poco::Net::HTTPServerResponse &Response) {
|
||||
}
|
||||
}
|
||||
25
src/RESTAPI_action_links.h
Normal file
25
src/RESTAPI_action_links.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-06-22.
|
||||
//
|
||||
|
||||
#ifndef UCENTRALSEC_RESTAPI_ACTION_LINKS_H
|
||||
#define UCENTRALSEC_RESTAPI_ACTION_LINKS_H
|
||||
|
||||
|
||||
#include "RESTAPI_handler.h"
|
||||
|
||||
namespace uCentral {
|
||||
class RESTAPI_action_links : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_action_links(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>{
|
||||
Poco::Net::HTTPRequest::HTTP_GET,
|
||||
Poco::Net::HTTPRequest::HTTP_POST,
|
||||
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {}
|
||||
void handleRequest(Poco::Net::HTTPServerRequest &Request,
|
||||
Poco::Net::HTTPServerResponse &Response) override;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //UCENTRALSEC_RESTAPI_ACTION_LINKS_H
|
||||
@@ -12,6 +12,9 @@
|
||||
#include "RESTAPI_oauth2Handler.h"
|
||||
#include "RESTAPI_unknownRequestHandler.h"
|
||||
#include "RESTAPI_system_command.h"
|
||||
#include "RESTAPI_user_handler.h"
|
||||
#include "RESTAPI_users_handler.h"
|
||||
#include "RESTAPI_action_links.h"
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
@@ -61,8 +64,14 @@ namespace uCentral {
|
||||
return new RESTAPI_oauth2Handler(bindings, Logger_);
|
||||
} else if (RESTAPIHandler::ParseBindings(Path, "/api/v1/oauth2", bindings)) {
|
||||
return new RESTAPI_oauth2Handler(bindings, Logger_);
|
||||
} else if (RESTAPIHandler::ParseBindings(Path, "/api/v1/users", bindings)) {
|
||||
return new RESTAPI_users_handler(bindings, Logger_);
|
||||
} else if (RESTAPIHandler::ParseBindings(Path, "/api/v1/user", bindings)) {
|
||||
return new RESTAPI_user_handler(bindings, Logger_);
|
||||
} else if (RESTAPIHandler::ParseBindings(Path, "/api/v1/system", bindings)) {
|
||||
return new RESTAPI_system_command(bindings, Logger_);
|
||||
} else if (RESTAPIHandler::ParseBindings(Path, "/api/v1/actions", bindings)) {
|
||||
return new RESTAPI_action_links(bindings, Logger_);
|
||||
}
|
||||
|
||||
Logger_.error(Poco::format("INVALID-API-ENDPOINT: %s",Path));
|
||||
|
||||
11
src/RESTAPI_user_handler.cpp
Normal file
11
src/RESTAPI_user_handler.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-06-21.
|
||||
//
|
||||
|
||||
#include "RESTAPI_user_handler.h"
|
||||
|
||||
namespace uCentral {
|
||||
void RESTAPI_user_handler::handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) {
|
||||
|
||||
}
|
||||
}
|
||||
28
src/RESTAPI_user_handler.h
Normal file
28
src/RESTAPI_user_handler.h
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-06-21.
|
||||
//
|
||||
|
||||
#ifndef UCENTRALSEC_RESTAPI_USER_HANDLER_H
|
||||
#define UCENTRALSEC_RESTAPI_USER_HANDLER_H
|
||||
|
||||
#include "RESTAPI_handler.h"
|
||||
|
||||
namespace uCentral {
|
||||
class RESTAPI_user_handler : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_user_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
|
||||
: 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}) {}
|
||||
void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override;
|
||||
private:
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif //UCENTRALSEC_RESTAPI_USER_HANDLER_H
|
||||
11
src/RESTAPI_users_handler.cpp
Normal file
11
src/RESTAPI_users_handler.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-06-21.
|
||||
//
|
||||
|
||||
#include "RESTAPI_users_handler.h"
|
||||
|
||||
namespace uCentral {
|
||||
void RESTAPI_users_handler::handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) {
|
||||
|
||||
}
|
||||
}
|
||||
25
src/RESTAPI_users_handler.h
Normal file
25
src/RESTAPI_users_handler.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-06-21.
|
||||
//
|
||||
|
||||
#ifndef UCENTRALSEC_RESTAPI_USERS_HANDLER_H
|
||||
#define UCENTRALSEC_RESTAPI_USERS_HANDLER_H
|
||||
|
||||
#include "RESTAPI_handler.h"
|
||||
|
||||
namespace uCentral {
|
||||
class RESTAPI_users_handler : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_users_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>
|
||||
{Poco::Net::HTTPRequest::HTTP_GET,
|
||||
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {}
|
||||
void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override;
|
||||
private:
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif //UCENTRALSEC_RESTAPI_USERS_HANDLER_H
|
||||
94
src/SMTPMailerService.cpp
Normal file
94
src/SMTPMailerService.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-06-17.
|
||||
//
|
||||
#include <iostream>
|
||||
|
||||
#include "Poco/Net/MailMessage.h"
|
||||
#include "Poco/Net/MailRecipient.h"
|
||||
#include "Poco/Net/SMTPClientSession.h"
|
||||
#include "Poco/Net/SecureSMTPClientSession.h"
|
||||
#include "Poco/Net/StringPartSource.h"
|
||||
#include "Poco/Path.h"
|
||||
#include "Poco/Exception.h"
|
||||
#include "Poco/Net/SSLManager.h"
|
||||
#include "Poco/Net/Context.h"
|
||||
#include "Poco/Net/InvalidCertificateHandler.h"
|
||||
#include "Poco/Net/AcceptCertificateHandler.h"
|
||||
|
||||
#include "SMTPMailerService.h"
|
||||
#include "Daemon.h"
|
||||
|
||||
namespace uCentral {
|
||||
|
||||
class SMTPMailerService * SMTPMailerService::instance_ = nullptr;
|
||||
|
||||
int SMTPMailerService::Start() {
|
||||
MailHost_ = Daemon()->ConfigGetString("mailer.hostname");
|
||||
SenderLoginUserName_ = Daemon()->ConfigGetString("mailer.username");
|
||||
SenderLoginPassword_ = Daemon()->ConfigGetString("mailer.password");
|
||||
LoginMethod_ = Daemon()->ConfigGetString("mailer.loginmethod");
|
||||
MailHostPort_ = Daemon()->ConfigGetInt("mailer.port");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SMTPMailerService::Stop() {
|
||||
|
||||
}
|
||||
|
||||
bool SMTPMailerService::SendMessage(SMTPMailerService::MessageAttributes Attrs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SMTPMailerService::SendIt() {
|
||||
try
|
||||
{
|
||||
Poco::SharedPtr<Poco::Net::InvalidCertificateHandler> ptrHandler = new Poco::Net::AcceptCertificateHandler(false);
|
||||
|
||||
/*
|
||||
Poco::Net::MailMessage message;
|
||||
message.setSender(Sender_);
|
||||
message.addRecipient(MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, recipient));
|
||||
message.setSubject("Hello from the POCO C++ Libraries");
|
||||
|
||||
std::string content;
|
||||
|
||||
content += "Hello ";
|
||||
content += recipient;
|
||||
content += ",\r\n\r\n";
|
||||
content += "This is a greeting from the POCO C++ Libraries.\r\n\r\n";
|
||||
std::string logo(reinterpret_cast<const char*>(PocoLogo), sizeof(PocoLogo));
|
||||
message.addContent(new Poco::Net::StringPartSource(content));
|
||||
message.addAttachment("logo", new Poco::Net::StringPartSource(logo, "image/gif"));
|
||||
|
||||
Poco::Net::SecureSMTPClientSession session(MailHost_,MailHostPort_);
|
||||
|
||||
Poco::Net::Context::Params P;
|
||||
auto ptrContext = new Poco::Net::Context( Poco::Net::Context::CLIENT_USE, "", "", "",
|
||||
Poco::Net::Context::VERIFY_RELAXED, 9, true,
|
||||
"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
|
||||
|
||||
Poco::Net::SSLManager::instance().initializeClient(nullptr,
|
||||
ptrHandler,
|
||||
ptrContext);
|
||||
|
||||
session.login();
|
||||
session.startTLS(ptrContext);
|
||||
session.login(MailHost_,
|
||||
Poco::Net::SecureSMTPClientSession::AUTH_LOGIN,
|
||||
SenderLoginUserName_,
|
||||
SenderLoginPassword_
|
||||
);
|
||||
session.sendMessage(message);
|
||||
session.close();
|
||||
*/
|
||||
}
|
||||
catch (const Poco::Exception& exc)
|
||||
{
|
||||
std::cerr << exc.displayText() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
60
src/SMTPMailerService.h
Normal file
60
src/SMTPMailerService.h
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-06-17.
|
||||
//
|
||||
|
||||
#ifndef UCENTRALSEC_SMTPMAILERSERVICE_H
|
||||
#define UCENTRALSEC_SMTPMAILERSERVICE_H
|
||||
|
||||
#include "SubSystemServer.h"
|
||||
|
||||
namespace uCentral {
|
||||
class SMTPMailerService : public SubSystemServer {
|
||||
public:
|
||||
enum MESSAGE_ATTRIBUTES {
|
||||
RECIPIENT_EMAIL,
|
||||
RECIPIENT_FIRST_NAME,
|
||||
RECIPIENT_LAST_NAME,
|
||||
RECIPIENT_INITIALS,
|
||||
RECIPIENT_FULL_NAME,
|
||||
RECIPIENT_SALUTATION,
|
||||
SUBJECT,
|
||||
SIGNATURE,
|
||||
TEMPLATE,
|
||||
LOGO
|
||||
};
|
||||
|
||||
typedef std::map<MESSAGE_ATTRIBUTES, std::string> MessageAttributes;
|
||||
|
||||
static SMTPMailerService *instance() {
|
||||
if (instance_ == nullptr) {
|
||||
instance_ = new SMTPMailerService;
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
int Start() override;
|
||||
void Stop() override;
|
||||
bool SendMessage(MessageAttributes Attrs);
|
||||
bool SendIt();
|
||||
|
||||
private:
|
||||
static SMTPMailerService * instance_;
|
||||
std::string MailHost_;
|
||||
std::string Sender_;
|
||||
int MailHostPort_=25;
|
||||
std::string SenderLoginUserName_;
|
||||
std::string SenderLoginPassword_;
|
||||
std::string LoginMethod_ = "login";
|
||||
std::string LogoFileName_;
|
||||
|
||||
SMTPMailerService() noexcept:
|
||||
SubSystemServer("SMTPMailer", "MAILER-SVR", "smtpmailer")
|
||||
{
|
||||
std::string E{"SHA512"};
|
||||
}
|
||||
};
|
||||
|
||||
inline SMTPMailerService * SMTPMailerService() { return SMTPMailerService::instance(); }
|
||||
}
|
||||
|
||||
#endif //UCENTRALSEC_SMTPMAILERSERVICE_H
|
||||
@@ -36,10 +36,13 @@ namespace uCentral {
|
||||
odbc
|
||||
};
|
||||
|
||||
enum CommandExecutionType {
|
||||
COMMAND_PENDING,
|
||||
COMMAND_EXECUTED,
|
||||
COMMAND_COMPLETED
|
||||
enum AUTH_ERROR {
|
||||
SUCCESS,
|
||||
PASSWORD_CHANGE_REQUIRED,
|
||||
PASSWORD_DOES_NOT_MATCH,
|
||||
PASSWORD_ALREADY_USED,
|
||||
USERNAME_PENDING_VERIFICATION,
|
||||
PASSWORD_INVALID
|
||||
};
|
||||
|
||||
static Storage *instance() {
|
||||
@@ -52,6 +55,10 @@ namespace uCentral {
|
||||
int Start() override;
|
||||
void Stop() override;
|
||||
|
||||
int CreateUser(const std::string & Admin, const std::string &UserName, const std::string &Password);
|
||||
bool DeleteUser(const std::string & Admin, const std::string &UserName);
|
||||
bool ChangePassword(const std::string & Admin, const std::string &UserName, const std::string &OldPassword, const std::string &NewPassword);
|
||||
|
||||
bool IdentityExists(std::string & Identity, AuthService::ACCESS_TYPE Type);
|
||||
bool AddIdentity(std::string & Identity, std::string & Password, AuthService::ACCESS_TYPE Type, uCentral::Objects::AclTemplate & ACL);
|
||||
bool GetIdentity(std::string & Identity, std::string & Password,AuthService::ACCESS_TYPE Type, uCentral::Objects::AclTemplate & ACL);
|
||||
@@ -86,6 +93,8 @@ namespace uCentral {
|
||||
#endif
|
||||
|
||||
int Create_Tables();
|
||||
int Create_UserTable();
|
||||
int Create_APIKeyTable();
|
||||
|
||||
int Setup_SQLite();
|
||||
[[nodiscard]] std::string ConvertParams(const std::string &S) const;
|
||||
|
||||
110
src/Utils.cpp
110
src/Utils.cpp
@@ -6,6 +6,8 @@
|
||||
// Arilia Wireless Inc.
|
||||
//
|
||||
#include <stdexcept>
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
@@ -17,8 +19,10 @@
|
||||
#include "Poco/StringTokenizer.h"
|
||||
#include "Poco/Logger.h"
|
||||
#include "Poco/Message.h"
|
||||
#include "Poco/File.h"
|
||||
|
||||
#include "uCentralProtocol.h"
|
||||
#include "Daemon.h"
|
||||
|
||||
namespace uCentral::Utils {
|
||||
|
||||
@@ -279,4 +283,110 @@ namespace uCentral::Utils {
|
||||
}
|
||||
}
|
||||
|
||||
bool SerialNumberMatch(const std::string &S1, const std::string &S2, int Bits) {
|
||||
auto S1_i = SerialNumberToInt(S1);
|
||||
auto S2_i = SerialNumberToInt(S2);
|
||||
return ((S1_i>>Bits)==(S2_i>>Bits));
|
||||
}
|
||||
|
||||
uint64_t SerialNumberToInt(const std::string & S) {
|
||||
uint64_t R=0;
|
||||
|
||||
for(const auto &i:S)
|
||||
if(i>='0' && i<='9') {
|
||||
R <<= 4;
|
||||
R += (i-'0');
|
||||
} else if(i>='a' && i<='f') {
|
||||
R <<= 4;
|
||||
R += (i-'a') + 10 ;
|
||||
} else if(i>='A' && i<='F') {
|
||||
R <<= 4;
|
||||
R += (i-'A') + 10 ;
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
uint64_t SerialNumberToOUI(const std::string & S) {
|
||||
uint64_t Result = 0 ;
|
||||
int Digits=0;
|
||||
|
||||
for(const auto &i:S) {
|
||||
if(std::isxdigit(i)) {
|
||||
if(i>='0' && i<='9') {
|
||||
Result <<=4;
|
||||
Result += i-'0';
|
||||
} else if(i>='A' && i<='F') {
|
||||
Result <<=4;
|
||||
Result += i-'A'+10;
|
||||
} else if(i>='a' && i<='f') {
|
||||
Result <<=4;
|
||||
Result += i-'a'+10;
|
||||
}
|
||||
Digits++;
|
||||
if(Digits==6)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
uint64_t GetDefaultMacAsInt64() {
|
||||
uint64_t Result=0;
|
||||
auto IFaceList = Poco::Net::NetworkInterface::list();
|
||||
|
||||
for(const auto &iface:IFaceList) {
|
||||
if(iface.isRunning() && !iface.isLoopback()) {
|
||||
auto MAC = iface.macAddress();
|
||||
for (auto const &i : MAC) {
|
||||
Result <<= 8;
|
||||
Result += (uint8_t)i;
|
||||
}
|
||||
if (Result != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
void SaveSystemId(uint64_t Id) {
|
||||
try {
|
||||
std::ofstream O;
|
||||
O.open(Daemon()->DataDir() + "/system.id",std::ios::binary | std::ios::trunc);
|
||||
O << Id;
|
||||
O.close();
|
||||
} catch (...)
|
||||
{
|
||||
std::cout << "Could not save system ID" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t InitializeSystemId() {
|
||||
uint64_t R = ~ std::rand();
|
||||
auto S = GetDefaultMacAsInt64() ^ R;
|
||||
SaveSystemId(S);
|
||||
return S;
|
||||
}
|
||||
|
||||
uint64_t GetSystemId() {
|
||||
uint64_t ID=0;
|
||||
|
||||
// if the system ID file exists, open and read it.
|
||||
Poco::File SID( Daemon()->DataDir() + "/system.id");
|
||||
try {
|
||||
if (SID.exists()) {
|
||||
std::ifstream I;
|
||||
I.open(SID.path());
|
||||
I >> ID;
|
||||
I.close();
|
||||
if (ID == 0)
|
||||
return InitializeSystemId();
|
||||
return ID;
|
||||
} else {
|
||||
return InitializeSystemId();
|
||||
}
|
||||
} catch (...) {
|
||||
return InitializeSystemId();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "Poco/Net/NetworkInterface.h"
|
||||
|
||||
namespace uCentral::Utils {
|
||||
|
||||
[[nodiscard]] std::vector<std::string> Split(const std::string &List, char Delimiter=',');
|
||||
@@ -37,5 +39,12 @@ namespace uCentral::Utils {
|
||||
|
||||
[[nodiscard]] bool ValidSerialNumber(const std::string &Serial);
|
||||
[[nodiscard]] std::string LogLevelToString(int Level);
|
||||
|
||||
[[nodiscard]] bool SerialNumberMatch(const std::string &S1, const std::string &S2, int extrabits=2);
|
||||
[[nodiscard]] uint64_t SerialNumberToInt(const std::string & S);
|
||||
[[nodiscard]] uint64_t SerialNumberToOUI(const std::string & S);
|
||||
|
||||
[[nodiscard]] uint64_t GetDefaultMacAsInt64();
|
||||
[[nodiscard]] uint64_t GetSystemId();
|
||||
}
|
||||
#endif // UCENTRALGW_UTILS_H
|
||||
|
||||
@@ -9,4 +9,85 @@ namespace uCentral {
|
||||
int Storage::Create_Tables() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Storage::Create_UserTable() {
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
|
||||
try {
|
||||
|
||||
if (dbType_ == mysql) {
|
||||
Sess << "CREATE TABLE IF NOT EXISTS Users ("
|
||||
"Id Id unique primary key, "
|
||||
"name varchar, "
|
||||
"description varchar, "
|
||||
"avatar varchar, "
|
||||
"email varchar, "
|
||||
"validated int, "
|
||||
"validationEmail varchar, "
|
||||
"validationDate bigint, "
|
||||
"creationDate bigint, "
|
||||
"validationURI text, "
|
||||
"changePassword int, "
|
||||
"lastLogin bigint, "
|
||||
"currentLoginURI varchar, "
|
||||
"lastPasswordChange bigint, "
|
||||
"lastEmailCheck bigint, "
|
||||
"currentPassword varchar, "
|
||||
"lastPasswords varchar,"
|
||||
"waitingForEmailCheck int, "
|
||||
"locale varchar, "
|
||||
"notes text, "
|
||||
"location text, "
|
||||
"owner varchar, "
|
||||
"suspended int, "
|
||||
"blackListed int, "
|
||||
"userType varchar, "
|
||||
"userTypeProprietaryInfo text"
|
||||
" ,INDEX emailindex (email ASC)"
|
||||
" ,INDEX nameindex (name ASC))",
|
||||
Poco::Data::Keywords::now;
|
||||
} else {
|
||||
Sess << "CREATE TABLE IF NOT EXISTS Users ("
|
||||
"Id Id unique primary key, "
|
||||
"name varchar, "
|
||||
"description varchar, "
|
||||
"avatar varchar, "
|
||||
"email varchar, "
|
||||
"validated int, "
|
||||
"validationEmail varchar, "
|
||||
"validationDate bigint, "
|
||||
"creationDate bigint, "
|
||||
"validationURI text, "
|
||||
"changePassword int, "
|
||||
"lastLogin bigint, "
|
||||
"currentLoginURI varchar, "
|
||||
"lastPasswordChange bigint, "
|
||||
"lastEmailCheck bigint, "
|
||||
"currentPassword varchar, "
|
||||
"lastPasswords varchar,"
|
||||
"waitingForEmailCheck int, "
|
||||
"locale varchar, "
|
||||
"notes text, "
|
||||
"location text, "
|
||||
"owner varchar, "
|
||||
"suspended int, "
|
||||
"blackListed int, "
|
||||
"userType varchar, "
|
||||
"userTypeProprietaryInfo text"
|
||||
")",
|
||||
Poco::Data::Keywords::now;
|
||||
Sess << "CREATE INDEX IF NOT EXISTS emailindex ON Users (email ASC)", Poco::Data::Keywords::now;
|
||||
Sess << "CREATE INDEX IF NOT EXISTS nameindex ON Users (name ASC)", Poco::Data::Keywords::now;
|
||||
}
|
||||
return 0;
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger_.log(E);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Storage::Create_APIKeyTable() {
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,12 @@ ucentral.system.debug = true
|
||||
ucentral.system.uri = https://localhost:16001
|
||||
ucentral.system.id = 1
|
||||
ucentral.system.commandchannel = /tmp/app.ucentralgw
|
||||
|
||||
|
||||
mailer.hostname = smtp.gmail.com
|
||||
mailer.username = no-reply@arilia.com
|
||||
mailer.password = pink-elephants-play-hockey
|
||||
mailer.loginmethod = login
|
||||
mailer.port = 587
|
||||
|
||||
#
|
||||
# Kafka
|
||||
@@ -82,7 +87,7 @@ storage.type.mysql.connectiontimeout = 60
|
||||
#
|
||||
authentication.enabled = true
|
||||
authentication.default.username = tip@ucentral.com
|
||||
authentication.default.password = openwifi
|
||||
authentication.default.password = 13268b7daa751240369d125e79c873bd8dd3bef7981bdfd38ea03dbb1fbe7dcf
|
||||
authentication.default.access = master
|
||||
authentication.service.type = internal
|
||||
|
||||
@@ -90,9 +95,13 @@ system.directory.data = $UCENTRALSEC_ROOT/data
|
||||
|
||||
ucentral.system.debug = true
|
||||
ucentral.system.uri = https://localhost:16001
|
||||
ucentral.system.id = 1
|
||||
ucentral.system.commandchannel = /tmp/app.ucentralgw
|
||||
|
||||
# email.includeonly = mydomain.com
|
||||
# email.exclude = gmail.com
|
||||
|
||||
|
||||
|
||||
########################################################################
|
||||
########################################################################
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user