Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set creator principal id annotation when creating projects and v3 clusters #12233

Merged
merged 5 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions cypress/e2e/blueprints/explorer/rbac/third-party-principals-get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const res = {
type: 'collection',
links: { self: 'https://localhost:8005/v3/principals' },
actions: { search: 'https://localhost:8005/v3/principals?action=search' },
pagination: { limit: 1000 },
sort: {
order: 'asc',
reverse: 'https://localhost:8005/v3/principals?order=desc',
links: {
loginName: 'https://localhost:8005/v3/principals?sort=loginName', name: 'https://localhost:8005/v3/principals?sort=name', principalType: 'https://localhost:8005/v3/principals?sort=principalType', profilePicture: 'https://localhost:8005/v3/principals?sort=profilePicture', profileURL: 'https://localhost:8005/v3/principals?sort=profileURL', provider: 'https://localhost:8005/v3/principals?sort=provider', uuid: 'https://localhost:8005/v3/principals?sort=uuid'
}
},
filters: {
created: null, creatorId: null, id: null, loginName: null, me: null, memberOf: null, name: null, principalType: null, profilePicture: null, profileURL: null, provider: null, removed: null, uuid: null
},
resourceType: 'principal',
data: [
{
baseType: 'principal',
created: null,
creatorId: null,
id: 'github://1234567890',
links: { self: 'https://localhost:8005/v3/principals/github:%2F%2F1234567890' },
loginName: 'admin',
me: true,
memberOf: false,
name: 'Default Admin',
principalType: 'user',
provider: 'github',
type: 'principal'
}
]
};

export function spoofThirdPartyPrincipal(): Cypress.Chainable<Response> {
return cy.intercept('GET', '/v3/principals', (req) => {
req.reply({
statusCode: 200,
body: res
});
}).as('spoofThirdPartyPrincipal');
}
52 changes: 45 additions & 7 deletions cypress/e2e/tests/pages/explorer2/project-namespace.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ProjectsNamespacesPagePo from '@/cypress/e2e/po/pages/explorer/projects-namespaces.po';
import { spoofThirdPartyPrincipal } from '@/cypress/e2e/blueprints/explorer/rbac/third-party-principals-get';

describe('Projects/Namespaces', { tags: ['@explorer2', '@adminUser'] }, () => {
const projectsNamespacesPage = new ProjectsNamespacesPagePo('local');
Expand All @@ -19,23 +20,60 @@ describe('Projects/Namespaces', { tags: ['@explorer2', '@adminUser'] }, () => {
projectsNamespacesPage.nsProject().checkExists();
});

describe('Create Project validation', () => {
describe('Project creation', () => {
beforeEach(() => {
cy.createE2EResourceName('proj').as('projectName');
cy.intercept('POST', '/v3/projects').as('createProjectRequest');
});

it('sets the creator principal id annotation when creating a project and using third-party auth', () => {
cy.get('@projectName').then((projectName) => {
// intercept the request to /v3/principals and return a principal authenticated by github instead of local
spoofThirdPartyPrincipal();

projectsNamespacesPage.createProjectButtonClick();
projectsNamespacesPage.name().set(projectName);
projectsNamespacesPage.buttonSubmit().click();

cy.wait('@createProjectRequest').then(({ request, response }) => {
expect(request.body.annotations['field.cattle.io/creator-principal-name']).to.equal('github://1234567890');
expect(response.body.annotations['field.cattle.io/creator-principal-name']).to.equal('github://1234567890');
});
});
});

it('does not set a creator principal id annotation when creating a project if using local auth', () => {
cy.get('@projectName').then((projectName) => {
projectsNamespacesPage.createProjectButtonClick();
projectsNamespacesPage.name().set(projectName);
projectsNamespacesPage.buttonSubmit().click();

cy.wait('@createProjectRequest').then(({ request, response }) => {
expect(request.body.annotations).to.not.have.property('field.cattle.io/creator-principal-name');
expect(response.body.annotations).to.not.have.property('field.cattle.io/creator-principal-name');
});
});
});

afterEach(() => {
cy.get('@projectName').then((projectName) => {
cy.deleteRancherResource('v3', 'projects', projectName, false);
});
});
});

describe('Project Error Banner and Validation', () => {
beforeEach(() => {
projectsNamespacesPage.goTo();
});

// Issue 5975: create button should be disabled unless name is filled in
it('Create button becomes available if the name is filled in', () => {
projectsNamespacesPage.createProjectButtonClick();
projectsNamespacesPage.buttonSubmit().expectToBeDisabled();
projectsNamespacesPage.name().set('test-1234');
projectsNamespacesPage.buttonSubmit().expectToBeEnabled();
});
});

describe('Create Project Error Banner', () => {
beforeEach(() => {
projectsNamespacesPage.goTo();
});

it('displays an error message when submitting a form with errors', () => {
projectsNamespacesPage.createProjectButtonClick();
Expand Down
6 changes: 6 additions & 0 deletions pkg/aks/components/CruAks.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
nodePoolNamesUnique,
nodePoolCount
} from '../util/validators';
import { CREATOR_PRINCIPAL_ID } from '@shell/config/labels-annotations';

export const defaultNodePool = {
availabilityZones: ['1', '2', '3'],
Expand Down Expand Up @@ -95,6 +96,7 @@ const defaultCluster = {
enableClusterMonitoring: false,
enableNetworkPolicy: false,
labels: {},
annotations: {},
windowsPreferedCluster: false,
};

Expand Down Expand Up @@ -161,6 +163,10 @@ export default defineComponent({
this.originalVersion = this.normanCluster?.aksConfig?.kubernetesVersion;
} else {
this.normanCluster = await store.dispatch('rancher/create', { type: NORMAN.CLUSTER, ...defaultCluster }, { root: true });

if (!this.$store.getters['auth/principalId'].includes('local://')) {
this.normanCluster.annotations[CREATOR_PRINCIPAL_ID] = this.$store.getters['auth/principalId'];
}
}
if (!this.normanCluster.aksConfig) {
this.normanCluster['aksConfig'] = { ...defaultAksConfig };
Expand Down
5 changes: 5 additions & 0 deletions pkg/eks/components/CruEKS.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ import Config from './Config.vue';
import Networking from './Networking.vue';
import AccountAccess from './AccountAccess.vue';
import EKSValidators from '../util/validators';
import { CREATOR_PRINCIPAL_ID } from '@shell/config/labels-annotations';

const DEFAULT_CLUSTER = {
dockerRootDir: '/var/lib/docker',
enableClusterAlerting: false,
enableClusterMonitoring: false,
enableNetworkPolicy: false,
labels: {},
annotations: {},
windowsPreferedCluster: false,
fleetAgentDeploymentCustomization: {},
clusterAgentDeploymentCustomization: {}
Expand Down Expand Up @@ -127,6 +129,9 @@ export default defineComponent({
this.originalVersion = this.normanCluster?.eksConfig?.kubernetesVersion || '';
} else {
this.normanCluster = await store.dispatch('rancher/create', { type: NORMAN.CLUSTER, ...DEFAULT_CLUSTER }, { root: true });
if (!this.$store.getters['auth/principalId'].includes('local://')) {
this.normanCluster.annotations[CREATOR_PRINCIPAL_ID] = this.$store.getters['auth/principalId'];
}
}

if (!this.normanCluster.eksConfig) {
Expand Down
5 changes: 5 additions & 0 deletions pkg/gke/components/CruGKE.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
clusterNameChars, clusterNameStartEnd, requiredInCluster, ipv4WithCidr, ipv4oripv6WithCidr, GKEInitialCount
} from '../util/validators';
import { diffUpstreamSpec, syncUpstreamConfig } from '@shell/utils/kontainer';
import { CREATOR_PRINCIPAL_ID } from '@shell/config/labels-annotations';

const defaultMachineType = 'n1-standard-2';

Expand Down Expand Up @@ -124,6 +125,7 @@ const defaultCluster = {
enableClusterMonitoring: false,
enableNetworkPolicy: false,
labels: {},
annotations: {},
windowsPreferedCluster: false,
};

Expand Down Expand Up @@ -174,6 +176,9 @@ export default defineComponent({
this.originalVersion = this.normanCluster?.gkeConfig?.kubernetesVersion;
} else {
this.normanCluster = await store.dispatch('rancher/create', { type: NORMAN.CLUSTER, ...defaultCluster }, { root: true });
if (!this.$store.getters['auth/principalId'].includes('local://')) {
this.normanCluster.annotations[CREATOR_PRINCIPAL_ID] = this.$store.getters['auth/principalId'];
}
}
// ensure any fields editable through this UI that have been altered in aws are shown here - see syncUpstreamConfig jsdoc for details
if (!this.isNewOrUnprovisioned) {
Expand Down
1 change: 1 addition & 0 deletions shell/config/labels-annotations.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const CATTLE_PUBLIC_ENDPOINTS = 'field.cattle.io/publicEndpoints';
export const TARGET_WORKLOADS = 'field.cattle.io/targetWorkloadIds';
export const UI_MANAGED = 'management.cattle.io/ui-managed';
export const CREATOR_ID = 'field.cattle.io/creatorId';
export const CREATOR_PRINCIPAL_ID = 'field.cattle.io/creator-principal-name';
export const RESOURCE_QUOTA = 'field.cattle.io/resourceQuota';
export const AZURE_MIGRATED = 'auth.cattle.io/azuread-endpoint-migrated';
export const WORKSPACE_ANNOTATION = 'objectset.rio.cattle.io/id';
Expand Down
5 changes: 4 additions & 1 deletion shell/edit/management.cattle.io.project.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { MANAGEMENT } from '@shell/config/types';
import { NAME } from '@shell/config/product/explorer';
import { PROJECT_ID, _VIEW, _CREATE, _EDIT } from '@shell/config/query-params';
import ProjectMembershipEditor, { canViewProjectMembershipEditor } from '@shell/components/form/Members/ProjectMembershipEditor';

import { CREATOR_PRINCIPAL_ID } from '@shell/config/labels-annotations';
import { HARVESTER_NAME as HARVESTER } from '@shell/config/features';
import { Banner } from '@components/Banner';

Expand Down Expand Up @@ -103,6 +103,9 @@ export default {
this.value.metadata['namespace'] = this.$store.getters['currentCluster'].id;
this.value['spec'] = this.value.spec || {};
this.value.spec['containerDefaultResourceLimit'] = this.value.spec.containerDefaultResourceLimit || {};
if (!this.$store.getters['auth/principalId'].includes('local://')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might make sense to add a utility or a getter to check isPrincipalIdLocal.

this.value.metadata.annotations[CREATOR_PRINCIPAL_ID] = this.$store.getters['auth/principalId'];
}
},
methods: {
async save(saveCb) {
Expand Down
Loading