SecurityAgentPlugin not working anymore on Yosemite (SFAuthorizationPluginView) - objective-c

We have developed an Authorization Plug-in that uses an SFAuthorizationPluginView to present UI to the user. This example is based on the "old" NameAndPassword example provided by Apple.
We are using this Authorization Plug-in to unlock the session (we have updated the "system.login.screensaver" authorization in the authorizationdb) using a custom view. This worked well until the last update to Yosemite. Since Yosemite, the SFAuthorizationPluginView is not closing anymore after the user logged into the session. We could update the example to force closing the window, by doing something like:
// confirm that we have authorized the user
[self callbacks]->SetResult ([self engineRef], kAuthorizationResultAllow);
// close the window
NSView* v = [self viewForType:SFViewTypeCredentials];
NSWindow* w = [v window];
[w close];
Now the SFAuthorizationPluginView is well closed once the user is logged in (after we have authorized the user), but something remains active in the background and the user has no focus in the session (the user cannot select something or write in a text edit for example). We have to manually kill the SecurityAgent in order to get the focus again. (note that the system seems to automatically kill the SecurityAgent after 30 seconds).
We have found a fixed 2014 version of Apple's NameAndPassword auth plugin sample at the following page, but we face the same issue on Yosemite:
A fixed 2014 version of Apple's NameAndPassword auth plugin sample
This code was working fine for us until the Yosemite release.
Anyone experimenting a similar issue? Any idea or advice to fix this issue?

Instead of close to the window you should overwrite didDeactivate method AND add call:
[self didDeactivate];
in the SFAuthorizationPluginView class AFTER you set the result to ALLOW.
Apple updated their documentation, you could re-read it, there are some good guidelines that would save me A LOT of time and effort one year ago.

Related

Fetch preferences / permissions for NSUserNotificationCenter

I'm currently trying to set up a notification service using the old NSUserNotification API on macOSX (not iOS) in objective c++.
The catch is that I would need to know the system preferences associated with notifications that the user set (and preferably during the rest of runtime too) to determine if my notifications are actually seen. The application is meant to get the users attention in some way to guarantee things like reminders don't go unnoticed, so the thing I want to avoid is a notification delivered that silently doesn't produce any sound nor image on the screen whatsoever without the application knowing. If the notification is not allowed the application should handle it in some other way.
I am working on a macOS 10.14 Mojave machine. My overall code with NSUserNotificationCenter works fine with a custom Delegate that implements shouldDeliver, didActivate etc. Its just that I am currently assuming the user didn't press the "frick off" button on my notifications.
Unfortunately, NSUserNotificationCenter and related classes don't appear to feature a function that determines the permissions the way the new API (UNUserNotificationCenter) does. I have been trying to resolve this for a few hours so I was wondering if anyone else has found a solution to this. I can't really use the new API since this is strictly for back-compatibility in favour of the possibly rather prevalent amount of people who don't use mojave yet.
Things I have tried to make it work so far:
Try and see if there is a .plist somewhere where these settings are stored, in the User-specific Library folder as well as the general Library folder.
Try and find said .plist using CFPreferencesCopyKeyList and related methods. Apparently I don't really know the right domain name.
This answer from 2012 which appears to be outdated since I cannot locate the database on my machine anymore. Might be due to me using a mojave machine, or the file has since moved somewhere else.
Try and determine whether we can retro-actively check our permissions by checking a NSUserNotification's "presented" property on didDeliverNotification, shouldPresentNotification in the delegate. Unfortunately as stated in NSUserNotificationCenter itself it still behaves the same whether the preferences allow notifications or not.
Some code I tried includes the following:
CFPreferencesCopyKeyList((CFStringRef) #"com.apple.systemPreferences.plist", kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
CFPreferencesCopyKeyList((CFStringRef) #"com.apple.systemPreferences.plist", kCFPreferencesAnyUser, kCFPreferencesAnyHost);
//Unfortunately returns null with any combination of com.apple.notificationcenter.plist, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost
CFPreferencesCopyKeyList((CFStringRef) #"com.apple.notificationcenter", kCFPreferencesAnyUser, kCFPreferencesAnyHost);
On my system (only macOS High Sierra), it looks like the file you're after is:
~/Library/Preferences/com.apple.ncprefs.plist
That would correspond to a combination of kCFPreferencesCurrentUser, and kCFPreferencesAnyHost. (For the record, kCFPreferencesCurrentUser + kCFPreferencesCurrentHost equates to pref files in the ~/Library/Preferences/ByHost/ folder).
I don't think any settings would be stored in the local domain (by that I mean in the root /Library/Preferences/ folder).
I was able to get a list of apps and settings using the following code:
[[NSUserDefaults standardUserDefaults] addSuiteNamed:#"com.apple.ncprefs"];
NSArray *apps = [[NSUserDefaults standardUserDefaults] objectForKey:#"apps"];
NSLog(#"[%# %#] apps == %#", NSStringFromClass([self class]), NSStringFromSelector(_cmd), apps);
NOTE: this will not work if your app is sanboxed, as access to other pref files like that will be denied.

FirebaseStorage Completion block not called. Objective-C

I am trying to simply load in a picture from firebase storage into my IOS app, but my completion block is not being called.
I use a TableViewController initially which loads plenty of pictures just fine. When I click on an item in the tableview it presents a simple view controller modally. I use the same code that successfully loads pictures in the TableViewController. In this modally presented view controller, my FirebaseStorage completion blocks do not execute. When I exit this view controller, the TableViewController can still load load pictures as necessary as I request.
I have enabled logging with -FIRAnalyticsDebugEnabled
and still do not receive any errors in my log.
I have allowed all read and writes temporarily in my rules for FirebaseStorage.
I have seen this question iOS setValue withCompletionBlock not called which states in the comments that a specific version of firebase had some issue with FirebaseDatabase blocking some other completion blocks.
The documentation said "Fixed a race condition where performing a transaction or adding an event observer immediately after connecting to the Firebase Realtime Database service could cause completion blocks for other operations to not be executed." from https://firebase.google.com/support/release-notes/ios#3.6.0
In the above question the asker fixed the problem by upgrading to Firebase 3.7.1, I am using 5.0.1
Here are the versions I am using
Using Firebase (5.0.1)
Using FirebaseAnalytics (5.0.0)
Using FirebaseAuth (5.0.0)
Using FirebaseCore (5.0.1)
Using FirebaseDatabase (5.0.0)
Using FirebaseInstanceID (3.0.0)
Using FirebaseStorage (3.0.0)
Using GTMSessionFetcher (1.1.15)
Using GoogleToolboxForMac (2.1.4)
Using leveldb-library (1.20)
Using nanopb (0.3.8)
Generating Pods project
I have logged all along my code to find that my code is being called, but the completion block is never executed. I am using the proper children ids, so I know that the storage reference does exist. I also know it is not a connection problem because the other pictures load fine just seconds before.
- (void)loadPicture {
NSLog(#"Attempting to load picture");
FIRStorageReference *ref = [[self.storageRef child:self.detailItem.faction] child:[self.detailItem firebaseEntry]];
[ref dataWithMaxSize:1024*1024 completion:^(NSData * _Nullable data, NSError * _Nullable error) {
NSLog(#"Got a result!");
}];
I am completely lost on what steps to take next. Has anyone experienced something similar or have any advice?
I appreciate any feedback that might help! Thanks!
Import this first
#import "UIImageView+FirebaseStorage.h"
and then for load image From reference use this
FIRStorageReference *gsReference = [[self.storageRef child:self.detailItem.faction] child:[self.detailItem firebaseEntry]];
[yourImage sd_setImageWithStorageReference:gsReference placeholderImage:nil];
I have solved the problem by creating a new ViewController to do the same action from scratch. Unfortunately I don't know what the original problem was or how to reproduce it. My original ViewController was about as simple as possible. The problem seemed like a strange glitch/flaw in the API, xcode, or emulator since no errors were present and it fixed itself in the new ViewController using the same exact code.
Everything else I saw online pointed to a issue involving Firebase 3.6.0 and below. Make sure you are using a recent Firebase version and then recreate the same ViewController and your problem will likely be fixed.

Controlling level and focus of windows other apps with CGPrivate functions

Question
How to use these private functions on other windows? It would be nice to have this knowledge back in the wild. I am specifically trying to get CGSOrderWindow and CGSSetWindowLevel to work.
I was trying in the direction of:
temporarily register as the dock and then register the dock as the dock again immediately afterwards
or
code injection into the Dock process per this comment:
Also, the author of the above project seems determined to make all core functionality available as a framework. It seems to be implemented as code injection into the Dock process.
Reason I know this is possible
I have been doing work on trying to setLevel on window of another app, and focus window of another app if focused. I am posting this again with the info I learned because from my searching online, I know this was done in the past, its just the knowledge is not publicly out there anymore. The sourceforge pages are no longer there. So I was wondering if you could help me make this information public again.
This is the topic I read that gave me this information - http://cocoadev.com/HowtoControlOtherAppsWindows
Here you see comments like:
You cannot control an another app's windows from a user-level process, unfortunately.
SlavaKarpenko
You can, Slava, you just need to register as the Dock. It might be possible to temporarily register as the dock and then register the dock as the dock again immediately afterwards, not sure. I think the call you'd be wanting to investigate as CoreDockRegisterDockOwner in HIServices.framework.
FinlayDobbie
You could also use APE or similar to do control the windows, or (as mentioned above) register as the Dock (look for the private APIs with Universal Connection in their name). Has anyone found a polite way of getting the Dock to give up its universal connection? The only way I can find is to force quit the Dock and grab the universal connection when it's not looking (which prevents the dock reloading).
SamTaylor
There's an open source project up on sourceforge.net that looks much more like the window managers I've used on Unix boxes than Space.app (or Space.dock): http://wsmanager.sourceforge.net/
SteveCook
Verifying things work
This is what I learned, from the sources at bottom of this post, we see all these functions work with CGWindowIds, so how do I get that, this is how:
Get all windows with CGWindowListCopyWindowInfo. Then access each element from that array with CFArrayGetValueAtIndex and then get the CGWindowId with objectForKey:, kCGWindowNumber, and then integerValue.
Now if I try to focus or set level of a window that is OWNED by the app running the code, it works fantastic. For instance:
MY_TARGET_CGWINDOW_ID = 179;
rez_CGError = CGSOrderWindow(_CGSDefaultConnection, MY_TARGET_CGWINDOW_ID, kCGSOrderAbove, 0);
Will focus it, rez_CGError is 0. Even if the window is minimized, it is unminimized, without animation, and shown.
Now however, if I try this on a window of a different app we get some errors:
MY_TARGET_CGWINDOW_ID_of_other_app = 40;
rez_CGError = CGSOrderWindow(_CGSDefaultConnection, MY_TARGET_CGWINDOW_ID_of_other_app, kCGSOrderAbove, 0);
This fails and rez_CGError is 1000, which I suspect means "cid (CGSConnection) used does not have permission to modify target window". The same happens if I first do [app activateWithOptions: (NSApplicationActivateIgnoringOtherApps | NSApplicationActivateAllWindows)] before making the call above.
So I first get the cid of that owning window like this:
var rez_CGError = CGSGetWindowOwner(_CGSDefaultConnection, MY_TARGET_CGWINDOW_ID_of_other_app, &ownerCid);
This works good and I get ownerCid is set to a value. Then I do the focus command with this new connection:
rez_CGError = CGSOrderWindow(ownerCid, MY_TARGET_CGWINDOW_ID_of_other_app, kCGSOrderAbove, 0);
However this gives rez_CGError of 268435459, which I suspect means "current app does not have permission to use this ConnectionId (cid)". (Same happens if I call activateWithOptions first.
My Sources for the Private Functions
Here is the sources for some private functions I found - https://code.google.com/p/undocumented-goodness/source/browse/trunk/CoreGraphics/CGSPrivate.h
This one source here contains a function that is not in the above link - CGSGetConnectionIDForPSN - i test it and it exists - from - https://github.com/mnutt/libqxt/blob/767498816dfa1742a6f3aee787281745afec11b8/src/gui/qxtwindowsystem_mac.h#L80

Values not always persisted in App group between companion app & app extension

From time to time, but not always (I have had this working for a bit), the app/extension gets in a state where I can't read a flag set in my App Group between my companion app and my app extension. Don't know how it gets in this state or why the values differ, but it's critical to my application these always be in sync.
Companion app viewDidLoad:
NSUserDefaults *myAppSettings = [[NSUserDefaults alloc] initWithSuiteName:#"group.myapp"];
.....
[myAppSettings setBool:true forKey:#"myBool"];
[myAppSettings synchronize];
NSLog([myAppSettings boolForKey:#"myBool"] ? #"Companion app - bool TRUE" : #"Companion app - bool FALSE");
App extension viewDidLoad
NSUserDefaults *myAppSettings = [[NSUserDefaults alloc] initWithSuiteName:#"group.myapp"];
[myAppSettings synchronize];
NSLog([myAppSettings boolForKey:#"myBool"] ? #"App extension app - bool TRUE" : #"App extension - bool FALSE");
Console output
Companion app - bool TRUE
App extension - bool FALSE
I also synchronize before my companion app will enter background. I have my app group set up in the portal etc.
What am I doing wrong?
EDIT
Apparently others having this problem too:
https://devforums.apple.com/message/977151#977151
"I think that this is currently very glitchy.
Sometimes the data sharing works, then a change and all of a sudden the widget can't see the shared data anymore (both on Simulator and device).
Annoying and hope it's a bit more reliable in next beta!"
EDIT 2
Looks like another person has reported this exact issue as well:
"I also noticed the same thing too.This not only happen to the
NSUserDefaults, but also all the files in the container folder. The
keyboard extension suddenly will lose read/write pemission to the
container folder after using the keyboard for a while."
EDIT 3
More evidence: https://devforums.apple.com/message/1028078#1028078
After I upgrade to beta 3, I noticed that sometimes the keyboard
failed to open the database because it failed to access to the DB
file. The keyboard has been able to access to the file before.
EDIT 4
Seems like this could be because the keyboard loses the RequestsOpenAccess flag. But I can't reproduce it, and there's no way for me to tell for sure.
EDIT 5
Seems like others are reporting this in the iOS8 GM build:
This issue still persists for me in the GM. It seems related to a
keyboard crash.. but also there seems to be some contention between
keyboard and containing app in terms of who creates the suite in what
order. I think this problem is on Apple's end. Trust me, I WANT it to
be my fault but I've spent countless hours with trial and error. No
matter what I do in code and verify with NSLog, it will end up in this
state eventually. Hoping someone finds a magic pill. :S
Has anyone solved this yet?
You must request open access in order to access shared NSUserDefaults. It's stated directly in the App Extension Programming guide:
By default, a keyboard has no network access and cannot share a container with its containing app. To enable these things, set the value of the RequestsOpenAccess Boolean key in the Info.plist file to YES.
Be sure you change the RequestsOpenAccess field to YES. You'll find it in keyboard's Info.plist > NSExtension > NSExtensionAttributes > RequestOpenAccess. Then remove the keyboard in Settings, delete the app, run it again, and add the keyboard again. After you add it, tap on the keyboard name and then flip the switch to enable Allow Full Access. You'll need to instruct the users to follow those same steps to grant access (and reassure them you're not evil), otherwise it simply will not work and you'll never get the data that's stored in your shared container. Note that in iOS 8.3+, if the user hasn't enabled full access the keyboard will be able to access the shared container, but writing to it will not save the data, for security and privacy purposes. In 8.2- you can't access that data without open access granted.
I can confirm that the problem is related to RequestsOpenAccess flag.
Assuming that everything done right (NSUserDefaults use initWithSuiteName, all Capabilities for main application and custom keyboard were set, etc.) I have the next steps:
1) Install the main application and a custom keyboard on device
2) Set 'Allow full access' for the custom keyboard to YES
3) Add some items (in my case this is a simple text templates) in the main app
4) Go to keyboard and check that all items, that were added from the main app,
appeared in custom keyboard
5) Go to main app and add a few more items
6) Go to keyboard and now you will see that nothing changed
7) Go to settings and switch 'Allow full access' to NO and then to YES
8) Go to custom keyboard again and check that item which were added in step 5 appeared.

libPusher + updating an open NSMenu

I have an NSMenu that I want to update with items pushed to my app through pusherapp and received using the libPusher client library. But events seem not to be received in NSEventTrackingRunLoopMode.
Given the following snippet:
[channel bindToEventNamed:#"my_event" handleWithBlock:^(PTPusherEvent *event) {
NSLog(#"event received");
}];
And I wait for a push to occur while I maintain the menu open, I expect to receive the event immediately but I only receive it when I close the menu.
I also tried passing the main queue to bindToEventNamed:handleWithBlock:queue: (using dispatch_get_main_queue();), to no avail.
So I'm left wondering whether I'm doing something wrong or there's a bug in libPusher?
I'm the author of libPusher. The reason you are seeing this problem is because the underlying WebSocket library used by libPusher, SocketRocket only works in the default run loop mode.
The good news is that this has been fixed in the latest HEAD of SocketRocket. I have tested libPusher agains the latest SocketRocket and can confirm that it fixes this issue and I intend to roll these changes in to the next release.
Now, I've just checked the outstanding Github issue and realised that you were the original reporter of this bug, so you probably know all this already but I'm going to post this answer anyway for posterity.
libPusher issue
SocketRocket issue