Skip to content

Commit

Permalink
Fixes #24872: Rework api authorization models
Browse files Browse the repository at this point in the history
  • Loading branch information
VinceMacBuche committed May 17, 2024
1 parent 1dec01e commit 7703cc9
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 162 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ trait EndpointSchema {
// data container name: the expected object key in answer
def dataContainer: Option[String]

// any authorization that allows to access that API - by default, admin.write
def authz: List[AuthorizationType] = List(AuthorizationType.Administration.Write)
// any authorization that allows to access that API
def authz: List[AuthorizationType]
}

trait EndpointSchema0 extends EndpointSchema {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@

package com.normation.rudder.rest

import com.normation.rudder.AuthorizationType
import com.normation.rudder.Rights
import com.normation.rudder.Role
import com.normation.rudder.api.AclPathSegment
import com.normation.rudder.{AuthorizationType, Rights, Role}
import com.normation.rudder.api.ApiAclElement
import com.normation.rudder.api.ApiAuthorization as ApiAuthz

Expand All @@ -61,6 +58,7 @@ class AuthorizationMappingListEndpoint(endpoints: List[EndpointSchema]) extends

override def mapAuthorization(authz: AuthorizationType): List[ApiAclElement] = {
acls.get(authz).getOrElse(Nil)

}
}

Expand All @@ -73,14 +71,17 @@ class ExtensibleAuthorizationApiMapping(base: List[AuthorizationApiMapping]) ext
private var mappers: List[AuthorizationApiMapping] = base

def addMapper(mapper: AuthorizationApiMapping): Unit = {
// no need to add again and again the default mapper - it's ok, we have it.
if (mapper != AuthorizationApiMapping.OnlyAdmin) {
mappers = mappers :+ mapper
}
}

override def mapAuthorization(authz: AuthorizationType): List[ApiAclElement] = {
mappers.flatMap(_.mapAuthorization(authz))
import AuthorizationType.*
authz match {
case NoRights => Nil
case AnyRights => ApiAuthz.allAuthz.acl
case _ =>
mappers.flatMap(_.mapAuthorization(authz))
}
}
}

Expand All @@ -89,148 +90,6 @@ object AuthorizationApiMapping {
def x: ApiAclElement = AuthzForApi(api)
}

/*
* A default mapping for "only 'all rights' (ie admin) can access it
*/
case object OnlyAdmin extends AuthorizationApiMapping {
override def mapAuthorization(authz: AuthorizationType): List[ApiAclElement] = Nil
}

/*
* The core authorization/api mapping, ie the authorization for Rudder
* default API.
*/
object Core extends AuthorizationApiMapping {

override def mapAuthorization(authz: AuthorizationType): List[ApiAclElement] = {
import AuthorizationType.*
// shorthand to get authz for a given api
authz match {
case NoRights => Nil
case AnyRights => ApiAuthz.allAuthz.acl
// Administration is Rudder setting

case Administration.Read =>
SettingsApi.GetAllSettings.x :: SettingsApi.GetSetting.x :: SystemApi.ArchivesDirectivesList.x ::
SystemApi.ArchivesFullList.x :: SystemApi.ArchivesGroupsList.x :: SystemApi.ArchivesRulesList.x ::
SystemApi.GetAllZipArchive.x :: SystemApi.GetDirectivesZipArchive.x :: SystemApi.GetGroupsZipArchive.x ::
SystemApi.GetRulesZipArchive.x :: SystemApi.Info.x :: SystemApi.Status.x :: SystemApi.ArchivesParametersList.x ::
SystemApi.GetParametersZipArchive.x :: SystemApi.GetHealthcheckResult.x :: PluginApi.GetPluginsSettings.x ::
SettingsApi.GetAllowedNetworks.x :: SettingsApi.GetAllAllowedNetworks.x :: HookApi.GetHooks.x :: InfoApi.endpoints.map(
_.x
)
case Administration.Write =>
PluginApi.UpdatePluginsSettings.x :: SettingsApi.ModifySettings.x :: SettingsApi.ModifySetting.x ::
InventoryApi.FileWatcherRestart.x :: InventoryApi.FileWatcherStart.x :: InventoryApi.FileWatcherStop.x ::
NodeApi.CreateNodes.x :: SystemApi.endpoints.map(_.x)
case Administration.Edit =>
PluginApi.UpdatePluginsSettings.x :: SettingsApi.ModifySettings.x :: SettingsApi.ModifySetting.x ::
SettingsApi.ModifyAllowedNetworks.x :: SettingsApi.ModifyDiffAllowedNetworks.x ::
Nil

case Compliance.Read =>
ComplianceApi.GetGlobalCompliance.x :: ComplianceApi.GetRulesCompliance.x :: ComplianceApi.GetRulesComplianceId.x ::
ComplianceApi.GetNodesCompliance.x :: ComplianceApi.GetNodeComplianceId.x :: ChangesApi.GetRuleRepairedReports.x ::
ChangesApi.GetRecentChanges.x :: ComplianceApi.GetDirectiveComplianceId.x :: ComplianceApi.GetNodeSystemCompliance.x ::
ComplianceApi.GetDirectivesCompliance.x :: ComplianceApi.GetNodeGroupComplianceId.x :: ComplianceApi.GetNodeGroupComplianceTargetId.x ::
ComplianceApi.GetNodeGroupComplianceSummary.x :: Nil
case Compliance.Write => Nil
case Compliance.Edit => Nil

case Configuration.Read =>
(Parameter.Read :: Technique.Read :: Directive.Read :: Rule.Read :: Nil).flatMap(c => mapAuthorization(c))
case Configuration.Write =>
(Parameter.Write :: Technique.Write :: Directive.Write :: Rule.Write :: Nil).flatMap(c => mapAuthorization(c))
case Configuration.Edit =>
(Parameter.Edit :: Technique.Edit :: Directive.Edit :: Rule.Edit :: Nil).flatMap(c => mapAuthorization(c))

case Deployment.Read => Nil
case Deployment.Write => Nil
case Deployment.Edit => Nil

case Deployer.Read => Nil // ChangeRequestApi.ListChangeRequests.x :: ChangeRequestApi.ChangeRequestsDetails.x :: Nil
case Deployer.Write => Nil // ChangeRequestApi.DeclineRequestsDetails.x :: ChangeRequestApi.AcceptRequestsDetails.x :: Nil
case Deployer.Edit => Nil // ChangeRequestApi.UpdateRequestsDetails.x :: Nil

case Parameter.Read => ParameterApi.ListParameters.x :: ParameterApi.ParameterDetails.x :: Nil
case Parameter.Write => ParameterApi.CreateParameter.x :: ParameterApi.DeleteParameter.x :: Nil
case Parameter.Edit => ParameterApi.UpdateParameter.x :: Nil

case Directive.Read =>
DirectiveApi.ListDirectives.x :: DirectiveApi.DirectiveDetails.x ::
DirectiveApi.DirectiveTree.x :: DirectiveApi.DirectiveRevisions.x ::
Nil
case Directive.Write =>
DirectiveApi.CreateDirective.x :: DirectiveApi.DeleteDirective.x ::
DirectiveApi.CheckDirective.x :: Nil
case Directive.Edit => DirectiveApi.UpdateDirective.x :: Nil

case Group.Read =>
GroupApi.ListGroups.x :: GroupApi.GroupDetails.x :: GroupApi.GetGroupTree.x ::
GroupApi.GetGroupCategoryDetails.x :: GroupApi.GroupInheritedProperties.x ::
NodeApi.NodeDetailsTable.x :: GroupApi.GroupDisplayInheritedProperties.x ::
GroupInternalApi.GetGroupCategoryTree.x :: Nil
case Group.Write =>
GroupApi.CreateGroup.x :: GroupApi.DeleteGroup.x :: GroupApi.ReloadGroup.x ::
GroupApi.DeleteGroupCategory.x :: GroupApi.CreateGroupCategory.x :: Nil
case Group.Edit => GroupApi.UpdateGroup.x :: GroupApi.UpdateGroupCategory.x :: Nil

case Node.Read =>
NodeApi.ListAcceptedNodes.x :: NodeApi.ListPendingNodes.x :: NodeApi.NodeDetails.x ::
NodeApi.NodeInheritedProperties.x :: NodeApi.NodeDisplayInheritedProperties.x :: NodeApi.NodeDetailsTable.x ::
NodeApi.PendingNodeDetails.x :: NodeApi.NodeDetailsSoftware.x :: NodeApi.NodeDetailsProperty.x ::
NodeApi.GetNodesStatus.x :: InventoryApi.QueueInformation.x ::
NodeApi.NodeGlobalScore.x :: NodeApi.NodeScoreDetail.x :: NodeApi.NodeScoreDetails.x ::
NodeApi.GetNodesStatus.x ::
// score about node
NodeApi.NodeGlobalScore.x :: NodeApi.NodeScoreDetails.x :: NodeApi.NodeScoreDetail.x ::
InventoryApi.QueueInformation.x ::
// node read also allows to read some settings
AuthzForApi.withValues(SettingsApi.GetSetting, AclPathSegment.Segment("global_policy_mode") :: Nil) ::
AuthzForApi.withValues(SettingsApi.GetSetting, AclPathSegment.Segment("global_policy_mode_overridable") :: Nil) ::
ScoreApi.GetScoreList.x ::
Nil
case Node.Write =>
NodeApi.DeleteNode.x :: NodeApi.ChangePendingNodeStatus.x :: NodeApi.ChangePendingNodeStatus2.x ::
NodeApi.ApplyPolicyAllNodes.x :: NodeApi.ApplyPolicy.x :: Nil
case Node.Edit => NodeApi.UpdateNode.x :: InventoryApi.UploadInventory.x :: Nil

case Rule.Read =>
RuleApi.ListRules.x :: RuleApi.RuleDetails.x :: RuleApi.GetRuleTree.x ::
RuleApi.GetRuleCategoryDetails.x :: RuleInternalApi.GetRuleNodesAndDirectives.x ::
RuleInternalApi.GetGroupRelatedRules.x ::
Nil
case Rule.Write =>
RuleApi.CreateRule.x :: RuleApi.DeleteRule.x :: RuleApi.CreateRuleCategory.x ::
RuleApi.DeleteRuleCategory.x :: RuleApi.LoadRuleRevisionForGeneration.x :: RuleApi.UnloadRuleRevisionForGeneration.x ::
Nil
case Rule.Edit => RuleApi.UpdateRule.x :: RuleApi.UpdateRuleCategory.x :: Nil

case Technique.Read =>
TechniqueApi.ListTechniques.x :: TechniqueApi.ListTechniquesDirectives.x ::
TechniqueApi.ListTechniqueDirectives.x :: TechniqueApi.TechniqueRevisions.x ::
TechniqueApi.GetMethods.x :: TechniqueApi.GetTechniques.x ::
TechniqueApi.GetAllTechniqueCategories.x :: TechniqueApi.GetResources.x :: TechniqueApi.GetNewResources.x ::
TechniqueApi.GetTechniqueAllVersion.x :: TechniqueApi.GetTechnique.x :: Nil
case Technique.Write =>
TechniqueApi.CreateTechnique.x :: SystemApi.PoliciesUpdate.x :: SystemApi.PoliciesRegenerate.x ::
TechniqueApi.DeleteTechnique.x :: Nil
case Technique.Edit =>
TechniqueApi.UpdateTechnique.x :: SystemApi.PoliciesUpdate.x :: SystemApi.PoliciesRegenerate.x ::
TechniqueApi.UpdateTechniques.x :: TechniqueApi.UpdateMethods.x :: Nil

case UserAccount.Read => UserApi.GetApiToken.x :: Nil
case UserAccount.Write => UserApi.CreateApiToken.x :: UserApi.DeleteApiToken.x :: Nil
case UserAccount.Edit => UserApi.UpdateApiToken.x :: Nil

case Validator.Read => Nil // ChangeRequestApi.ListChangeRequests.x :: ChangeRequestApi.ChangeRequestsDetails.x :: Nil
case Validator.Write =>
Nil // ChangeRequestApi.DeclineRequestsDetails.x :: ChangeRequestApi.AcceptRequestsDetails.x :: Nil
case Validator.Edit => Nil // ChangeRequestApi.UpdateRequestsDetails.x :: Nil
case _ => Nil // Done within plugin
}
}
}
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ object UserManagementApi extends Enum[UserManagementApi] with ApiModuleProvider[
val z = implicitly[Line].value
val description = "Reload (read again rudder-users.xml and process result) information about registered users in Rudder"
val (action, path) = POST / "usermanagement" / "users" / "reload"

override def dataContainer: Option[String] = None
val authz: List[AuthorizationType] = AuthorizationType.UserAccount.Write :: Nil
}

final case object DeleteUser extends UserManagementApi with OneParam with StartsAtVersion10 {
Expand All @@ -138,6 +138,8 @@ object UserManagementApi extends Enum[UserManagementApi] with ApiModuleProvider[
val (action, path) = DELETE / "usermanagement" / "{username}"

override def dataContainer: Option[String] = None

val authz: List[AuthorizationType] = AuthorizationType.UserAccount.Write :: Nil
}

final case object AddUser extends UserManagementApi with ZeroParam with StartsAtVersion10 {
Expand All @@ -146,6 +148,8 @@ object UserManagementApi extends Enum[UserManagementApi] with ApiModuleProvider[
val (action, path) = POST / "usermanagement"

override def dataContainer: Option[String] = None

val authz: List[AuthorizationType] = AuthorizationType.UserAccount.Write :: Nil
}

final case object UpdateUser extends UserManagementApi with OneParam with StartsAtVersion10 {
Expand All @@ -154,6 +158,8 @@ object UserManagementApi extends Enum[UserManagementApi] with ApiModuleProvider[
val (action, path) = POST / "usermanagement" / "update" / "{username}"

override def dataContainer: Option[String] = None

val authz: List[AuthorizationType] = AuthorizationType.UserAccount.Write :: Nil
}

final case object UpdateUserInfo extends UserManagementApi with OneParam with StartsAtVersion10 {
Expand All @@ -162,6 +168,8 @@ object UserManagementApi extends Enum[UserManagementApi] with ApiModuleProvider[
val (action, path) = POST / "usermanagement" / "update" / "info" / "{username}"

override def dataContainer: Option[String] = None

val authz: List[AuthorizationType] = AuthorizationType.UserAccount.Write :: Nil
}

final case object ActivateUser extends UserManagementApi with OneParam with StartsAtVersion10 {
Expand All @@ -170,6 +178,8 @@ object UserManagementApi extends Enum[UserManagementApi] with ApiModuleProvider[
val (action, path) = PUT / "usermanagement" / "status" / "activate" / "{username}"

override def dataContainer: Option[String] = None

val authz: List[AuthorizationType] = AuthorizationType.UserAccount.Write :: Nil
}

final case object DisableUser extends UserManagementApi with OneParam with StartsAtVersion10 {
Expand All @@ -178,6 +188,8 @@ object UserManagementApi extends Enum[UserManagementApi] with ApiModuleProvider[
val (action, path) = PUT / "usermanagement" / "status" / "disable" / "{username}"

override def dataContainer: Option[String] = None

val authz: List[AuthorizationType] = AuthorizationType.UserAccount.Write :: Nil
}

final case object RoleCoverage extends UserManagementApi with OneParam with StartsAtVersion10 {
Expand All @@ -186,6 +198,8 @@ object UserManagementApi extends Enum[UserManagementApi] with ApiModuleProvider[
val (action, path) = POST / "usermanagement" / "coverage" / "{username}"

override def dataContainer: Option[String] = None

val authz: List[AuthorizationType] = AuthorizationType.UserAccount.Read :: Nil
}

def endpoints = values.toList.sortBy(_.z)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,7 @@ import com.normation.rudder.repository.FullNodeGroupCategory
import com.normation.rudder.repository.RoNodeGroupRepository
import com.normation.rudder.repository.RoRuleRepository
import com.normation.rudder.repository.WoRuleRepository
import com.normation.rudder.rest.AuthorizationApiMapping
import com.normation.rudder.rest.ProviderRoleExtension
import com.normation.rudder.rest.RoleApiMapping
import com.normation.rudder.rest.{ExtensibleAuthorizationApiMapping, ProviderRoleExtension, RoleApiMapping}
import com.normation.rudder.rest.lift.ComplianceAPIService
import com.normation.rudder.rule.category.RuleCategoryId
import com.normation.rudder.services.policies.NodeConfigData
Expand All @@ -134,12 +132,14 @@ import com.normation.rudder.users.UserSession
import com.normation.rudder.users.UserStatus
import com.normation.zio.UnsafeRun
import com.typesafe.config.ConfigFactory

import java.io.InputStream
import java.nio.charset.StandardCharsets
import net.liftweb.common.Box
import net.liftweb.common.Full
import org.apache.commons.io.IOUtils
import org.joda.time.DateTime

import scala.collection.MapView
import scala.collection.immutable.SortedMap
import zio.*
Expand Down Expand Up @@ -858,7 +858,7 @@ class MockUserManagement(userInfos: List[UserInfo], userSessions: List[UserSessi
val userService: FileUserDetailListProvider = {
val usersFile = UserFile("test-users.xml", usersInputStream)

val roleApiMapping = new RoleApiMapping(AuthorizationApiMapping.Core)
val roleApiMapping = new RoleApiMapping(new ExtensibleAuthorizationApiMapping(Nil))

val res = new FileUserDetailListProvider(roleApiMapping, usersFile)
res.reload()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,7 @@ class RestTestSetUp {
mockUserManagement.userRepo,
mockUserManagement.userService,
mockUserManagement.userManagementService,
new RoleApiMapping(new ExtensibleAuthorizationApiMapping(AuthorizationApiMapping.Core :: Nil)),
new RoleApiMapping(new ExtensibleAuthorizationApiMapping(Nil)),
() => mockUserManagement.providerRoleExtension,
() => mockUserManagement.authBackendProviders
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ import com.normation.rudder.domain.eventlog.LogoutEventLog
import com.normation.rudder.domain.logger.ApplicationLogger
import com.normation.rudder.domain.logger.ApplicationLoggerPure
import com.normation.rudder.domain.logger.PluginLogger
import com.normation.rudder.rest.ApiModuleProvider
import com.normation.rudder.rest.EndpointSchema
import com.normation.rudder.rest.InfoApi as InfoApiDef
import com.normation.rudder.rest.{ApiModuleProvider, AuthorizationMappingListEndpoint, EndpointSchema, InfoApi as InfoApiDef}
import com.normation.rudder.rest.lift.InfoApi
import com.normation.rudder.rest.lift.LiftApiModuleProvider
import com.normation.rudder.rest.v1.RestStatus
Expand All @@ -70,6 +68,7 @@ import com.normation.rudder.web.snippet.WithCachedResource
import com.normation.rudder.web.snippet.WithNonce
import com.normation.utils.DateFormaterService
import com.normation.zio.*

import java.net.URI
import java.net.URLConnection
import java.util.Locale
Expand All @@ -88,6 +87,7 @@ import org.joda.time.DateTime
import org.reflections.Reflections
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContextHolder

import scala.concurrent.duration.DAYS
import scala.concurrent.duration.Duration
import scala.util.chaining.*
Expand Down Expand Up @@ -541,6 +541,8 @@ class Boot extends Loggable {
new InfoApi(RudderConfig.restExtractorService, RudderConfig.ApiVersions, endpoints)
}
RudderConfig.rudderApi.addModules(infoApi.getLiftEndpoints())
val apiRoleMapper = new AuthorizationMappingListEndpoint(RudderConfig.rudderApi.apis().map(_.schema))
RudderConfig.authorizationApiMapping.addMapper(apiRoleMapper)
LiftRules.statelessDispatch.append(RudderConfig.rudderApi.getLiftRestApi())

// URL rewrites
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1515,7 +1515,7 @@ object RudderConfigInit {
lazy val authenticationProviders = new AuthBackendProvidersManager()

// Plugin input interface for Authorization for API
lazy val authorizationApiMapping = new ExtensibleAuthorizationApiMapping(AuthorizationApiMapping.Core :: Nil)
lazy val authorizationApiMapping = new ExtensibleAuthorizationApiMapping(Nil)

////////// end pluggable service providers //////////

Expand Down

0 comments on commit 7703cc9

Please sign in to comment.