Row level security in Excel - what approach should I use: - vba

Morning folks,
Over the last couple of days I have been trying to find a way of implementing "pseudo" row level security in Excel using VBA macros.
What I require is to filter all of my Excel table based on the Windows user credentials. The problem is that I am not sure how to tackle this issue and this is the only idea I have so far:
1.Derive the NT account and store it in namedrange
2. Use autofilter based on NamedRange for each table in my spreadsheet
3. Delete / Hide the unnecessary rows ?
Example:
Perhaps, there are other ways which are more suitable for my scenario, however I havent been able to find them.
If you could point me into the right direction, I would appreciate it.
Thanks

This can be achieved by pasting the following into the ThisWorkBook Module in your specific WorkBook VBA project. Just remember to save the WorkBook as a .dotm
As mentioned by J.Chomel bypassing WorkBook passwords is not the hardest thing to do and you could find a macro to do that on the net.
Obviously you will need to password protect your VBA project as well to prevent users from accessing the WorkBook password from there.
Private Sub Workbook_Open()
Dim CurrentUserName As String
CurrentUserName = Environ("Username")
Dim WorkBookPassword As String
'Replace password with your desired password
WorkBookPassword = "password"
Dim DataWS1 As Worksheet
'Replace Sheet1 with your specific sheet name
Set DataWS1 = ThisWorkbook.Sheets("Sheet1")
With DataWS1
.Unprotect WorkBookPassword
If .AutoFilterMode Then .AutoFilterMode = False
.UsedRange.AutoFilter Field:=3, Criteria1:=CurrentUserName
.Protect Contents:=True, _
AllowFiltering:=False, _
UserInterfaceOnly:=True, _
Password:=WorkBookPassword
End With
End Sub

To implement row level security in Excel, you should avoid retrieving the data in Excel in the first place. Because if you use vba to filter, it will easily be bypassed by some users. So it could not be called security.

Related

Refreshing pivot tables on locked sheet with data from sheet locked by diff password

I'm having a number of issues designing a VBA that will allow me to refresh pivot tables in one sheet with data from another sheet.
There are five sheets that we want our field teams to access, enter data into and edit (as a procurement tracker). These five sheets are to be protected by one password (say, Minneapolis). Data from these five sheets feed into pivot tables on the next six sheets and we would like these protected by a different password that only HQ knows (say, jambalaya). All of these sheets (i.e. those with pivot tables) tab names start with LOG. Following these eleven sheets are two sheets for guidelines. We're also working in a binary format but have also tried the macro-enabled format (although not necessarily with all the different iterations of code). We preferred binary because it makes for a smaller file size and this is important for, for ex., teams in South Sudan with slow and limited internet access but need to be able to send this back and forth to HQ.
Our problem is that in order to get the pivot tables to refresh, we have to unprotect, refresh and reprotect the sheets and we're meeting problems here. I've found a number of examples of solutions for this on the internet but we're having trouble with things like autofilters not working properly, some sheets being left unlocked, or fixing these but not being able to refresh the pivot tables.
Sub UnprotectRefreshAll()
Dim ws As Worksheet
On Error Resume Next
For Each ws In ActiveWorkbook.Worksheets
ws.Unprotect Password:="jambalaya"
Next ws
ActiveWorkbook.RefreshAll
For Each ws In ActiveWorkbook.Worksheets
ws.Protect Password:="jambalaya", _
AllowUsingPivotTables:=True
ActiveSheet.Protection.AllowFiltering = False
ActiveSheet.Protect AllowFiltering:=True
Next ws
Application.Calculate
End Sub
If I run this code in the VBA on the page with data that feeds into the pivot tables (called PROCUREMENT TRACKER), the autofilters work and other pages are properly locked, and this page is locked but without a password. If I run it from a button linked to that macro on the PROCUREMENT TRACKER page, the autofilters on the page don't work (and for some reason it lands on the second to last page, one of the guidance pages). There is also still the issue of wanting to have a different password for this, and if we start it with Minneapolis, it'll revert to jambalaya anyway. I was hoping that it would be possible to have it only refresh pages with the password jambalaya (and have tried a couple different codes for that), but it still changes the page it's run on even if that password is Minneapolis. I've also seen some options where you prompt the user to enter in the password but we DONT want to give the teams access to the password jambalaya, ONLY Minneapolis.
Sub LetsTryThis()
Dim I As Integer
On Error Resume Next
For Each Worksheet In ActiveWorkbook.Worksheets
If Current.Name Like "LOG*" Then
Worksheet.Unprotect Password:="jambalaya"
End If
Next
ActiveWorkbook.RefreshAll
For Each Worksheet In ActiveWorkbook.Worksheets
If Worksheet.Name Like "LOG*" Then
Worksheet.Protect Password:="jambalaya", _
AllowUsingPivotTables:=True
ActiveSheet.Protection.AllowFiltering = False
ActiveSheet.Protect AllowFiltering:=True
End If
Next
End Sub
Because the pages with pivot tables all start with LOG, I tried doing this code above as well, hoping it would only refresh the sheets with pivot tables. If I run this macro in the VBA from the PROCUREMENT TRACKER sheet, there is an issue of leaving the PROCUREMENT TRACKER sheet locked but without a password, and then if I do it from the button on the PROCUREMENT TRACKER sheet, it lands on the last LOG sheet (i.e. the ones with pivot tables), leaving that page protected without a password and it leaves the PROCUREMENT TRACKER sheet (again, the one with the button and the data used in the pivot tables) completely unprotected.
Ideal outcome:
(1) Have one password (such as Minneapolis) for sheets 1-4 where the field team can enter data into
(2) Have a second password (such as jambalaya) for sheets 5-12 with the pivot tables (and guides) that only HQ knows. These pivot tables pull data from sheet 3 (PROCUREMENT TRACKER).
(3) Find a way to refresh the pivot tables on tabs 5-10 without leaving anything unprotected/protected without password in the process and without altering features such as the use of autofilters.
The biggest issue you are facing here is the use of ActiveSheet. Stay away from that unless absolutely needed. It's causing behavior that you feel like you can't trace - as it often does.
Do the following
Manually protect Minneapolis sheets as needed. Then never do anything with them :)
Use this code for refresh.
Code
Option Explicit
Sub RefreshData()
ProtectPivotSheets False
ThisWorkbook.RefreshAll
ProtectPivotSheets True
End Sub
Sub ProtectPivotSheets(switch As Boolean)
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
If ws.Name Like "LOG*" Then
If switch Then
ws.Protect Password:="jambalaya", AllowUsingPivotTables:=True
Else
ws.Unprotect "jambalaya"
End If
End If
Next
End Sub

Excel VBA - Sorting With Protected Row and Column

I'm an average/intermediate VBA developer, mostly able to modify existing code and re-use code for expanding Excel projects.
We ran into a situation where someone recorded some Macro's in Excel 2013 but they won't work in Excel 2003. They HAVE to work in Excel 2003 due to the legacy nature of the machine this sheet will be primary on.
So, I did some research and re-wrote the code for the Macros so it would on 2003. However, of the 4 worksheets in this workbook, it only worked on the first page. It drove me crazy trying to figure out why. Finally noticed that on the other 3 worksheets, the first column and row are PROTECTED. If I unprotect them, the re-written Macros work.
So need a little advice on how to make these re-written macros work with the first row and column protected.
This is a sample of the Macro (there are multiple ones like this with just range differences):
Sub KA24()
' Had to Convert Macro to work with the older version of Excel 2003 in the
shop - BW - 8/31/2017
Dim Ws As Worksheet
Set Ws = Worksheets("24 GA KY")
Application.ScreenUpdating = False
Ws.Range("D2:J9").Sort Key1:=Ws.Range("G2"), Order1:=xlAscending,
Header:=xlNo, OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom,
DataOption1:=xlSortNormal
Application.ScreenUpdating = True
End Sub
You can unprotect the Worksheet for use in your VBA code using the UserInterFaceOnly method. This assumes your sheet is password protected with the password "Something", if not just leave out the password part.
Dim Ws As Worksheet
Set Ws = Worksheets("24 GA KY")
Ws.Protect Password:="Something", UserInterFaceOnly:=True
...

Copy from a workbook to another workbook

I am having issues developing this code. I was able to develop the code to copy new data from my workbook to an existing path but am running into issues when trying to retrieve data from the existing path work book.
The concept is that there is a workbook in my system that will be collecting data. The data comes from different users that are working on project information. Once they have completed the project this new information along with existing information gets uploaded back to the workbook collecting that data. The work book collecting the data will always have a defined path. The workbooks that users are working off of will be in multiple places across the system.
The below macro keeps failing on the "Organizer.Sheets("Partnumber_Vendor_Database").Select". I am unsure why.
"Organizer" is the local database the user will use.
"Partnumber_Vendor_File" is the local database the information is stored.
If you can see that this code could be developed better please let me know! :)
Sub Find_Partnumber_Vendor_File()
' This sub is to open the partnumber_Vendor file to update the local database.
On Error Resume Next
Dim Organizer As Workbook
Set Organizer = Application.ActiveWorkbook
Dim Partnumber_Vendor_File As Workbook
Set Partnumber_Vendor_File = Workbooks.Open("S:\Supply Chain\PURCHASING\Forms and Templates\BOM Organizer\Partnumber_Vendor_File.xlsx")
If Err.Number = 1004 Then
MsgBox "Could not open. Check path in VBA"
Exit Sub
End If
If Partnumber_Vendor_File.ReadOnly Then
MsgBox "Sorry, partnumber to vendor database was already in use, try later"
Exit Sub
End If
On Error GoTo 0
Dim Data As Long
Data = ActiveSheet.Cells(Rows.count, 1).End(xlUp).Row
Range("A1:" & "D" & Data).Copy
Organizer.Sheets("Partnumber_Vendor_Database").Select
Range("A1:D1").Select
Selection.Insert Shift:=xlDown
Partnumber_Vendor_File.Close
End Sub
Althoug it is easy to use, avoid ActiveWorkbook, ActiveSheet and Sheets(<Title of the Sheet.).
The problem with the first two is that is hard to tell if the activeworkbook is actually the workbook you are looking for, specially when there are more than 1 workbook open. To workaround this, one solution is the use the Workbooks object to select the correct work book by its NAME (property CodeName). The Same for the Sheets, change the REAL NAMES of the Sheets so you can properlly call them.
The third is basically the same principle. In general, do not use titles of Sheets as references, use the REAL NAME of the object. Use the Property window in the VBA code to change that.
The error may come from 2 situations:
1 - Your selected workbook is not the actual workbook you want to work on.
2 - The Sheet "Partnumber_Vendor_Database" had its title changed or miswritten.
Hope it helps.

Excel VBA - Code to move or copy worksheets without warnings

I am trying to create a macro that would act the same as right clicking a workbook tab, selecting move or copy, checking the copy option, selecting another open workbook and clicking ok but without the warnings. I found the code to disable warning and I was able to record a macro that does what I want but I don't know how to make it request which open workbook to copy to.
In short how do I make the following code work where WorksheetIWantToCopy is the one the user currently has selected and OpenWorkbookIWantToCopyToo.xlsx is a workbook to be selected by the user out of a list of open workbooks.
Application.DisplayAlerts = False
Sheets("**WorksheetIWantToCopy**").Select
Sheets("**WorksheetIWantToCopy**").Copy Before:=Workbooks( _
"**OpenWorkbookIWantToCopyToo.xlsx**").Sheets(1)
I appreciate any information anyone can provide. My team greatly appreciates your support (we currently have to hit ok on 25 warnings due to conflicts we don't really care about). Thx!
If the worksheet you want to copy will always be the active sheet then you can use ActiveSheet.
As for letting the user select a workbook, it can be as simple as using the InputBox.
Public Function getWorkbookName() As String
Dim i As Integer, sListOfWbks As String, sRsp As String
' build list of workbooks
For i = 1 To Workbooks.Count
sListOfWbks = sWbkList & vbCrLf & i & " - " & Workbooks(i).Name
Next i
sRsp = InputBox("Select workbook." & vbCrLf & sListOfWbks)
If IsNumeric(sRsp) Then
getWorkbookName = Workbooks(CInt(sRsp)).Name
Else
' user pressed cancel or entered invalid text
getWorkbookName = ""
End If
End Function
This basic example will of course list all workbooks, including hidden add-ins and the workbook you are moving away from.
This needs to be said before anything else: always, always, ALWAYS make use of .Copy instead of .Move when automatically shuffling excel workbooks with VBA. Move has inherent risks because it is a modification of the other file, and if your code misbehaves then you could lose all of the data you're working with.
First of all, know which workbook is which, with no ambiguity:
Dim wkbkDestination, wkbkTemporary As Workbook
Set wkbkDestination = Workbooks("OpenWorkbookIWantToCopyTo.xlsx")
Set wkbkTemporary = Workbooks.Open("WorkbookIWantToCopy.xlsx")
Next, Copy your desired tab to your destination workbook, rename the new tab to prevent errors, and close the second workbook, without saving.
wkbkTemporary.Worksheets("WorksheetIWantToCopy").Copy Before:=wkbkDestination.Worksheets(1)
wkbkDestination.Worksheets(1).Name = "WorkbookIWantToCopy"
wkbkTemporary.Close SaveChanges = False
Naturally, depending on the exact controls you intend to use, there are lots of ways this code could be implemented. From your description it is somewhat unclear what exact problem you're trying to solve and whether this is a one-off event you're trying to accomplish for a given list of files, or whether this function is to be used on an ongoing basis.

How do I dynamically update the name of a worksheet in Excel?

I have an Excel spreadsheet which is being used to specify filesystem build information for our Unix team.
This worksheet would have different information depending on the system to be built. For example, a production system would have many filesystems, whereas a development system only one or two.
So based on user input, we would populate one or other of the two input worksheet formats ("dev","prod") but the destination worksheet will be always be the same. Only one of the dev or prod worksheets would be visible and active for users to input data at any given time.
How do we tell Excel to use a particular input worksheet to calculate another output worksheet?
My initial idea was that, for simplicity's sake, the "output" worksheet would take its data from an "input" worksheet, and that we would dynamically make either the "dev" or "prod" template become the "input" worksheet. Is this the right way to do this, or is there some better method?
There's a few ways depending on the complexity of the sheet:
If you only have a few worksheets, and users would just select "Production" or "Test", you could set up the conditional logic in the formulas. For example: =if(A1="Test",TestSheet!A1 + TestSheet!A2, ProdSheet!A1 + ProdSheet!A2)
You could utilize the same type of idea as above, but add more complexity using vlookup tables
Your VBA code could create the entire Excel spreadsheet dynamically based on the user input
if you can use VBA code as LuckyLindy suggests, you can do pretty much anything. What I used to do is record macros for various operations, for example adding or renaming sheets and take the generated code and adapt it to do exactly what was required.
An example of simple macro code:
Sub Macro1()
'
' Macro1 Macro
'
'
Sheets("Sheet1").Select
Sheets("Sheet1").Name = "My Sheet"
Sheets.Add After:=Sheets(Sheets.Count)
Sheets("Sheet5").Select
Sheets("Sheet5").Name = "My Sheet 2"
End Sub
You can record very complex operations and manipulate the generated code and place it behind specific events. So if you wanted the code to fire on worksheet load or activation of a sheet, you can insert it in the relevant block: i.e.
'in Sheet1 - code behind (alt+F11 shortcut)
Private Sub Worksheet_Activate()
Sheets("Sheet1").Select
Sheets("Sheet1").Name = "My Sheet"
Sheets.Add After:=Sheets(Sheets.Count)
Sheets("Sheet5").Select
Sheets("Sheet5").Name = "My Sheet 2"
End Sub
HTH
Another way would be to use the INDIRECT formula.
Say you have in cell A1 the name of the sheet you want to look up input from, then the following will look up the value of cell B7 from that sheet:
=INDIRECT($A$1,"!B7")