Skip to content

Commit

Permalink
Merge pull request #171 from VAuthenticator/random-password-generator
Browse files Browse the repository at this point in the history
Random password generator
mrFlick72 authored Nov 25, 2023
2 parents 3e940fd + f920c45 commit d42bd96
Showing 6 changed files with 259 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.vauthenticator.server.config

import com.vauthenticator.server.password.PasswordGenerator
import com.vauthenticator.server.password.PasswordGeneratorCriteria
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@EnableConfigurationProperties(PasswordGeneratorCriteria::class)
@Configuration(proxyBeanMethods = false)
class PasswordGeneratorConfig {


@Bean
fun passwordGenerator(passwordGeneratorCriteria: PasswordGeneratorCriteria) = PasswordGenerator(passwordGeneratorCriteria)
}
Original file line number Diff line number Diff line change
@@ -89,6 +89,13 @@ class WebSecurityConfig(

.requestMatchers("/change-password").permitAll()

.requestMatchers(HttpMethod.POST, "/api/password")
.hasAnyAuthority(Scope.GENERATE_PASSWORD.content)


.requestMatchers(HttpMethod.PUT, "/api/reset-password-challenge").permitAll()
.requestMatchers(HttpMethod.PUT, "/api/reset-password/{ticket}").permitAll()

.requestMatchers(*WHITE_LIST).permitAll()
.requestMatchers("/api/accounts").permitAll()

@@ -101,9 +108,6 @@ class WebSecurityConfig(
.requestMatchers(HttpMethod.PUT, "/api/accounts/password")
.hasAnyAuthority(Scope.CHANGE_PASSWORD.content)

.requestMatchers(HttpMethod.PUT, "/api/reset-password-challenge").permitAll()
.requestMatchers(HttpMethod.PUT, "/api/reset-password/{ticket}").permitAll()

.requestMatchers(HttpMethod.GET, "/api/mail-template")
.hasAnyAuthority(Scope.MAIL_TEMPLATE_READER.content)

Original file line number Diff line number Diff line change
@@ -65,7 +65,10 @@ data class Scope(val content: String) {

val SIGN_UP = Scope("admin:signup")
val WELCOME = Scope("admin:welcome")

val MAIL_VERIFY = Scope("admin:mail-verify")

val GENERATE_PASSWORD = Scope("admin:generate-password")
val RESET_PASSWORD = Scope("admin:reset-password")
val CHANGE_PASSWORD = Scope("admin:change-password")

Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package com.vauthenticator.server.password

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RestController
import kotlin.random.Random

@ConfigurationProperties(prefix = "password.generator-criteria")
data class PasswordGeneratorCriteria(
val upperCaseCharactersSize: Int,
val lowerCaseCharactersSize: Int,
val specialCharactersSize: Int,
val numberCharactersSize: Int
) {
fun size() = upperCaseCharactersSize + specialCharactersSize + numberCharactersSize
}

class PasswordGenerator(private val passwordGeneratorCriteria: PasswordGeneratorCriteria) {

fun generate(): String {
val random = Random
val builder = StringBuilder()
val mutableListOf = mutableListOf<Char>()

for (index in 1..passwordGeneratorCriteria.specialCharactersSize) {
mutableListOf.add(specialCharactersAlphabet[random.nextInt(specialCharactersAlphabet.size - 1)])
}
for (index in 1..passwordGeneratorCriteria.numberCharactersSize) {
mutableListOf.add(numberAlphabet[random.nextInt(numberAlphabet.size - 1)])
}
for (index in 1..passwordGeneratorCriteria.upperCaseCharactersSize) {
mutableListOf.add(upperCaseAlphabet[random.nextInt(upperCaseAlphabet.size - 1)])
}
for (index in 1..passwordGeneratorCriteria.lowerCaseCharactersSize) {
mutableListOf.add(lowerCaseAlphabet[random.nextInt(lowerCaseAlphabet.size - 1)])
}

for (index in 0..mutableListOf.size) {
val nextInt = if (mutableListOf.size > 0) {
random.nextInt(mutableListOf.size)
} else {
0
}

if (mutableListOf.isNotEmpty()) {
builder.append(mutableListOf[nextInt])
mutableListOf.removeAt(nextInt)
}
}

return builder.toString()
}
}

data class GeneratedPasswordResponse(val pwd: String)

@RestController
class PasswordGeneratorEndPoint(private val passwordGenerator: PasswordGenerator) {

@PostMapping("/api/password")
fun generate() = ResponseEntity.ok(GeneratedPasswordResponse(passwordGenerator.generate()))
}

val specialCharactersAlphabet = charArrayOf(
'!',
'@',
'#',
'$',
'%',
'^',
'&',
'*',
'(',
')',
'-',
'_',
'=',
'+',
'[',
']',
'{',
'}',
';',
':',
'"',
'\'',
'\\',
'|',
'~',
'`',
',',
'<',
'.',
'>',
'/',
'?',
)
val numberAlphabet = charArrayOf(
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
)
val lowerCaseAlphabet = charArrayOf(
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z'
)
val upperCaseAlphabet = charArrayOf(
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z'
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.vauthenticator.server.password

import com.fasterxml.jackson.databind.ObjectMapper
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.junit5.MockKExtension
import io.mockk.verify
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup

@ExtendWith(MockKExtension::class)
internal class PasswordGeneratorEndPointTest {

private val objectMapper = ObjectMapper()

lateinit var mokMvc: MockMvc

@MockK
lateinit var passwordGenerator: PasswordGenerator

@BeforeEach
internal fun setUp() {
mokMvc = standaloneSetup(
PasswordGeneratorEndPoint(passwordGenerator)
).build()
}

@Test
fun `when a new random password is generated`() {
every { passwordGenerator.generate() } returns "A_RANDOM_PASSWORD"
val expected = GeneratedPasswordResponse("A_RANDOM_PASSWORD")

mokMvc.perform(post("/api/password"))
.andExpect(content().json(objectMapper.writeValueAsString(expected)))
.andExpect(status().isOk)

verify { passwordGenerator.generate() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.vauthenticator.server.password

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

private const val STRONG_PASSWORD_LENGTH = 20

class PasswordGeneratorTest {

private val uut = PasswordGenerator(PasswordGeneratorCriteria(5, 5, 5, 5))


@Test
fun name() {

val actual = uut.generate()
assertEquals(STRONG_PASSWORD_LENGTH, actual.length)
assertTrue(actual.length == STRONG_PASSWORD_LENGTH)
}

}

0 comments on commit d42bd96

Please sign in to comment.