Skip to content

Commit

Permalink
Merge pull request #2488 from NationalSecurityAgency/2.11.2-merge
Browse files Browse the repository at this point in the history
2.11.2 merge
  • Loading branch information
sudo-may authored Nov 29, 2023
2 parents 397c571 + c047d86 commit eec5b23
Show file tree
Hide file tree
Showing 20 changed files with 190 additions and 38 deletions.
2 changes: 1 addition & 1 deletion dashboard/src/components/access/InviteUsersToProject.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ limitations under the License.
import AccessService from '@/components/access/AccessService';
import MsgBoxMixin from '@/components/utils/modal/MsgBoxMixin';
const validEmail = /^[a-z0-9.]{1,64}@[a-z0-9.]{1,64}$/i;
const validEmail = /^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]{1,64}@[a-zA-Z0-9.-]{1,64}$/i;
const stripNames = /<([^\s<>@]+@[^\s<>@]+)>/;
export default {
name: 'InviteUsersToProject',
Expand Down
2 changes: 1 addition & 1 deletion dashboard/src/components/badges/EditBadge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ limitations under the License.
<div class="media-body">
<div class="form-group">
<label for="badgeName">* Badge Name</label>
<ValidationProvider rules="required|minNameLength|maxBadgeNameLength|uniqueName|customNameValidator"
<ValidationProvider rules="required|minNameLength|maxBadgeNameLength|nullValueNotAllowed|uniqueName|customNameValidator"
v-slot="{errors}" name="Badge Name" :debounce="250">
<input v-focus class="form-control" id="badgeName" type="text" v-model="badgeInternal.name"
@input="updateBadgeId" aria-required="true" data-cy="badgeName"
Expand Down
2 changes: 1 addition & 1 deletion dashboard/src/components/projects/EditProject.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ limitations under the License.
<div class="col-12">
<div class="form-group">
<label for="projectIdInput">* {{ nameLabelTxt }}</label>
<ValidationProvider rules="required|minNameLength|maxProjectNameLength|uniqueName|customNameValidator"
<ValidationProvider rules="required|minNameLength|maxProjectNameLength|uniqueName|customNameValidator|nullValueNotAllowed"
v-slot="{errors}"
:debounce="250"
name="Project Name">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ limitations under the License.
<b-container v-if="!loading" fluid data-cy="editQuestionModal">
<ReloadMessage v-if="restoredFromStorage" @discard-changes="discardChanges" />

<ValidationProvider rules="required|maxDescriptionLength|customDescriptionValidator" :debounce="250" v-slot="{errors}" name="Question">
<ValidationProvider rules="required|maxDescriptionLength|nullValueNotAllowed|customDescriptionValidator" :debounce="250" v-slot="{errors}" name="Question">
<markdown-editor v-if="showQuestion && questionDefInternal"
:quiz-id="quizId"
label="Question"
Expand Down
2 changes: 1 addition & 1 deletion dashboard/src/components/quiz/testCreation/EditQuiz.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ limitations under the License.
<div class="form-group">
<label for="quizNameInput">* Name</label>
<ValidationProvider
rules="required|minNameLength|maxQuizNameLength|uniqueName|customNameValidator"
rules="required|minNameLength|maxQuizNameLength|nullValueNotAllowed|uniqueName|customNameValidator"
:debounce="500"
v-slot="{errors}"
name="Quiz Name">
Expand Down
2 changes: 1 addition & 1 deletion dashboard/src/components/skills/EditSkill.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ limitations under the License.
<div class="col-12 col-lg">
<div class="form-group">
<label for="skillName">* Skill Name</label>
<ValidationProvider rules="required|minNameLength|maxSkillNameLength|uniqueName|customNameValidator" :debounce="250" v-slot="{errors}" name="Skill Name" ref="skillNameProvider">
<ValidationProvider rules="required|minNameLength|maxSkillNameLength|nullValueNotAllowed|uniqueName|customNameValidator" :debounce="250" v-slot="{errors}" name="Skill Name" ref="skillNameProvider">
<input type="text" class="form-control" id="skillName" @input="updateSkillId"
v-model="skillInternal.name" v-focus
aria-required="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ limitations under the License.
<div class="col-12">
<div class="form-group">
<label for="groupNameInput">* Group Name</label>
<ValidationProvider rules="required|minNameLength|maxSkillNameLength|uniqueGroupName|customNameValidator"
<ValidationProvider rules="required|minNameLength|maxSkillNameLength|nullValueNotAllowed|uniqueGroupName|customNameValidator"
v-slot="{errors}"
:debounce="250"
name="Group Name">
Expand Down
2 changes: 1 addition & 1 deletion dashboard/src/components/subjects/EditSubject.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ limitations under the License.
<div class="form-group">
<label for="subjName">Subject Name</label>
<ValidationProvider
rules="required|minNameLength|maxSubjectNameLength|uniqueName|customNameValidator" :debounce="250"
rules="required|minNameLength|maxSubjectNameLength|nullValueNotAllowed|uniqueName|customNameValidator" :debounce="250"
v-slot="{ errors }" name="Subject Name">
<input type="text" class="form-control" id="subjName" @input="updateSubjectId"
v-model="subjectInternal.name" v-on:input="updateSubjectId"
Expand Down
2 changes: 1 addition & 1 deletion dashboard/src/components/utils/inputForm/IdInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ limitations under the License.
},
data() {
return {
rules: `required|minIdLength|maxIdLength|${this.isSkillId ? 'skill_id_validator' : 'id_validator'}`,
rules: `required|minIdLength|maxIdLength|nullValueNotAllowed|${this.isSkillId ? 'skill_id_validator' : 'id_validator'}`,
canEdit: false,
internalValue: this.value,
};
Expand Down
27 changes: 27 additions & 0 deletions dashboard/src/validators/NotNullValidator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2020 SkillTree
*
* 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
*
* https://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.
*/
import { extend } from 'vee-validate';

const validator = {
message: (field) => `Null is not allowed for ${field}`,
validate(value) {
return !value || value.trim().toLowerCase() !== 'null';
},
};

extend('nullValueNotAllowed', validator);

export default validator;
1 change: 1 addition & 0 deletions dashboard/src/validators/RegisterValidators.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import './OptionalNumericValidator';
import './CustomNameValidator';
import './IdValidator';
import './SkillIdValidator';
import './NotNullValidator';
import './UrlValidator';
import store from '../store/store';

Expand Down
30 changes: 30 additions & 0 deletions e2e-tests/cypress/e2e/projects_modal_validation_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,4 +342,34 @@ describe('Projects Modal Validation Tests', () => {
cy.get('[data-cy="projectDescriptionError"]').contains('Mocked up validation failure')
});

it('null word is not allowed for project ID or project name', () => {
cy.visit('/administrator/');
cy.get('[data-cy="newProjectButton"]').click()
cy.get('[data-cy="projectName"]')
.type('null');
cy.get('[data-cy=projectNameError]')
.contains('Null is not allowed for Project Name')
.should('be.visible');
cy.get('[data-cy="idError"]')
.contains('Null is not allowed for Project ID')
.should('be.visible');
cy.get('[data-cy=saveProjectButton]')
.should('be.disabled');
cy.get('[data-cy="projectName"]').clear().type('one')
cy.get('[data-cy=projectNameError]').should('not.be.visible')
cy.get('[data-cy=idError]').should('not.be.visible')

// verify validator is case-insensitive and trims whitespace
cy.get('[data-cy="projectName"]').clear()
.type(' NUlL ');
cy.get('[data-cy=projectNameError]')
.contains('Null is not allowed for Project Name')
.should('be.visible');
cy.get('[data-cy="idError"]')
.contains('Null is not allowed for Project ID')
.should('be.visible');
cy.get('[data-cy=saveProjectButton]')
.should('be.disabled');
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ class AdminController {
@RequestBody NameExistsRequest existsRequest) {
String subjectName = existsRequest.name?.trim()
SkillsValidator.isNotBlank(projectId, "Project Id")
SkillsValidator.isNotBlank(subjectName, "Subject Name")
SkillsValidator.isNotBlank(subjectName, "Subject Name", projectId, null, true )

def sanitize = InputSanitizer.sanitize(subjectName)
return subjAdminService.existsBySubjectName(InputSanitizer.sanitize(projectId), sanitize)
Expand All @@ -342,7 +342,7 @@ class AdminController {
@RequestBody NameExistsRequest nameExistsRequest) {
String badgeName = nameExistsRequest.name?.trim()
SkillsValidator.isNotBlank(projectId, "Project Id")
SkillsValidator.isNotBlank(badgeName, "Badge Name")
SkillsValidator.isNotBlank(badgeName, "Badge Name", projectId, null, true)
return badgeAdminService.existsByBadgeName(InputSanitizer.sanitize(projectId), InputSanitizer.sanitize(badgeName))
}

Expand All @@ -353,7 +353,7 @@ class AdminController {
@RequestBody NameExistsRequest existsRequest) {
String skillName = existsRequest.name?.trim()
SkillsValidator.isNotBlank(projectId, "Project Id")
SkillsValidator.isNotBlank(skillName, "Skill Name")
SkillsValidator.isNotBlank(skillName, "Skill Name", projectId, null, true)
return skillsAdminService.existsBySkillName(InputSanitizer.sanitize(projectId), InputSanitizer.sanitize(skillName))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import org.apache.commons.lang3.StringUtils

class QuizValidator {

static void isNotBlank(String value, String attrName, String quizId = null) {
if (StringUtils.isBlank(value) || value?.trim().equalsIgnoreCase("null")) {
static void isNotBlank(String value, String attrName, String quizId = null, boolean allowNullValueStr = false) {
if (StringUtils.isBlank(value) || (!allowNullValueStr && value?.trim().equalsIgnoreCase("null"))) {
throw new SkillQuizException("${attrName} was not provided.".toString(), quizId, ErrorCode.BadParam)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import org.apache.commons.lang3.StringUtils

class SkillsValidator {

static void isNotBlank(String value, String attrName, String projectId = null, String skillId = null) {
if (StringUtils.isBlank(value) || value?.trim().equalsIgnoreCase("null")) {
static void isNotBlank(String value, String attrName, String projectId = null, String skillId = null, boolean allowNullValueStr = false) {
if (StringUtils.isBlank(value) || (!allowNullValueStr && value?.trim().equalsIgnoreCase("null"))) {
throw new SkillException("${attrName} was not provided.".toString(), projectId, skillId, ErrorCode.BadParam)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ class QuizDefService {
QuizValidator.isNotNull(questionDefRequest.answers, "answers", quizId)
QuizValidator.isTrue(questionDefRequest.answers.size() >= 2, "Must have at least 2 answers", quizId)
questionDefRequest.answers.each {
QuizValidator.isNotBlank(it.answer, "answers.answer", quizId)
QuizValidator.isNotBlank(it.answer, "answers.answer", quizId, true)
propsBasedValidator.quizValidationMaxStrLength(PublicProps.UiProp.maxQuizTextAnswerLength, "Answer", it.answer, quizDef.quizId)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -689,15 +689,15 @@ interface UserPointsRepo extends CrudRepository<UserPoints, Integer> {
)
SELECT COUNT(*)
FROM (
SELECT DISTINCT up.user_id
SELECT up.user_id, SUM(up.points) as total_points
from user_points up, user_attrs usattr
where
up.user_id = usattr.user_id and
up.skill_ref_id in (select id from subj_skills) and
up.points >= :minimumPoints and
(lower(CONCAT(usattr.first_name, ' ', usattr.last_name, ' (', usattr.user_id_for_display, ')')) like lower(CONCAT('%', :userId, '%')) OR
lower(usattr.user_id_for_display) like lower(CONCAT('%', :userId, '%')))
) AS temp
group by up.user_id
) AS temp WHERE total_points >= :minimumPoints
''', nativeQuery = true)
Long countDistinctUsersByProjectIdAndSubjectIdAndUserIdLike(@Param("projectId") String projectId, @Param("subjectId") String subjectId, @Param("userId") String userId, @Param("minimumPoints") int minimumPoints)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ class ConstraintViolationSpecs extends DefaultIntSpec {
Map proj = SkillsFactory.createProject()
Map subject = SkillsFactory.createSubject()
Map skill = SkillsFactory.createSkill()
Map skill2 = SkillsFactory.createSkill(1, 1, 2, )
Map skill2 = SkillsFactory.createSkill(1, 1, 2,)
Map copy = new HashMap(skill)
copy.skillId = skill2.skillId.toUpperCase()
copy.name = "somethingElse"
Expand Down Expand Up @@ -460,4 +460,48 @@ class ConstraintViolationSpecs extends DefaultIntSpec {
exception.message.contains("errorCode:ConstraintViolation")
}

}
def "subject name exists does not fail on the null word"() {
def proj = SkillsFactory.createProject(1)
skillsService.createProject(proj)
when:
def exist = skillsService.subjectNameExists([projectId: proj.projectId, subjectName: "null"])

then:
exist == false
}

def "project name exists does not fail on the null word"() {
when:
def exist = skillsService.projectNameExists([projectName: "null"])
then:
exist == false
}

def "badge name exists does not fail on the null word"() {
def proj = SkillsFactory.createProject(1)
skillsService.createProject(proj)
when:
def exist = skillsService.badgeNameExists([projectId: proj.projectId, badgeName: "null"])
then:
exist == false
}

def "skill name exists does not fail on the null word"() {
def proj = SkillsFactory.createProject(1)
skillsService.createProject(proj)
def subj = SkillsFactory.createSubject(1, 1)
skillsService.createSubject(subj)
when:
def exist = skillsService.skillNameExists([projectId: proj.projectId, skillName: "null"])
then:
exist == false
}

def "quiz name exists does not fail on the null word"() {
when:
def exist = skillsService.quizNameExist("null")
then:
exist.body == false
}

}
Loading

0 comments on commit eec5b23

Please sign in to comment.