Let me start by saying that Excel is not a secure platform. VBA driven password code like this is not secure and can be easily overcome by any one with moderate VBA knowledge. Routines like this are good only for controlling access of willing users. Malevolent people can get past this in a trice.
Your requirement that certain users have access to different ranges in the same sheet made things a bit more complicated.
First, you need to set up your worksheet.
There has to be a sheet called Home, that will be always visible for all users.
There also has to be a sheet called Users, that will be visible only to admin. This sheet will hold the list of all users, their passwords and the sheet access that they have.
(You have to adjust the functions PassDataSheet and HomeSheet in the user form's code module.)
This is the test data that I used in my Users sheet.
Unknown |
---|
|
---|
| A | B | C | D | E | F | G |
---|
1 | Users | Password | Home | Users | Shared | Jane's Sheet | Bob's Sheet |
---|
2 | admin | admin | | | | | |
---|
3 | Bob | BobPW | | FALSE | 1:10 | FALSE | |
---|
4 | Jane | JanePW | | FALSE | 11:20 | | FALSE |
---|
|
---|
The workbook has 5 sheets, Home, Users, Shared, Bob's Sheet and Jane's Sheet. All the workbooks sheets should be in row 1 of the Users sheet.
There are three users: admin, Bob and Jane. Their case sensitive passwords are next to their names.
Looking down admin's row, we see that admin has access to all of all of the sheets (blank means "all access")
Bob has access to all of Home, no access to Users, access to rows 1:10 of Shared, no access to Jane's Sheet and access to all of Bob's Sheet
Jane has access to all of Home and Jane's Sheet, none to Users or Bob's Sheet, and to rows 11:20 of shared.
Partial access to a sheet is indicated by the range that the user has access to.
Discontinuous ranges are allowed, but work best with whole row or whole column areas (e.g. 1:1,10:20 vs A1:D4,E8:G10)
UserNames are case insensitive and should be unique
Passwords are case sensitive.
This is implemented through a user form, Userform1.
The code in the user form is
Code:
' in useform code module
Private Function PassDataSheet() As Worksheet
Set PassDataSheet = Sheet2: Rem adjust
End Function
Private Function HomeSheet() As Worksheet
Set HomeSheet = Sheet1: Rem adjust
End Function
Private Sub CommandButton1_Click()
If Me.Tag = vbNullString Then
Unload Me
Else
Me.Hide
End If
End Sub
Private Sub UserForm_Initialize()
Rem can be set at design time
With Label1
.Width = 300
.WordWrap = False
.Caption = "UserName"
.AutoSize = True
End With
With Label2
.Width = 300
.WordWrap = False
.Caption = "Password (case sensitive)"
.AutoSize = True
End With
With Label3
.Width = 200
.WordWrap = False
.Caption = "Enter username and password"
End With
With CommandButton1
.Caption = "Submit"
.TakeFocus******* = True
End With
With TextBox1
.EnterFieldBehavior = fmEnterFieldBehaviorSelectAll
.EnterKeyBehavior = False
.TabKeyBehavior = False
End With
With TextBox2
.EnterFieldBehavior = fmEnterFieldBehaviorSelectAll
.EnterKeyBehavior = False
.TabKeyBehavior = False
.PasswordChar = Chr(165)
End With
Rem end design time properties
TextBox1.Text = vbNullString
Call HideAll
End Sub
Public Function SubmitPassword() As Boolean
Dim uiUserName As String, uiPassword As String
Dim strPrompt As String
Dim userData As Range
Dim oneRng As Range, oneSheet As Worksheet
Dim AttemptCount As Long
AttemptCount = 3
Me.Tag = "active"
Label3.Caption = "Enter username and password"
Me.Caption = TextBox1.TabKeyBehavior
Do
TextBox2.Text = vbNullString
Me.Show
AttemptCount = AttemptCount - 1
With UserForm1
If .Tag <> vbNullString Then
uiUserName = TextBox1.Text
uiPassword = TextBox2.Text
Set userData = PassDataSheet.Columns(1).Find(what:=uiUserName, MatchCase:=False)
If userData Is Nothing Then
Label3.Caption = "No such user"
Label3.Caption = Label3.Caption & vbCr & AttemptCount & " attempts remain."
Label3.Caption = Label3.Caption & vbCr & vbCr & "Enter UserName and Password"
Else
Set userData = userData.EntireRow
If userData.Cells(1, 2) = uiPassword Then
SubmitPassword = True
Exit Do
Else
Label3.Caption = "Wrong password"
Label3.Caption = Label3.Caption & vbCr & AttemptCount & " attempts remain."
Label3.Caption = Label3.Caption & vbCr & vbCr & "Enter UserName and Password"
End If
End If
Else
Exit Do
End If
End With
Loop Until AttemptCount <= 0
If SubmitPassword Then
HomeSheet.Visible = xlSheetVisible
On Error Resume Next
With PassDataSheet
For Each oneRng In Range(.Cells(1, 3), .Cells(1, .Columns.Count).End(xlToLeft))
ViewStatus(ThisWorkbook.Sheets(oneRng.Value)) = Application.Intersect(userData, oneRng.EntireColumn).Value
Next oneRng
End With
On Error GoTo 0
Else
Rem bad password
HomeSheet.Visible = xlSheetVisible
For Each oneSheet In ThisWorkbook.Worksheets
ViewStatus(oneSheet) = (oneSheet.Name = "Home")
Next oneSheet
End If
Application.ScreenUpdating = True
Unload UserForm1
End Function
Public Sub HideAll()
Dim oneSheet As Worksheet
HomeSheet.Visible = xlSheetVisible
For Each oneSheet In ThisWorkbook.Worksheets
ViewStatus(oneSheet) = (oneSheet.Name = "Home")
Next oneSheet
Application.ScreenUpdating = True
End Sub
Property Get ViewStatus(aSheet As Worksheet) As Variant
ViewStatus = aSheet.Visible = xlSheetVisible
End Property
Property Let ViewStatus(aSheet As Worksheet, inVal As Variant)
Dim HideSheet As Boolean
Dim ViewRange As Range
Dim ScrollRange As Range
Dim oneRng As Range
Application.ScreenUpdating = False
Select Case LCase(inVal)
Case "all", "yes", "true", xlSheetVisible
inVal = ""
Case "none", "no", "false", xlSheetHidden, xlSheetVeryHidden
HideSheet = True
inVal = "A1"
End Select
With aSheet
If HideSheet Then
.Cells.EntireColumn.Hidden = True
.Cells.EntireRow.Hidden = True
.ScrollArea = "A1"
.Visible = xlSheetVeryHidden
Else
.Visible = xlSheetVisible
.Cells.EntireColumn.Hidden = False
.Cells.EntireRow.Hidden = False
If inVal = vbNullString Then
.ScrollArea = vbNullString
Else
Set ViewRange = .Range(inVal)
Set ScrollRange = ViewRange.Areas(1)
For Each oneRng In ViewRange.Areas
Set ScrollRange = Range(ScrollRange, oneRng)
Next oneRng
For Each oneRng In ScrollRange.Rows
If Application.Intersect(oneRng, ViewRange) Is Nothing Then
oneRng.EntireRow.Hidden = True
End If
Next oneRng
If ScrollRange.Column > 1 Then
Range(.Range("A1"), ScrollRange.Cells(1, 1).Offset(0, -1)).EntireColumn.Hidden = True
End If
If ScrollRange.Row > 1 Then
Range(.Range("A1"), ScrollRange.Cells(1, 1).Offset(-1, 0)).EntireRow.Hidden = True
End If
If ScrollRange.Column + ScrollRange.Columns.Count < .Columns.Count Then
Range(ScrollRange.Cells(1, 1).Offset(0, ScrollRange.Columns.Count), .Cells(.Columns.Count, .Columns.Count)).EntireColumn.Hidden = True
End If
If ScrollRange.Row + ScrollRange.Rows.Count < .Rows.Count Then
Range(ScrollRange.Cells(1, 1).Offset(ScrollRange.Rows.Count, 0), .Cells(.Rows.Count, .Columns.Count)).EntireRow.Hidden = True
End If
.ScrollArea = ScrollRange.Address
End If
End If
End With
End Property
And you should put this code in the ThisWorkbook code module.
Code:
' in ThisWorkbook code module
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Unload UserForm1
If Me.Saved Then Me.Save
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Unload UserForm1
End Sub
Private Sub Workbook_Open()
UserForm1.SubmitPassword
End Sub
And in a normal module
Code:
' in normal module
Sub ChangeUser()
UserForm1.SubmitPassword
End Sub