/*
// Copyright (c) 2018 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

#include "user_layer.hpp"

#include "passwd_mgr.hpp"
#include "user_mgmt.hpp"

namespace
{
ipmi::PasswdMgr passwdMgr;
}

namespace ipmi
{

Cc ipmiUserInit()
{
    getUserAccessObject();
    return ccSuccess;
}

SecureString ipmiUserGetPassword(const std::string& userName)
{
    return passwdMgr.getPasswdByUserName(userName);
}

Cc ipmiClearUserEntryPassword(const std::string& userName)
{
    if (passwdMgr.updateUserEntry(userName, "") != 0)
    {
        return ccUnspecifiedError;
    }
    return ccSuccess;
}

Cc ipmiRenameUserEntryPassword(const std::string& userName,
                               const std::string& newUserName)
{
    if (passwdMgr.updateUserEntry(userName, newUserName) != 0)
    {
        return ccUnspecifiedError;
    }
    return ccSuccess;
}

bool ipmiUserIsValidUserId(const uint8_t userId)
{
    return UserAccess::isValidUserId(userId);
}

bool ipmiUserIsValidPrivilege(const uint8_t priv)
{
    return UserAccess::isValidPrivilege(priv);
}

uint8_t ipmiUserGetUserId(const std::string& userName)
{
    return getUserAccessObject().getUserId(userName);
}

Cc ipmiUserSetUserName(const uint8_t userId, const char* userName)
{
    std::string newUser(userName, 0, ipmiMaxUserName);
    return getUserAccessObject().setUserName(userId, newUser);
}

Cc ipmiUserSetUserName(const uint8_t userId, const std::string& userName)
{
    std::string newUser(userName, 0, ipmiMaxUserName);
    return getUserAccessObject().setUserName(userId, newUser);
}

Cc ipmiUserGetUserName(const uint8_t userId, std::string& userName)
{
    return getUserAccessObject().getUserName(userId, userName);
}

Cc ipmiUserSetUserPassword(const uint8_t userId, const char* userPassword)
{
    return getUserAccessObject().setUserPassword(userId, userPassword);
}

Cc ipmiSetSpecialUserPassword(const std::string& userName,
                              const SecureString& userPassword)
{
    return getUserAccessObject().setSpecialUserPassword(userName, userPassword);
}

Cc ipmiUserGetAllCounts(uint8_t& maxChUsers, uint8_t& enabledUsers,
                        uint8_t& fixedUsers)
{
    maxChUsers = ipmiMaxUsers;
    UsersTbl* userData = getUserAccessObject().getUsersTblPtr();
    enabledUsers = 0;
    fixedUsers = 0;
    // user index 0 is reserved, starts with 1
    for (size_t count = 1; count <= ipmiMaxUsers; ++count)
    {
        if (userData->user[count].userEnabled)
        {
            enabledUsers++;
        }
        if (userData->user[count].fixedUserName)
        {
            fixedUsers++;
        }
    }
    return ccSuccess;
}

Cc ipmiUserUpdateEnabledState(const uint8_t userId, const bool& state)
{
    return getUserAccessObject().setUserEnabledState(userId, state);
}

Cc ipmiUserCheckEnabled(const uint8_t userId, bool& state)
{
    if (!UserAccess::isValidUserId(userId))
    {
        return ccParmOutOfRange;
    }
    UserInfo* userInfo = getUserAccessObject().getUserInfo(userId);
    state = userInfo->userEnabled;
    return ccSuccess;
}

Cc ipmiUserGetPrivilegeAccess(const uint8_t userId, const uint8_t chNum,
                              PrivAccess& privAccess)
{
    if (!UserAccess::isValidChannel(chNum))
    {
        return ccInvalidFieldRequest;
    }
    if (!UserAccess::isValidUserId(userId))
    {
        return ccParmOutOfRange;
    }
    UserInfo* userInfo = getUserAccessObject().getUserInfo(userId);
    privAccess.privilege = userInfo->userPrivAccess[chNum].privilege;
    privAccess.ipmiEnabled = userInfo->userPrivAccess[chNum].ipmiEnabled;
    privAccess.linkAuthEnabled =
        userInfo->userPrivAccess[chNum].linkAuthEnabled;
    privAccess.accessCallback = userInfo->userPrivAccess[chNum].accessCallback;
    return ccSuccess;
}

Cc ipmiUserSetPrivilegeAccess(const uint8_t userId, const uint8_t chNum,
                              const PrivAccess& privAccess,
                              const bool& otherPrivUpdates)
{
    UserPrivAccess userPrivAccess;
    userPrivAccess.privilege = privAccess.privilege;
    if (otherPrivUpdates)
    {
        userPrivAccess.ipmiEnabled = privAccess.ipmiEnabled;
        userPrivAccess.linkAuthEnabled = privAccess.linkAuthEnabled;
        userPrivAccess.accessCallback = privAccess.accessCallback;
    }
    return getUserAccessObject().setUserPrivilegeAccess(
        userId, chNum, userPrivAccess, otherPrivUpdates);
}

bool ipmiUserPamAuthenticate(std::string_view userName,
                             std::string_view userPassword)
{
    return pamUserCheckAuthenticate(userName, userPassword);
}

Cc ipmiUserSetUserPayloadAccess(const uint8_t chNum, const uint8_t operation,
                                const uint8_t userId,
                                const PayloadAccess& payloadAccess)
{
    if (!UserAccess::isValidChannel(chNum))
    {
        return ccInvalidFieldRequest;
    }
    if (!UserAccess::isValidUserId(userId))
    {
        return ccParmOutOfRange;
    }

    return getUserAccessObject().setUserPayloadAccess(chNum, operation, userId,
                                                      payloadAccess);
}

Cc ipmiUserGetUserPayloadAccess(const uint8_t chNum, const uint8_t userId,
                                PayloadAccess& payloadAccess)
{
    if (!UserAccess::isValidChannel(chNum))
    {
        return ccInvalidFieldRequest;
    }
    if (!UserAccess::isValidUserId(userId))
    {
        return ccParmOutOfRange;
    }

    UserInfo* userInfo = getUserAccessObject().getUserInfo(userId);

    payloadAccess.stdPayloadEnables1 =
        userInfo->payloadAccess[chNum].stdPayloadEnables1;
    payloadAccess.stdPayloadEnables2Reserved =
        userInfo->payloadAccess[chNum].stdPayloadEnables2Reserved;
    payloadAccess.oemPayloadEnables1 =
        userInfo->payloadAccess[chNum].oemPayloadEnables1;
    payloadAccess.oemPayloadEnables2Reserved =
        userInfo->payloadAccess[chNum].oemPayloadEnables2Reserved;

    return ccSuccess;
}

} // namespace ipmi