secure/precis: add ability to restrict profiles

Fixes golang/go#24885

Change-Id: I4d5e6c9279af9c3183aa71b4f4562209445af165
Reviewed-on: https://go-review.googlesource.com/113816
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/secure/precis/enforce10.0.0_test.go b/secure/precis/enforce10.0.0_test.go
index f224936..34da150 100644
--- a/secure/precis/enforce10.0.0_test.go
+++ b/secure/precis/enforce10.0.0_test.go
@@ -7,6 +7,9 @@
 package precis
 
 import (
+	"strings"
+
+	"golang.org/x/text/runes"
 	"golang.org/x/text/secure/bidirule"
 )
 
@@ -241,4 +244,10 @@
 		{"\u212B", "\u00c5", nil},    // Angstrom sign, NFC -> U+00E5
 		{"ẛ", "", errDisallowedRune}, // LATIN SMALL LETTER LONG S WITH DOT ABOVE
 	}},
+	{"UsernameCaseMappedRestricted", NewRestrictedProfile(UsernameCaseMapped, runes.Predicate(func(r rune) bool {
+		return strings.ContainsRune(`@`, r)
+	})), []testCase{
+		{"juliet@example.com", "", errDisallowedRune},
+		{"\u0049", "\u0069", nil},
+	}},
 }
diff --git a/secure/precis/enforce9.0.0_test.go b/secure/precis/enforce9.0.0_test.go
index 298c8a9..ed5759c 100644
--- a/secure/precis/enforce9.0.0_test.go
+++ b/secure/precis/enforce9.0.0_test.go
@@ -241,4 +241,10 @@
 		{"\u212B", "\u00c5", nil},    // Angstrom sign, NFC -> U+00E5
 		{"ẛ", "", errDisallowedRune}, // LATIN SMALL LETTER LONG S WITH DOT ABOVE
 	}},
+	{"UsernameCaseMappedRestricted", NewRestrictedProfile(UsernameCaseMapped, runes.Predicate(func(r rune) bool {
+		return strings.ContainsRune(`@`, r)
+	})), []testCase{
+		{"juliet@example.com", "", errDisallowedRune},
+		{"\u0049", "\u0069", nil},
+	}},
 }
diff --git a/secure/precis/profile.go b/secure/precis/profile.go
index 0419159..35bd6f0 100644
--- a/secure/precis/profile.go
+++ b/secure/precis/profile.go
@@ -52,6 +52,16 @@
 	}
 }
 
+// NewRestrictedProfile creates a new PRECIS profile based on an existing
+// profile.
+// If the parent profile already had the Disallow option set, the new rule
+// overrides the parents rule.
+func NewRestrictedProfile(parent *Profile, disallow runes.Set) *Profile {
+	p := *parent
+	Disallow(disallow)(&p.options)
+	return &p
+}
+
 // NewTransformer creates a new transform.Transformer that performs the PRECIS
 // preparation and enforcement steps on the given UTF-8 encoded bytes.
 func (p *Profile) NewTransformer() *Transformer {