diff options
author | HcySunYang <HcySunYang@outlook.com> | 2018-11-02 23:08:31 +0800 |
---|---|---|
committer | xuri <xuri.me@gmail.com> | 2018-11-02 23:08:31 +0800 |
commit | 4dbc78ce0a0d40be189b4c77207cdbb598846c73 (patch) | |
tree | b8295d120ecafa5c7e9e7aad22be09e65fc76aed | |
parent | 30122d0346dc751b01c9865af3febb1f99e1f5e0 (diff) |
resolve #273 new feature: protect sheet support
new feature: protect sheet support, relate issue #273
-rwxr-xr-x[-rw-r--r--] | excelize_test.go | 13 | ||||
-rw-r--r-- | lib.go | 28 | ||||
-rw-r--r-- | sheet.go | 41 | ||||
-rw-r--r--[-rwxr-xr-x] | test/Book1.xlsx | bin | 33070 -> 23099 bytes | |||
-rw-r--r-- | xmlWorksheet.go | 61 |
5 files changed, 123 insertions, 20 deletions
diff --git a/excelize_test.go b/excelize_test.go index d021b55..f7a70d9 100644..100755 --- a/excelize_test.go +++ b/excelize_test.go @@ -1208,6 +1208,19 @@ func TestSearchSheet(t *testing.T) { t.Log(xlsx.SearchSheet("Sheet1", "A")) } +func TestProtectSheet(t *testing.T) { + xlsx := NewFile() + xlsx.ProtectSheet("Sheet1", nil) + xlsx.ProtectSheet("Sheet1", &FormatSheetProtection{ + Password: "password", + EditScenarios: false, + }) + err := xlsx.SaveAs("./test/Book_protect_sheet.xlsx") + if err != nil { + t.Error(err) + } +} + func trimSliceSpace(s []string) []string { for { if len(s) > 0 && s[len(s)-1] == "" { @@ -15,6 +15,8 @@ import ( "io" "log" "math" + "strconv" + "strings" "unicode" ) @@ -199,3 +201,29 @@ func namespaceStrictToTransitional(content []byte) []byte { } return content } + +// genSheetPasswd provides a method to generate password for worksheet +// protection by given plaintext. When an Excel sheet is being protected with +// a password, a 16-bit (two byte) long hash is generated. To verify a +// password, it is compared to the hash. Obviously, if the input data volume +// is great, numerous passwords will match the same hash. Here is the +// algorithm to create the hash value: +// +// take the ASCII values of all characters shift left the first character 1 bit, the second 2 bits and so on (use only the lower 15 bits and rotate all higher bits, the highest bit of the 16-bit value is always 0 [signed short]) +// XOR all these values +// XOR the count of characters +// XOR the constant 0xCE4B +func genSheetPasswd(plaintext string) string { + var password int64 = 0x0000 + var charPos uint = 1 + for _, v := range plaintext { + value := int64(v) << charPos + charPos++ + rotatedBits := value >> 15 // rotated bits beyond bit 15 + value &= 0x7fff // first 15 bits + password ^= (value | rotatedBits) + } + password ^= int64(len(plaintext)) + password ^= 0xCE4B + return strings.ToUpper(strconv.FormatInt(password, 16)) +} @@ -711,6 +711,47 @@ func (f *File) SearchSheet(sheet, value string) []string { return result } +// ProtectSheet provides a function to prevent other users from accidentally +// or deliberately changing, moving, or deleting data in a worksheet. For +// example protect Sheet1 with protection settings: +// +// xlsx.ProtectSheet("Sheet1", &excelize.FormatSheetProtection{ +// Password: "password", +// EditScenarios: false, +// }) +// +func (f *File) ProtectSheet(sheet string, settings *FormatSheetProtection) { + xlsx := f.workSheetReader(sheet) + if settings == nil { + settings = &FormatSheetProtection{ + EditObjects: true, + EditScenarios: true, + SelectLockedCells: true, + } + } + xlsx.SheetProtection = &xlsxSheetProtection{ + AutoFilter: settings.AutoFilter, + DeleteColumns: settings.DeleteColumns, + DeleteRows: settings.DeleteRows, + FormatCells: settings.FormatCells, + FormatColumns: settings.FormatColumns, + FormatRows: settings.FormatRows, + InsertColumns: settings.InsertColumns, + InsertHyperlinks: settings.InsertHyperlinks, + InsertRows: settings.InsertRows, + Objects: settings.EditObjects, + PivotTables: settings.PivotTables, + Scenarios: settings.EditScenarios, + SelectLockedCells: settings.SelectLockedCells, + SelectUnlockedCells: settings.SelectUnlockedCells, + Sheet: true, + Sort: settings.Sort, + } + if settings.Password != "" { + xlsx.SheetProtection.Password = genSheetPasswd(settings.Password) + } +} + // trimSheetName provides a function to trim invaild characters by given worksheet // name. func trimSheetName(name string) string { diff --git a/test/Book1.xlsx b/test/Book1.xlsx Binary files differindex 7d9886e..84c43d1 100755..100644 --- a/test/Book1.xlsx +++ b/test/Book1.xlsx diff --git a/xmlWorksheet.go b/xmlWorksheet.go index 072ecce..42f8ddb 100644 --- a/xmlWorksheet.go +++ b/xmlWorksheet.go @@ -377,26 +377,27 @@ type xlsxF struct { // xlsxSheetProtection collection expresses the sheet protection options to // enforce when the sheet is protected. type xlsxSheetProtection struct { - AlgorithmName string `xml:"algorithmName,attr,omitempty"` - AutoFilter int `xml:"autoFilter,attr,omitempty"` - DeleteColumns int `xml:"deleteColumns,attr,omitempty"` - DeleteRows int `xml:"deleteRows,attr,omitempty"` - FormatCells int `xml:"formatCells,attr,omitempty"` - FormatColumns int `xml:"formatColumns,attr,omitempty"` - FormatRows int `xml:"formatRows,attr,omitempty"` - HashValue string `xml:"hashValue,attr,omitempty"` - InsertColumns int `xml:"insertColumns,attr,omitempty"` - InsertHyperlinks int `xml:"insertHyperlinks,attr,omitempty"` - InsertRows int `xml:"insertRows,attr,omitempty"` - Objects int `xml:"objects,attr,omitempty"` - PivotTables int `xml:"pivotTables,attr,omitempty"` - SaltValue string `xml:"saltValue,attr,omitempty"` - Scenarios int `xml:"scenarios,attr,omitempty"` - SelectLockedCells int `xml:"selectLockedCells,attr,omitempty"` - SelectUnlockedCell int `xml:"selectUnlockedCell,attr,omitempty"` - Sheet int `xml:"sheet,attr,omitempty"` - Sort int `xml:"sort,attr,omitempty"` - SpinCount int `xml:"spinCount,attr,omitempty"` + AlgorithmName string `xml:"algorithmName,attr,omitempty"` + AutoFilter bool `xml:"autoFilter,attr,omitempty"` + DeleteColumns bool `xml:"deleteColumns,attr,omitempty"` + DeleteRows bool `xml:"deleteRows,attr,omitempty"` + FormatCells bool `xml:"formatCells,attr,omitempty"` + FormatColumns bool `xml:"formatColumns,attr,omitempty"` + FormatRows bool `xml:"formatRows,attr,omitempty"` + HashValue string `xml:"hashValue,attr,omitempty"` + InsertColumns bool `xml:"insertColumns,attr,omitempty"` + InsertHyperlinks bool `xml:"insertHyperlinks,attr,omitempty"` + InsertRows bool `xml:"insertRows,attr,omitempty"` + Objects bool `xml:"objects,attr,omitempty"` + Password string `xml:"password,attr,omitempty"` + PivotTables bool `xml:"pivotTables,attr,omitempty"` + SaltValue string `xml:"saltValue,attr,omitempty"` + Scenarios bool `xml:"scenarios,attr,omitempty"` + SelectLockedCells bool `xml:"selectLockedCells,attr,omitempty"` + SelectUnlockedCells bool `xml:"selectUnlockedCells,attr,omitempty"` + Sheet bool `xml:"sheet,attr,omitempty"` + Sort bool `xml:"sort,attr,omitempty"` + SpinCount int `xml:"spinCount,attr,omitempty"` } // xlsxPhoneticPr (Phonetic Properties) represents a collection of phonetic @@ -599,3 +600,23 @@ type formatConditional struct { MultiRange string `json:"multi_range,omitempty"` BarColor string `json:"bar_color,omitempty"` } + +// FormatSheetProtection directly maps the settings of worksheet protection. +type FormatSheetProtection struct { + AutoFilter bool + DeleteColumns bool + DeleteRows bool + EditObjects bool + EditScenarios bool + FormatCells bool + FormatColumns bool + FormatRows bool + InsertColumns bool + InsertHyperlinks bool + InsertRows bool + Password string + PivotTables bool + SelectLockedCells bool + SelectUnlockedCells bool + Sort bool +} |