Skip to content

Commit

Permalink
Merge pull request #83 from atlanhq/APP-5079
Browse files Browse the repository at this point in the history
APP-5079 : Manage Policies
  • Loading branch information
0xquark authored Jan 29, 2025
2 parents 7450911 + b143592 commit a0f4bfc
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 65 deletions.
10 changes: 10 additions & 0 deletions atlan/assets/asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type AssetFields struct {
VIEWER_GROUPS *KeywordField
CONNECTOR_NAME *KeywordTextField
CONNECTION_NAME *KeywordTextField
CONNECTION_QUALIFIED_NAME *KeywordTextField
}

type CatalogFields struct {
Expand Down Expand Up @@ -366,6 +367,7 @@ func NewSearchTable() *AtlasTableFields {
VIEWER_USERS: NewKeywordField("viewerUsers", "viewerUsers"),
VIEWER_GROUPS: NewKeywordField("viewerGroups", "viewerGroups"),
CONNECTOR_NAME: NewKeywordTextField("connectorName", "connectorName", "connectorName.text"),
CONNECTION_QUALIFIED_NAME: NewKeywordTextField("connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text"),
},
INPUT_TO_PROCESSES: NewRelationField("inputToProcesses"),
OUTPUT_FROM_AIRFLOW_TASKS: NewRelationField("outputFromAirflowTasks"),
Expand Down Expand Up @@ -432,6 +434,7 @@ func NewSearchColumn() *ColumnFields {
VIEWER_USERS: NewKeywordField("viewerUsers", "viewerUsers"),
VIEWER_GROUPS: NewKeywordField("viewerGroups", "viewerGroups"),
CONNECTOR_NAME: NewKeywordTextField("connectorName", "connectorName", "connectorName.text"),
CONNECTION_QUALIFIED_NAME: NewKeywordTextField("connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text"),
},
INPUT_TO_PROCESSES: NewRelationField("inputToProcesses"),
OUTPUT_FROM_AIRFLOW_TASKS: NewRelationField("outputFromAirflowTasks"),
Expand Down Expand Up @@ -561,6 +564,7 @@ func NewSearchConnection() *ConnectionFields {
VIEWER_USERS: NewKeywordField("viewerUsers", "viewerUsers"),
VIEWER_GROUPS: NewKeywordField("viewerGroups", "viewerGroups"),
CONNECTOR_NAME: NewKeywordTextField("connectorName", "connectorName", "connectorName.text"),
CONNECTION_QUALIFIED_NAME: NewKeywordTextField("connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text"),
},
CATEGORY: NewKeywordField("category", "category"),
SUB_CATEGORY: NewKeywordField("subCategory", "subCategory"),
Expand Down Expand Up @@ -628,6 +632,7 @@ func NewSearchGlossary() *AtlasGlossaryFields {
VIEWER_USERS: NewKeywordField("viewerUsers", "viewerUsers"),
VIEWER_GROUPS: NewKeywordField("viewerGroups", "viewerGroups"),
CONNECTOR_NAME: NewKeywordTextField("connectorName", "connectorName", "connectorName.text"),
CONNECTION_QUALIFIED_NAME: NewKeywordTextField("connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text"),
},
}
}
Expand Down Expand Up @@ -669,6 +674,7 @@ func NewSearchMaterialisedView() *MaterialisedViewFields {
VIEWER_USERS: NewKeywordField("viewerUsers", "viewerUsers"),
VIEWER_GROUPS: NewKeywordField("viewerGroups", "viewerGroups"),
CONNECTOR_NAME: NewKeywordTextField("connectorName", "connectorName", "connectorName.text"),
CONNECTION_QUALIFIED_NAME: NewKeywordTextField("connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text"),
},
INPUT_TO_PROCESSES: NewRelationField("inputToProcesses"),
OUTPUT_FROM_AIRFLOW_TASKS: NewRelationField("outputFromAirflowTasks"),
Expand Down Expand Up @@ -749,6 +755,7 @@ func NewSearchView() *ViewFields {
VIEWER_USERS: NewKeywordField("viewerUsers", "viewerUsers"),
VIEWER_GROUPS: NewKeywordField("viewerGroups", "viewerGroups"),
CONNECTOR_NAME: NewKeywordTextField("connectorName", "connectorName", "connectorName.text"),
CONNECTION_QUALIFIED_NAME: NewKeywordTextField("connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text"),
},
INPUT_TO_PROCESSES: NewRelationField("inputToProcesses"),
OUTPUT_FROM_AIRFLOW_TASKS: NewRelationField("outputFromAirflowTasks"),
Expand Down Expand Up @@ -835,6 +842,7 @@ func NewAccessControlFields() *AccessControlFields {
VIEWER_USERS: NewKeywordField("viewerUsers", "viewerUsers"),
VIEWER_GROUPS: NewKeywordField("viewerGroups", "viewerGroups"),
CONNECTOR_NAME: NewKeywordTextField("connectorName", "connectorName", "connectorName.text"),
CONNECTION_QUALIFIED_NAME: NewKeywordTextField("connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text"),
},
}
}
Expand Down Expand Up @@ -886,6 +894,7 @@ func NewPersonaFields() *PersonaFields {
VIEWER_USERS: NewKeywordField("viewerUsers", "viewerUsers"),
VIEWER_GROUPS: NewKeywordField("viewerGroups", "viewerGroups"),
CONNECTOR_NAME: NewKeywordTextField("connectorName", "connectorName", "connectorName.text"),
CONNECTION_QUALIFIED_NAME: NewKeywordTextField("connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text"),
},
},
PERSONA_GROUPS: NewKeywordField("personaGroups", "personaGroups"),
Expand Down Expand Up @@ -930,6 +939,7 @@ func NewAuthPolicyFields() *AuthPolicyFields {
VIEWER_USERS: NewKeywordField("viewerUsers", "viewerUsers"),
VIEWER_GROUPS: NewKeywordField("viewerGroups", "viewerGroups"),
CONNECTOR_NAME: NewKeywordTextField("connectorName", "connectorName", "connectorName.text"),
CONNECTION_QUALIFIED_NAME: NewKeywordTextField("connectionQualifiedName", "connectionQualifiedName", "connectionQualifiedName.text"),
},
POLICY_TYPE: NewKeywordField("policyType", "policyType"),
POLICY_SERVICE_NAME: NewKeywordField("policyServiceName", "policyServiceName"),
Expand Down
10 changes: 10 additions & 0 deletions atlan/assets/atlan_fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,16 @@ func NewKeywordField(atlanFieldName, keywordFieldName string) *KeywordField {
}
}

// StartsWith Returns a query that will match all assets whose field has a value that starts with
// the provided value. Note that this can also be a case-insensitive match.
func (kf *KeywordField) StartsWith(value string, caseInsensitive *bool) model.Query {
return &model.PrefixQuery{
Field: kf.KeywordFieldName,
Value: value,
CaseInsensitive: caseInsensitive,
}
}

func (kf *KeywordField) GetKeywordFieldName() string {
return kf.KeywordFieldName
}
Expand Down
106 changes: 68 additions & 38 deletions atlan/assets/auth_policy_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,53 +21,77 @@ func (a *AuthPolicy) Updater(name string, qualifiedName string) error {
return nil
}

// UnmarshalJSON unmarshal a AuthPolicy from JSON.
func (a *AuthPolicy) UnmarshalJSON(data []byte) error {
// Define a temporary structure with the expected JSON structure.
var temp struct {
ReferredEntities map[string]interface{} `json:"referredEntities"`
Entity struct {
TypeName string `json:"typeName"`
AttributesJSON json.RawMessage `json:"attributes"`
Guid string `json:"guid"`
IsIncomplete bool `json:"isIncomplete"`
Status atlan.AtlanStatus `json:"status"`
CreatedBy string `json:"createdBy"`
UpdatedBy string `json:"updatedBy"`
CreateTime int64 `json:"createTime"`
UpdateTime int64 `json:"updateTime"`
Version int `json:"version"`
RelationshipAttributes struct {
SchemaRegistrySubjects []structs.SchemaRegistrySubject `json:"schemaRegistrySubjects"`
McMonitors []structs.MCMonitor `json:"mcMonitors"`
Terms []structs.AtlasGlossaryTerm `json:"terms"`
OutputPortDataProducts []string `json:"outputPortDataProducts"`
AtlasGlossary []structs.AtlasGlossary `json:"AtlasGlossary"`
AccessControl []structs.AccessControl `json:"AccessControl"`
Policies []structs.AuthPolicy `json:"policies"`
} `json:"relationshipAttributes"`
}
}

// Unmarshal the JSON data into the temporary structure.
if err := json.Unmarshal(data, &temp); err != nil {
attributes := struct {
// Base attributes
QualifiedName *string `json:"qualifiedName,omitempty"`
Name *string `json:"name,omitempty"`

// AuthPolicy specific attributes
PolicyType *atlan.AuthPolicyType `json:"policyType,omitempty"`
PolicyServiceName *string `json:"policyServiceName,omitempty"`
PolicyCategory *string `json:"policyCategory,omitempty"`
PolicySubCategory *string `json:"policySubCategory,omitempty"`
PolicyUsers *[]string `json:"policyUsers,omitempty"`
PolicyGroups *[]string `json:"policyGroups,omitempty"`
PolicyRoles *[]string `json:"policyRoles,omitempty"`
PolicyActions *[]string `json:"policyActions,omitempty"`
PolicyResources *[]string `json:"policyResources,omitempty"`
PolicyResourceCategory *string `json:"policyResourceCategory,omitempty"`
PolicyPriority *int `json:"policyPriority,omitempty"`
IsPolicyEnabled *bool `json:"isPolicyEnabled,omitempty"`
PolicyMaskType *string `json:"policyMaskType,omitempty"`
PolicyValiditySchedule *[]atlan.AuthPolicyValiditySchedule `json:"policyValiditySchedule,omitempty"`
PolicyResourceSignature *string `json:"policyResourceSignature,omitempty"`
PolicyDelegateAdmin *bool `json:"policyDelegateAdmin,omitempty"`
PolicyConditions *[]atlan.AuthPolicyCondition `json:"policyConditions,omitempty"`
AccessControl *structs.AccessControl `json:"accessControl,omitempty"` // Relationship
}{}

// Unmarshal Base attributes
base, err := UnmarshalBaseEntity(data, &attributes)
if err != nil {
return err
}

// Unmarshal the attributes JSON into the entity.
if err := json.Unmarshal(temp.Entity.AttributesJSON, &a); err != nil {
return err
}

// Set the GUID and TypeName.
a.Guid = &temp.Entity.Guid
a.TypeName = &temp.Entity.TypeName
// Map base entity fields.
a.Guid = &base.Entity.Guid
a.TypeName = &base.Entity.TypeName
a.IsIncomplete = &base.Entity.IsIncomplete
a.Status = &base.Entity.Status
a.CreatedBy = &base.Entity.CreatedBy
a.UpdatedBy = &base.Entity.UpdatedBy
a.CreateTime = &base.Entity.CreateTime
a.UpdateTime = &base.Entity.UpdateTime

// Map AuthPolicy specific attributes to AuthPolicy fields.
a.UniqueAttributes.QualifiedName = attributes.QualifiedName
a.Name = attributes.Name
a.PolicyType = attributes.PolicyType
a.PolicyServiceName = attributes.PolicyServiceName
a.PolicyCategory = attributes.PolicyCategory
a.PolicySubCategory = attributes.PolicySubCategory
a.PolicyUsers = attributes.PolicyUsers
a.PolicyGroups = attributes.PolicyGroups
a.PolicyRoles = attributes.PolicyRoles
a.PolicyActions = attributes.PolicyActions
a.PolicyResources = attributes.PolicyResources
a.PolicyResourceCategory = attributes.PolicyResourceCategory
a.PolicyPriority = attributes.PolicyPriority
a.IsPolicyEnabled = attributes.IsPolicyEnabled
a.PolicyMaskType = attributes.PolicyMaskType
a.PolicyValiditySchedule = attributes.PolicyValiditySchedule
a.PolicyResourceSignature = attributes.PolicyResourceSignature
a.PolicyDelegateAdmin = attributes.PolicyDelegateAdmin
a.PolicyConditions = attributes.PolicyConditions
a.AccessControl = attributes.AccessControl

return nil
}

// MarshalJSON Marshals the AuthPolicy asset into a JSON object.
func (a *AuthPolicy) MarshalJSON() ([]byte, error) {
// Marshal the AccessControl asset into a JSON object.

// Construct the custom JSON structure
customJSON := map[string]interface{}{
"typeName": "AuthPolicy",
Expand Down Expand Up @@ -144,6 +168,12 @@ func (a *AuthPolicy) MarshalJSON() ([]byte, error) {
accessControl["typeName"] = *a.AccessControl.TypeName
}

if a.AccessControl.UniqueAttributes.QualifiedName != nil && *a.AccessControl.UniqueAttributes.QualifiedName != "" {
accessControl["uniqueAttributes"] = map[string]interface{}{
"qualifiedName": *a.AccessControl.UniqueAttributes.QualifiedName,
}
}

attributes["accessControl"] = accessControl
}

Expand Down
36 changes: 36 additions & 0 deletions atlan/assets/purpose_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,18 @@ func (p *Purpose) UnmarshalJSON(data []byte) error {

// Purpose-specific attributes
PurposeAtlanTags *[]structs.AtlanTagName `json:"purposeAtlanTags,omitempty"`

// Access Control-specific Attributes
IsAccessControlEnabled *bool `json:"isAccessControlEnabled,omitempty"`
DenyCustomMetadataGuids *[]string `json:"denyCustomMetadataGuids,omitempty"`
DenyAssetTabs *[]string `json:"denyAssetTabs,omitempty"`
DenyAssetFilters *[]string `json:"denyAssetFilters,omitempty"`
ChannelLink *string `json:"channelLink,omitempty"`
DenyAssetTypes *[]string `json:"denyAssetTypes,omitempty"`
DenyNavigationPages *[]string `json:"denyNavigationPages,omitempty"`
DefaultNavigation *string `json:"defaultNavigation,omitempty"`
DisplayPreferences *[]string `json:"displayPreferences,omitempty"`
Policies *[]AuthPolicy `json:"policies,omitempty"`
}{}
base, err := UnmarshalBaseEntity(data, &attributes)
if err != nil {
Expand All @@ -264,6 +276,30 @@ func (p *Purpose) UnmarshalJSON(data []byte) error {
PurposeAtlanTags: attributes.PurposeAtlanTags,
}

// Map Access Control Attributes
p.IsAccessControlEnabled = attributes.IsAccessControlEnabled
p.DenyCustomMetadataGuids = attributes.DenyCustomMetadataGuids
p.DenyAssetTabs = attributes.DenyAssetTabs
p.DenyAssetFilters = attributes.DenyAssetFilters
p.ChannelLink = attributes.ChannelLink
p.DenyAssetTypes = attributes.DenyAssetTypes
p.DenyNavigationPages = attributes.DenyNavigationPages
p.DefaultNavigation = attributes.DefaultNavigation
p.DisplayPreferences = attributes.DisplayPreferences

// Unmarshal RelationshipAttributes for Policies
if base.Entity.RelationshipAttributes != nil {
relationshipAttributes := struct {
Policies []structs.AuthPolicy `json:"policies,omitempty"`
}{}

if err := json.Unmarshal(base.Entity.RelationshipAttributes, &relationshipAttributes); err != nil {
return err
}
// Map the Policies field
p.Policies = &relationshipAttributes.Policies
}

return nil
}

Expand Down
67 changes: 61 additions & 6 deletions atlan/model/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,30 @@ func (sa *SearchAssets) MarshalJSON() ([]byte, error) {
customJSON["guid"] = *sa.Guid
}

if sa.Status != nil {
customJSON["status"] = *sa.Status
}

if sa.CreatedBy != nil {
customJSON["createdBy"] = *sa.CreatedBy
}

if sa.CreateTime != nil {
customJSON["createTime"] = *sa.CreateTime
}

if sa.UpdateTime != nil {
customJSON["updateTime"] = *sa.UpdateTime
}

if sa.UpdatedBy != nil {
customJSON["updatedBy"] = *sa.UpdatedBy
}

if sa.DisplayText != nil && *sa.DisplayText != "" {
customJSON["DisplayText"] = *sa.DisplayText
}

if sa.Asset.DisplayName != nil && *sa.Asset.DisplayName != "" {
attributes["DisplayText"] = *sa.Asset.DisplayName
}
Expand Down Expand Up @@ -832,6 +856,27 @@ func (sa *SearchAssets) MarshalJSON() ([]byte, error) {
attributes["roleId"] = *sa.RoleId
}

// Handle nested AccessControl field
accessControl := map[string]interface{}{}

if sa.AccessControl.Guid != nil && *sa.AccessControl.Guid != "" {
accessControl["guid"] = *sa.AccessControl.Guid
}

if sa.AccessControl.TypeName != nil && *sa.AccessControl.TypeName != "" {
accessControl["typeName"] = *sa.AccessControl.TypeName
}

if sa.AccessControl.UniqueAttributes.QualifiedName != nil && *sa.AccessControl.UniqueAttributes.QualifiedName != "" {
accessControl["uniqueAttributes"] = map[string]interface{}{
"qualifiedName": sa.AccessControl.UniqueAttributes.QualifiedName,
}
}

if len(accessControl) > 0 {
attributes["accessControl"] = accessControl
}

// Marshal the custom JSON
return json.MarshalIndent(customJSON, "", " ")
}
Expand All @@ -843,14 +888,15 @@ func (sa *SearchAssets) UnmarshalJSON(data []byte) error {
structs.Table
structs.Column
structs.AuthPolicy
structs.AccessControl
structs.Persona
structs.Purpose
QualifiedName *string `json:"qualifiedName,omitempty"`
Name *string `json:"name,omitempty"`
SearchAttributes *SearchAttributes `json:"attributes,omitempty"`
SearchMeanings []Meanings `json:"meanings,omitempty"`
NotNull *bool `json:"notNull,omitempty"`
structs.AccessControl
QualifiedName *string `json:"qualifiedName,omitempty"`
Name *string `json:"name,omitempty"`
SearchAttributes *SearchAttributes `json:"attributes,omitempty"`
SearchMeanings []Meanings `json:"meanings,omitempty"`
NotNull *bool `json:"notNull,omitempty"`

rawSearchAttributes map[string]interface{}
}

Expand All @@ -877,6 +923,8 @@ func (sa *SearchAssets) UnmarshalJSON(data []byte) error {
sa.Column = aux.Column
sa.NotNull = aux.NotNull
sa.SearchMeanings = meaningsData.SearchMeanings
sa.AuthPolicy = aux.AuthPolicy
sa.AccessControl = aux.AccessControl

// Check if any search attributes are present
if aux.SearchAttributes != nil {
Expand Down Expand Up @@ -990,6 +1038,13 @@ func (sa *SearchAssets) UnmarshalJSON(data []byte) error {
sa.PersonaUsers = aux.SearchAttributes.PersonaUsers
sa.RoleId = aux.SearchAttributes.RoleId

if aux.SearchAttributes.AccessControl != nil {
// Attributes under AccessControl struct
sa.AccessControl.TypeName = aux.SearchAttributes.AccessControl.TypeName
sa.AccessControl.Guid = aux.SearchAttributes.AccessControl.Guid
sa.AccessControl.UniqueAttributes.QualifiedName = aux.SearchAttributes.AccessControl.UniqueAttributes.QualifiedName
}

// Populate `rawSearchAttributes` (necessary for setting `SearchAssets.CustomMetadataSets`)
// First, unmarshal the data into a `rawSearchAsset` map
var rawSearchAsset map[string]interface{}
Expand Down
3 changes: 3 additions & 0 deletions atlan/model/structs/access_control.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ type AccessControl struct {
DefaultNavigation *string `json:"defaultNavigation,omitempty"`
DisplayPreferences *[]string `json:"displayPreferences,omitempty"`
Policies *[]AuthPolicy `json:"policies,omitempty"` // Relationship
UniqueAttributes struct {
QualifiedName *string `json:"qualifiedName,omitempty"`
} `json:"uniqueAttributes,omitempty"`
}

// AuthPolicy represents a policy with various attributes.
Expand Down
Loading

0 comments on commit a0f4bfc

Please sign in to comment.